1use std::ops::Deref;
22
23use radicle_git_ext::Oid;
24
25#[cfg(feature = "serde")]
26use serde::{
27 ser::{SerializeStruct as _, Serializer},
28 Serialize,
29};
30
31use crate::Commit;
32
33pub struct Blob<T> {
38 id: Oid,
39 is_binary: bool,
40 commit: Commit,
41 content: T,
42}
43
44impl<T> Blob<T> {
45 pub fn object_id(&self) -> Oid {
46 self.id
47 }
48
49 pub fn is_binary(&self) -> bool {
50 self.is_binary
51 }
52
53 pub fn commit(&self) -> &Commit {
55 &self.commit
56 }
57
58 pub fn content(&self) -> &[u8]
59 where
60 T: AsRef<[u8]>,
61 {
62 self.content.as_ref()
63 }
64
65 pub fn size(&self) -> usize
66 where
67 T: AsRef<[u8]>,
68 {
69 self.content.as_ref().len()
70 }
71}
72
73impl<'a> Blob<BlobRef<'a>> {
74 pub(crate) fn new(id: Oid, git2_blob: git2::Blob<'a>, commit: Commit) -> Self {
76 let is_binary = git2_blob.is_binary();
77 let content = BlobRef { inner: git2_blob };
78 Self {
79 id,
80 is_binary,
81 content,
82 commit,
83 }
84 }
85
86 pub fn to_owned(&self) -> Blob<Vec<u8>> {
88 Blob {
89 id: self.id,
90 content: self.content.to_vec(),
91 commit: self.commit.clone(),
92 is_binary: self.is_binary,
93 }
94 }
95}
96
97pub struct BlobRef<'a> {
99 pub(crate) inner: git2::Blob<'a>,
100}
101
102impl BlobRef<'_> {
103 pub fn id(&self) -> Oid {
104 self.inner.id().into()
105 }
106}
107
108impl AsRef<[u8]> for BlobRef<'_> {
109 fn as_ref(&self) -> &[u8] {
110 self.inner.content()
111 }
112}
113
114impl Deref for BlobRef<'_> {
115 type Target = [u8];
116
117 fn deref(&self) -> &Self::Target {
118 self.inner.content()
119 }
120}
121
122#[cfg(feature = "serde")]
123impl<T> Serialize for Blob<T>
124where
125 T: AsRef<[u8]>,
126{
127 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
128 where
129 S: Serializer,
130 {
131 use base64::Engine as _;
132
133 const FIELDS: usize = 4;
134 let mut state = serializer.serialize_struct("Blob", FIELDS)?;
135 state.serialize_field("id", &self.id)?;
136 state.serialize_field("binary", &self.is_binary())?;
137
138 let bytes = self.content.as_ref();
139 match std::str::from_utf8(bytes) {
140 Ok(s) => state.serialize_field("content", s)?,
141 Err(_) => {
142 let encoded = base64::prelude::BASE64_STANDARD.encode(bytes);
143 state.serialize_field("content", &encoded)?
144 }
145 };
146 state.serialize_field("lastCommit", &self.commit)?;
147 state.end()
148 }
149}
150
151#[cfg(feature = "serde")]
152impl Serialize for BlobRef<'_> {
153 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
154 where
155 S: Serializer,
156 {
157 use base64::Engine as _;
158
159 const FIELDS: usize = 3;
160 let mut state = serializer.serialize_struct("BlobRef", FIELDS)?;
161 state.serialize_field("id", &self.id())?;
162 state.serialize_field("binary", &self.inner.is_binary())?;
163
164 let bytes = self.as_ref();
165 match std::str::from_utf8(bytes) {
166 Ok(s) => state.serialize_field("content", s)?,
167 Err(_) => {
168 let encoded = base64::prelude::BASE64_STANDARD.encode(bytes);
169 state.serialize_field("content", &encoded)?
170 }
171 };
172 state.end()
173 }
174}