Skip to main content

gix_object/object/
mod.rs

1use crate::{Blob, Commit, Object, Tag, Tree};
2use gix_error::Result;
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::{OptionExt, ResultExt, message, 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) -> Result<ObjectRef<'a>> {
195        let (kind, size, offset) = loose_header(data)?;
196
197        let size = usize::try_from(size).or_raise(|| {
198            message!("Object sized {size} does not fit into memory - this can happen on 32 bit systems").validation()
199        })?;
200        let body = &data[offset..]
201            .get(..size)
202            .ok_or_raise(|| validation("object data was shorter than its size declared in the header"))?;
203
204        Self::from_bytes(body, kind, hash_kind)
205    }
206
207    /// Deserialize an object of `kind` from the given `data`, using `object_hash`.
208    pub fn from_bytes(data: &'a [u8], kind: Kind, hash_kind: gix_hash::Kind) -> Result<ObjectRef<'a>> {
209        Ok(match kind {
210            Kind::Tree => ObjectRef::Tree(TreeRef::from_bytes(data, hash_kind)?),
211            Kind::Blob => ObjectRef::Blob(BlobRef { data }),
212            Kind::Commit => ObjectRef::Commit(CommitRef::from_bytes(data, hash_kind)?),
213            Kind::Tag => ObjectRef::Tag(TagRef::from_bytes(data, hash_kind)?),
214        })
215    }
216
217    /// Convert the immutable object into a mutable version, consuming the source in the process.
218    ///
219    /// Note that this is an expensive operation.
220    pub fn into_owned(self) -> Result<Object> {
221        self.try_into()
222    }
223
224    /// Convert this immutable object into its mutable counterpart.
225    ///
226    /// Note that this is an expensive operation.
227    pub fn to_owned(&self) -> Result<Object> {
228        self.clone().try_into()
229    }
230}
231
232/// Convenient access to contained objects.
233impl<'a> ObjectRef<'a> {
234    /// Interpret this object as blob.
235    pub fn as_blob(&self) -> Option<&BlobRef<'a>> {
236        match self {
237            ObjectRef::Blob(v) => Some(v),
238            _ => None,
239        }
240    }
241    /// Interpret this object as blob, chainable.
242    pub fn into_blob(self) -> Option<BlobRef<'a>> {
243        match self {
244            ObjectRef::Blob(v) => Some(v),
245            _ => None,
246        }
247    }
248    /// Interpret this object as commit.
249    pub fn as_commit(&self) -> Option<&CommitRef<'a>> {
250        match self {
251            ObjectRef::Commit(v) => Some(v),
252            _ => None,
253        }
254    }
255    /// Interpret this object as commit, chainable.
256    pub fn into_commit(self) -> Option<CommitRef<'a>> {
257        match self {
258            ObjectRef::Commit(v) => Some(v),
259            _ => None,
260        }
261    }
262    /// Interpret this object as tree.
263    pub fn as_tree(&self) -> Option<&TreeRef<'a>> {
264        match self {
265            ObjectRef::Tree(v) => Some(v),
266            _ => None,
267        }
268    }
269    /// Interpret this object as tree, chainable
270    pub fn into_tree(self) -> Option<TreeRef<'a>> {
271        match self {
272            ObjectRef::Tree(v) => Some(v),
273            _ => None,
274        }
275    }
276    /// Interpret this object as tag.
277    pub fn as_tag(&self) -> Option<&TagRef<'a>> {
278        match self {
279            ObjectRef::Tag(v) => Some(v),
280            _ => None,
281        }
282    }
283    /// Interpret this object as tag, chainable.
284    pub fn into_tag(self) -> Option<TagRef<'a>> {
285        match self {
286            ObjectRef::Tag(v) => Some(v),
287            _ => None,
288        }
289    }
290    /// Return the kind of object.
291    pub fn kind(&self) -> Kind {
292        match self {
293            ObjectRef::Tree(_) => Kind::Tree,
294            ObjectRef::Blob(_) => Kind::Blob,
295            ObjectRef::Commit(_) => Kind::Commit,
296            ObjectRef::Tag(_) => Kind::Tag,
297        }
298    }
299}