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    #[allow(clippy::result_large_err)]
99    pub fn try_into_blob(self) -> Result<Blob, Self> {
100        match self {
101            Object::Blob(v) => Ok(v),
102            _ => Err(self),
103        }
104    }
105    /// Turns this instance into a [`BlobRef`] if it is a blob.
106    pub fn try_into_blob_ref(&self) -> Option<BlobRef<'_>> {
107        match self {
108            Object::Blob(v) => Some(v.to_ref()),
109            _ => None,
110        }
111    }
112    /// Turns this instance into a [`Commit`] if it is one.
113    #[allow(clippy::result_large_err)]
114    pub fn try_into_commit(self) -> Result<Commit, Self> {
115        match self {
116            Object::Commit(v) => Ok(v),
117            _ => Err(self),
118        }
119    }
120    /// Turns this instance into a [`Tree`] if it is one.
121    #[allow(clippy::result_large_err)]
122    pub fn try_into_tree(self) -> Result<Tree, Self> {
123        match self {
124            Object::Tree(v) => Ok(v),
125            _ => Err(self),
126        }
127    }
128    /// Turns this instance into a [`Tag`] if it is one.
129    #[allow(clippy::result_large_err)]
130    pub fn try_into_tag(self) -> Result<Tag, Self> {
131        match self {
132            Object::Tag(v) => Ok(v),
133            _ => Err(self),
134        }
135    }
136
137    /// Returns a [`Blob`] if it is one.
138    pub fn as_blob(&self) -> Option<&Blob> {
139        match self {
140            Object::Blob(v) => Some(v),
141            _ => None,
142        }
143    }
144    /// Returns a [`Commit`] if it is one.
145    pub fn as_commit(&self) -> Option<&Commit> {
146        match self {
147            Object::Commit(v) => Some(v),
148            _ => None,
149        }
150    }
151    /// Returns a [`Tree`] if it is one.
152    pub fn as_tree(&self) -> Option<&Tree> {
153        match self {
154            Object::Tree(v) => Some(v),
155            _ => None,
156        }
157    }
158    /// Returns a [`Tag`] if it is one.
159    pub fn as_tag(&self) -> Option<&Tag> {
160        match self {
161            Object::Tag(v) => Some(v),
162            _ => None,
163        }
164    }
165    /// Returns the kind of object stored in this instance.
166    pub fn kind(&self) -> crate::Kind {
167        match self {
168            Object::Tree(_) => crate::Kind::Tree,
169            Object::Blob(_) => crate::Kind::Blob,
170            Object::Commit(_) => crate::Kind::Commit,
171            Object::Tag(_) => crate::Kind::Tag,
172        }
173    }
174}
175
176use crate::{
177    decode::{loose_header, Error as DecodeError, LooseHeaderDecodeError},
178    BlobRef, CommitRef, Kind, ObjectRef, TagRef, TreeRef,
179};
180
181#[derive(Debug, thiserror::Error)]
182#[allow(missing_docs)]
183pub enum LooseDecodeError {
184    #[error(transparent)]
185    InvalidHeader(#[from] LooseHeaderDecodeError),
186    #[error(transparent)]
187    InvalidContent(#[from] DecodeError),
188    #[error("Object sized {size} does not fit into memory - this can happen on 32 bit systems")]
189    OutOfMemory { size: u64 },
190}
191
192impl<'a> ObjectRef<'a> {
193    /// Deserialize an object from a loose serialisation
194    pub fn from_loose(data: &'a [u8]) -> Result<ObjectRef<'a>, LooseDecodeError> {
195        let (kind, size, offset) = loose_header(data)?;
196
197        let body = &data[offset..]
198            .get(..size.try_into().map_err(|_| LooseDecodeError::OutOfMemory { size })?)
199            .ok_or(LooseHeaderDecodeError::InvalidHeader {
200                message: "object data was shorter than its size declared in the header",
201            })?;
202
203        Ok(Self::from_bytes(kind, body)?)
204    }
205
206    /// Deserialize an object of `kind` from the given `data`.
207    pub fn from_bytes(kind: Kind, data: &'a [u8]) -> Result<ObjectRef<'a>, crate::decode::Error> {
208        Ok(match kind {
209            Kind::Tree => ObjectRef::Tree(TreeRef::from_bytes(data)?),
210            Kind::Blob => ObjectRef::Blob(BlobRef { data }),
211            Kind::Commit => ObjectRef::Commit(CommitRef::from_bytes(data)?),
212            Kind::Tag => ObjectRef::Tag(TagRef::from_bytes(data)?),
213        })
214    }
215
216    /// Convert the immutable object into a mutable version, consuming the source in the process.
217    ///
218    /// Note that this is an expensive operation.
219    pub fn into_owned(self) -> Object {
220        self.into()
221    }
222
223    /// Convert this immutable object into its mutable counterpart.
224    ///
225    /// Note that this is an expensive operation.
226    pub fn to_owned(&self) -> Object {
227        self.clone().into()
228    }
229}
230
231/// Convenient access to contained objects.
232impl<'a> ObjectRef<'a> {
233    /// Interpret this object as blob.
234    pub fn as_blob(&self) -> Option<&BlobRef<'a>> {
235        match self {
236            ObjectRef::Blob(v) => Some(v),
237            _ => None,
238        }
239    }
240    /// Interpret this object as blob, chainable.
241    pub fn into_blob(self) -> Option<BlobRef<'a>> {
242        match self {
243            ObjectRef::Blob(v) => Some(v),
244            _ => None,
245        }
246    }
247    /// Interpret this object as commit.
248    pub fn as_commit(&self) -> Option<&CommitRef<'a>> {
249        match self {
250            ObjectRef::Commit(v) => Some(v),
251            _ => None,
252        }
253    }
254    /// Interpret this object as commit, chainable.
255    pub fn into_commit(self) -> Option<CommitRef<'a>> {
256        match self {
257            ObjectRef::Commit(v) => Some(v),
258            _ => None,
259        }
260    }
261    /// Interpret this object as tree.
262    pub fn as_tree(&self) -> Option<&TreeRef<'a>> {
263        match self {
264            ObjectRef::Tree(v) => Some(v),
265            _ => None,
266        }
267    }
268    /// Interpret this object as tree, chainable
269    pub fn into_tree(self) -> Option<TreeRef<'a>> {
270        match self {
271            ObjectRef::Tree(v) => Some(v),
272            _ => None,
273        }
274    }
275    /// Interpret this object as tag.
276    pub fn as_tag(&self) -> Option<&TagRef<'a>> {
277        match self {
278            ObjectRef::Tag(v) => Some(v),
279            _ => None,
280        }
281    }
282    /// Interpret this object as tag, chainable.
283    pub fn into_tag(self) -> Option<TagRef<'a>> {
284        match self {
285            ObjectRef::Tag(v) => Some(v),
286            _ => None,
287        }
288    }
289    /// Return the kind of object.
290    pub fn kind(&self) -> Kind {
291        match self {
292            ObjectRef::Tree(_) => Kind::Tree,
293            ObjectRef::Blob(_) => Kind::Blob,
294            ObjectRef::Commit(_) => Kind::Commit,
295            ObjectRef::Tag(_) => Kind::Tag,
296        }
297    }
298}