Skip to main content

gix_object/object/
mod.rs

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