use std::ops::Deref;
use base64::Engine;
use radicle_git_ext::Oid;
#[cfg(feature = "serde")]
use serde::{
ser::{SerializeStruct as _, Serializer},
Serialize,
};
use crate::Commit;
pub struct Blob<T> {
id: Oid,
is_binary: bool,
commit: Commit,
content: T,
}
impl<T> Blob<T> {
pub fn object_id(&self) -> Oid {
self.id
}
pub fn is_binary(&self) -> bool {
self.is_binary
}
pub fn commit(&self) -> &Commit {
&self.commit
}
pub fn content(&self) -> &[u8]
where
T: AsRef<[u8]>,
{
self.content.as_ref()
}
pub fn size(&self) -> usize
where
T: AsRef<[u8]>,
{
self.content.as_ref().len()
}
}
impl<'a> Blob<BlobRef<'a>> {
pub(crate) fn new(id: Oid, git2_blob: git2::Blob<'a>, commit: Commit) -> Self {
let is_binary = git2_blob.is_binary();
let content = BlobRef { inner: git2_blob };
Self {
id,
is_binary,
content,
commit,
}
}
pub fn to_owned(&self) -> Blob<Vec<u8>> {
Blob {
id: self.id,
content: self.content.to_vec(),
commit: self.commit.clone(),
is_binary: self.is_binary,
}
}
}
pub struct BlobRef<'a> {
pub(crate) inner: git2::Blob<'a>,
}
impl<'a> BlobRef<'a> {
pub fn id(&self) -> Oid {
self.inner.id().into()
}
}
impl AsRef<[u8]> for BlobRef<'_> {
fn as_ref(&self) -> &[u8] {
self.inner.content()
}
}
impl Deref for BlobRef<'_> {
type Target = [u8];
fn deref(&self) -> &Self::Target {
self.inner.content()
}
}
#[cfg(feature = "serde")]
impl<T> Serialize for Blob<T>
where
T: AsRef<[u8]>,
{
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
const FIELDS: usize = 4;
let mut state = serializer.serialize_struct("Blob", FIELDS)?;
state.serialize_field("id", &self.id)?;
state.serialize_field("binary", &self.is_binary())?;
let bytes = self.content.as_ref();
match std::str::from_utf8(bytes) {
Ok(s) => state.serialize_field("content", s)?,
Err(_) => {
let encoded = base64::prelude::BASE64_STANDARD.encode(bytes);
state.serialize_field("content", &encoded)?
}
};
state.serialize_field("lastCommit", &self.commit)?;
state.end()
}
}
#[cfg(feature = "serde")]
impl<'a> Serialize for BlobRef<'a> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
const FIELDS: usize = 3;
let mut state = serializer.serialize_struct("BlobRef", FIELDS)?;
state.serialize_field("id", &self.id())?;
state.serialize_field("binary", &self.inner.is_binary())?;
let bytes = self.as_ref();
match std::str::from_utf8(bytes) {
Ok(s) => state.serialize_field("content", s)?,
Err(_) => {
let encoded = base64::prelude::BASE64_STANDARD.encode(bytes);
state.serialize_field("content", &encoded)?
}
};
state.end()
}
}