git_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<'a> WriteTo for ObjectRef<'a> {
12        /// Write the contained object to `out` in the git serialization format.
13        fn write_to(&self, out: impl 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 size(&self) -> usize {
24            use crate::ObjectRef::*;
25            match self {
26                Tree(v) => v.size(),
27                Blob(v) => v.size(),
28                Commit(v) => v.size(),
29                Tag(v) => v.size(),
30            }
31        }
32
33        fn kind(&self) -> Kind {
34            self.kind()
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: impl 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 size(&self) -> usize {
52            use crate::Object::*;
53            match self {
54                Tree(v) => v.size(),
55                Blob(v) => v.size(),
56                Commit(v) => v.size(),
57                Tag(v) => v.size(),
58            }
59        }
60
61        fn kind(&self) -> Kind {
62            self.kind()
63        }
64    }
65}
66
67/// Convenient extraction of typed object.
68impl Object {
69    /// Turns this instance into a [`Blob`][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`][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`][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`][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`][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`][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`][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`][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`][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`][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`][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`][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`][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}
189
190impl<'a> ObjectRef<'a> {
191    /// Deserialize an object from a loose serialisation
192    pub fn from_loose(data: &'a [u8]) -> Result<ObjectRef<'a>, LooseDecodeError> {
193        let (kind, size, offset) = loose_header(data)?;
194
195        let body = &data[offset..]
196            .get(..size)
197            .ok_or(LooseHeaderDecodeError::InvalidHeader {
198                message: "object data was shorter than its size declared in the header",
199            })?;
200
201        Ok(Self::from_bytes(kind, body)?)
202    }
203
204    /// Deserialize an object of `kind` from the given `data`.
205    pub fn from_bytes(kind: Kind, data: &'a [u8]) -> Result<ObjectRef<'a>, crate::decode::Error> {
206        Ok(match kind {
207            Kind::Tree => ObjectRef::Tree(TreeRef::from_bytes(data)?),
208            Kind::Blob => ObjectRef::Blob(BlobRef { data }),
209            Kind::Commit => ObjectRef::Commit(CommitRef::from_bytes(data)?),
210            Kind::Tag => ObjectRef::Tag(TagRef::from_bytes(data)?),
211        })
212    }
213
214    /// Convert the immutable object into a mutable version, consuming the source in the process.
215    ///
216    /// Note that this is an expensive operation.
217    pub fn into_owned(self) -> Object {
218        self.into()
219    }
220
221    /// Convert this immutable object into its mutable counterpart.
222    ///
223    /// Note that this is an expensive operation.
224    pub fn to_owned(&self) -> Object {
225        self.clone().into()
226    }
227}
228
229/// Convenient access to contained objects.
230impl<'a> ObjectRef<'a> {
231    /// Interpret this object as blob.
232    pub fn as_blob(&self) -> Option<&BlobRef<'a>> {
233        match self {
234            ObjectRef::Blob(v) => Some(v),
235            _ => None,
236        }
237    }
238    /// Interpret this object as blob, chainable.
239    pub fn into_blob(self) -> Option<BlobRef<'a>> {
240        match self {
241            ObjectRef::Blob(v) => Some(v),
242            _ => None,
243        }
244    }
245    /// Interpret this object as commit.
246    pub fn as_commit(&self) -> Option<&CommitRef<'a>> {
247        match self {
248            ObjectRef::Commit(v) => Some(v),
249            _ => None,
250        }
251    }
252    /// Interpret this object as commit, chainable.
253    pub fn into_commit(self) -> Option<CommitRef<'a>> {
254        match self {
255            ObjectRef::Commit(v) => Some(v),
256            _ => None,
257        }
258    }
259    /// Interpret this object as tree.
260    pub fn as_tree(&self) -> Option<&TreeRef<'a>> {
261        match self {
262            ObjectRef::Tree(v) => Some(v),
263            _ => None,
264        }
265    }
266    /// Interpret this object as tree, chainable
267    pub fn into_tree(self) -> Option<TreeRef<'a>> {
268        match self {
269            ObjectRef::Tree(v) => Some(v),
270            _ => None,
271        }
272    }
273    /// Interpret this object as tag.
274    pub fn as_tag(&self) -> Option<&TagRef<'a>> {
275        match self {
276            ObjectRef::Tag(v) => Some(v),
277            _ => None,
278        }
279    }
280    /// Interpret this object as tag, chainable.
281    pub fn into_tag(self) -> Option<TagRef<'a>> {
282        match self {
283            ObjectRef::Tag(v) => Some(v),
284            _ => None,
285        }
286    }
287    /// Return the kind of object.
288    pub fn kind(&self) -> Kind {
289        match self {
290            ObjectRef::Tree(_) => Kind::Tree,
291            ObjectRef::Blob(_) => Kind::Blob,
292            ObjectRef::Commit(_) => Kind::Commit,
293            ObjectRef::Tag(_) => Kind::Tag,
294        }
295    }
296}