Skip to main content

gix_object/object/
mod.rs

1use crate::{Blob, Commit, Object, Tag, Tree};
2
3mod convert;
4
5mod write {
6    use std::io;
7
8    use crate::{Kind, Object, ObjectRef, WriteTo};
9
10    /// Serialization
11    impl WriteTo for ObjectRef<'_> {
12        /// Write the contained object to `out` in the git serialization format.
13        fn write_to(&self, out: &mut dyn io::Write) -> io::Result<()> {
14            use crate::ObjectRef::*;
15            match self {
16                Tree(v) => v.write_to(out),
17                Blob(v) => v.write_to(out),
18                Commit(v) => v.write_to(out),
19                Tag(v) => v.write_to(out),
20            }
21        }
22
23        fn kind(&self) -> Kind {
24            self.kind()
25        }
26
27        fn size(&self) -> u64 {
28            use crate::ObjectRef::*;
29            match self {
30                Tree(v) => v.size(),
31                Blob(v) => v.size(),
32                Commit(v) => v.size(),
33                Tag(v) => v.size(),
34            }
35        }
36    }
37
38    /// Serialization
39    impl WriteTo for Object {
40        /// Write the contained object to `out` in the git serialization format.
41        fn write_to(&self, out: &mut dyn io::Write) -> io::Result<()> {
42            use crate::Object::*;
43            match self {
44                Tree(v) => v.write_to(out),
45                Blob(v) => v.write_to(out),
46                Commit(v) => v.write_to(out),
47                Tag(v) => v.write_to(out),
48            }
49        }
50
51        fn kind(&self) -> Kind {
52            self.kind()
53        }
54
55        fn size(&self) -> u64 {
56            use crate::Object::*;
57            match self {
58                Tree(v) => v.size(),
59                Blob(v) => v.size(),
60                Commit(v) => v.size(),
61                Tag(v) => v.size(),
62            }
63        }
64    }
65}
66
67/// Convenient extraction of typed object.
68impl Object {
69    /// Turns this instance into a [`Blob`], panic otherwise.
70    pub fn into_blob(self) -> Blob {
71        match self {
72            Object::Blob(v) => v,
73            _ => panic!("BUG: not a blob"),
74        }
75    }
76    /// Turns this instance into a [`Commit`] panic otherwise.
77    pub fn into_commit(self) -> Commit {
78        match self {
79            Object::Commit(v) => v,
80            _ => panic!("BUG: not a commit"),
81        }
82    }
83    /// Turns this instance into a [`Tree`] panic otherwise.
84    pub fn into_tree(self) -> Tree {
85        match self {
86            Object::Tree(v) => v,
87            _ => panic!("BUG: not a tree"),
88        }
89    }
90    /// Turns this instance into a [`Tag`] panic otherwise.
91    pub fn into_tag(self) -> Tag {
92        match self {
93            Object::Tag(v) => v,
94            _ => panic!("BUG: not a tag"),
95        }
96    }
97    /// Turns this instance into a [`Blob`] if it is one.
98    #[expect(
99        clippy::result_large_err,
100        reason = "will be removed once `gix-error` is used consistently"
101    )]
102    pub fn try_into_blob(self) -> Result<Blob, Self> {
103        match self {
104            Object::Blob(v) => Ok(v),
105            _ => Err(self),
106        }
107    }
108    /// Turns this instance into a [`BlobRef`] if it is a blob.
109    pub fn try_into_blob_ref(&self) -> Option<BlobRef<'_>> {
110        match self {
111            Object::Blob(v) => Some(v.to_ref()),
112            _ => None,
113        }
114    }
115    /// Turns this instance into a [`Commit`] if it is one.
116    #[expect(
117        clippy::result_large_err,
118        reason = "will be removed once `gix-error` is used consistently"
119    )]
120    pub fn try_into_commit(self) -> Result<Commit, Self> {
121        match self {
122            Object::Commit(v) => Ok(v),
123            _ => Err(self),
124        }
125    }
126    /// Turns this instance into a [`Tree`] if it is one.
127    #[expect(
128        clippy::result_large_err,
129        reason = "will be removed once `gix-error` is used consistently"
130    )]
131    pub fn try_into_tree(self) -> Result<Tree, Self> {
132        match self {
133            Object::Tree(v) => Ok(v),
134            _ => Err(self),
135        }
136    }
137    /// Turns this instance into a [`Tag`] if it is one.
138    #[expect(
139        clippy::result_large_err,
140        reason = "will be removed once `gix-error` is used consistently"
141    )]
142    pub fn try_into_tag(self) -> Result<Tag, Self> {
143        match self {
144            Object::Tag(v) => Ok(v),
145            _ => Err(self),
146        }
147    }
148
149    /// Returns a [`Blob`] if it is one.
150    pub fn as_blob(&self) -> Option<&Blob> {
151        match self {
152            Object::Blob(v) => Some(v),
153            _ => None,
154        }
155    }
156    /// Returns a [`Commit`] if it is one.
157    pub fn as_commit(&self) -> Option<&Commit> {
158        match self {
159            Object::Commit(v) => Some(v),
160            _ => None,
161        }
162    }
163    /// Returns a [`Tree`] if it is one.
164    pub fn as_tree(&self) -> Option<&Tree> {
165        match self {
166            Object::Tree(v) => Some(v),
167            _ => None,
168        }
169    }
170    /// Returns a [`Tag`] if it is one.
171    pub fn as_tag(&self) -> Option<&Tag> {
172        match self {
173            Object::Tag(v) => Some(v),
174            _ => None,
175        }
176    }
177    /// Returns the kind of object stored in this instance.
178    pub fn kind(&self) -> crate::Kind {
179        match self {
180            Object::Tree(_) => crate::Kind::Tree,
181            Object::Blob(_) => crate::Kind::Blob,
182            Object::Commit(_) => crate::Kind::Commit,
183            Object::Tag(_) => crate::Kind::Tag,
184        }
185    }
186}
187
188use crate::{
189    BlobRef, CommitRef, Kind, ObjectRef, TagRef, TreeRef,
190    decode::{Error as DecodeError, LooseHeaderDecodeError, loose_header},
191};
192
193#[derive(Debug, thiserror::Error)]
194pub enum LooseDecodeError {
195    #[error(transparent)]
196    InvalidHeader(#[from] LooseHeaderDecodeError),
197    #[error(transparent)]
198    InvalidContent(#[from] DecodeError),
199    #[error("Object sized {size} does not fit into memory - this can happen on 32 bit systems")]
200    OutOfMemory { size: u64 },
201}
202
203impl<'a> ObjectRef<'a> {
204    /// Deserialize an object from a loose serialisation given `data`, parsing with the provided `object_hash`.
205    pub fn from_loose(data: &'a [u8], hash_kind: gix_hash::Kind) -> Result<ObjectRef<'a>, LooseDecodeError> {
206        let (kind, size, offset) = loose_header(data)?;
207
208        let body = &data[offset..]
209            .get(..size.try_into().map_err(|_| LooseDecodeError::OutOfMemory { size })?)
210            .ok_or(LooseHeaderDecodeError::InvalidHeader {
211                message: "object data was shorter than its size declared in the header",
212            })?;
213
214        Ok(Self::from_bytes(body, kind, hash_kind)?)
215    }
216
217    /// Deserialize an object of `kind` from the given `data`, using `object_hash`.
218    pub fn from_bytes(
219        data: &'a [u8],
220        kind: Kind,
221        hash_kind: gix_hash::Kind,
222    ) -> Result<ObjectRef<'a>, crate::decode::Error> {
223        Ok(match kind {
224            Kind::Tree => ObjectRef::Tree(TreeRef::from_bytes(data, hash_kind)?),
225            Kind::Blob => ObjectRef::Blob(BlobRef { data }),
226            Kind::Commit => ObjectRef::Commit(CommitRef::from_bytes(data, hash_kind)?),
227            Kind::Tag => ObjectRef::Tag(TagRef::from_bytes(data, hash_kind)?),
228        })
229    }
230
231    /// Convert the immutable object into a mutable version, consuming the source in the process.
232    ///
233    /// Note that this is an expensive operation.
234    pub fn into_owned(self) -> Result<Object, crate::decode::Error> {
235        self.try_into()
236    }
237
238    /// Convert this immutable object into its mutable counterpart.
239    ///
240    /// Note that this is an expensive operation.
241    pub fn to_owned(&self) -> Result<Object, crate::decode::Error> {
242        self.clone().try_into()
243    }
244}
245
246/// Convenient access to contained objects.
247impl<'a> ObjectRef<'a> {
248    /// Interpret this object as blob.
249    pub fn as_blob(&self) -> Option<&BlobRef<'a>> {
250        match self {
251            ObjectRef::Blob(v) => Some(v),
252            _ => None,
253        }
254    }
255    /// Interpret this object as blob, chainable.
256    pub fn into_blob(self) -> Option<BlobRef<'a>> {
257        match self {
258            ObjectRef::Blob(v) => Some(v),
259            _ => None,
260        }
261    }
262    /// Interpret this object as commit.
263    pub fn as_commit(&self) -> Option<&CommitRef<'a>> {
264        match self {
265            ObjectRef::Commit(v) => Some(v),
266            _ => None,
267        }
268    }
269    /// Interpret this object as commit, chainable.
270    pub fn into_commit(self) -> Option<CommitRef<'a>> {
271        match self {
272            ObjectRef::Commit(v) => Some(v),
273            _ => None,
274        }
275    }
276    /// Interpret this object as tree.
277    pub fn as_tree(&self) -> Option<&TreeRef<'a>> {
278        match self {
279            ObjectRef::Tree(v) => Some(v),
280            _ => None,
281        }
282    }
283    /// Interpret this object as tree, chainable
284    pub fn into_tree(self) -> Option<TreeRef<'a>> {
285        match self {
286            ObjectRef::Tree(v) => Some(v),
287            _ => None,
288        }
289    }
290    /// Interpret this object as tag.
291    pub fn as_tag(&self) -> Option<&TagRef<'a>> {
292        match self {
293            ObjectRef::Tag(v) => Some(v),
294            _ => None,
295        }
296    }
297    /// Interpret this object as tag, chainable.
298    pub fn into_tag(self) -> Option<TagRef<'a>> {
299        match self {
300            ObjectRef::Tag(v) => Some(v),
301            _ => None,
302        }
303    }
304    /// Return the kind of object.
305    pub fn kind(&self) -> Kind {
306        match self {
307            ObjectRef::Tree(_) => Kind::Tree,
308            ObjectRef::Blob(_) => Kind::Blob,
309            ObjectRef::Commit(_) => Kind::Commit,
310            ObjectRef::Tag(_) => Kind::Tag,
311        }
312    }
313}