5 use crate::util
::Binding
;
6 use crate::{raw, Blob, Buf, Commit, Error, ObjectType, Oid, Repository, Tag, Tree}
;
7 use crate::{Describe, DescribeOptions}
;
9 /// A structure to represent a git [object][1]
11 /// [1]: http://git-scm.com/book/en/Git-Internals-Git-Objects
12 pub struct Object
<'repo
> {
13 raw
: *mut raw
::git_object
,
14 _marker
: marker
::PhantomData
<&'repo Repository
>,
17 impl<'repo
> Object
<'repo
> {
18 /// Get the id (SHA1) of a repository object
19 pub fn id(&self) -> Oid
{
20 unsafe { Binding::from_raw(raw::git_object_id(&*self.raw)) }
23 /// Get the object type of an object.
25 /// If the type is unknown, then `None` is returned.
26 pub fn kind(&self) -> Option
<ObjectType
> {
27 ObjectType
::from_raw(unsafe { raw::git_object_type(&*self.raw) }
)
30 /// Recursively peel an object until an object of the specified type is met.
32 /// If you pass `Any` as the target type, then the object will be
33 /// peeled until the type changes (e.g. a tag will be chased until the
34 /// referenced object is no longer a tag).
35 pub fn peel(&self, kind
: ObjectType
) -> Result
<Object
<'repo
>, Error
> {
36 let mut raw
= ptr
::null_mut();
38 try_call
!(raw
::git_object_peel(&mut raw
, &*self.raw(), kind
));
39 Ok(Binding
::from_raw(raw
))
43 /// Recursively peel an object until a blob is found
44 pub fn peel_to_blob(&self) -> Result
<Blob
<'repo
>, Error
> {
45 self.peel(ObjectType
::Blob
)
46 .map(|o
| o
.cast_or_panic(ObjectType
::Blob
))
49 /// Recursively peel an object until a commit is found
50 pub fn peel_to_commit(&self) -> Result
<Commit
<'repo
>, Error
> {
51 self.peel(ObjectType
::Commit
)
52 .map(|o
| o
.cast_or_panic(ObjectType
::Commit
))
55 /// Recursively peel an object until a tag is found
56 pub fn peel_to_tag(&self) -> Result
<Tag
<'repo
>, Error
> {
57 self.peel(ObjectType
::Tag
)
58 .map(|o
| o
.cast_or_panic(ObjectType
::Tag
))
61 /// Recursively peel an object until a tree is found
62 pub fn peel_to_tree(&self) -> Result
<Tree
<'repo
>, Error
> {
63 self.peel(ObjectType
::Tree
)
64 .map(|o
| o
.cast_or_panic(ObjectType
::Tree
))
67 /// Get a short abbreviated OID string for the object
69 /// This starts at the "core.abbrev" length (default 7 characters) and
70 /// iteratively extends to a longer string if that length is ambiguous. The
71 /// result will be unambiguous (at least until new objects are added to the
73 pub fn short_id(&self) -> Result
<Buf
, Error
> {
76 try_call
!(raw
::git_object_short_id(buf
.raw(), &*self.raw()));
81 /// Attempt to view this object as a commit.
83 /// Returns `None` if the object is not actually a commit.
84 pub fn as_commit(&self) -> Option
<&Commit
<'repo
>> {
85 self.cast(ObjectType
::Commit
)
88 /// Attempt to consume this object and return a commit.
90 /// Returns `Err(self)` if this object is not actually a commit.
91 pub fn into_commit(self) -> Result
<Commit
<'repo
>, Object
<'repo
>> {
92 self.cast_into(ObjectType
::Commit
)
95 /// Attempt to view this object as a tag.
97 /// Returns `None` if the object is not actually a tag.
98 pub fn as_tag(&self) -> Option
<&Tag
<'repo
>> {
99 self.cast(ObjectType
::Tag
)
102 /// Attempt to consume this object and return a tag.
104 /// Returns `Err(self)` if this object is not actually a tag.
105 pub fn into_tag(self) -> Result
<Tag
<'repo
>, Object
<'repo
>> {
106 self.cast_into(ObjectType
::Tag
)
109 /// Attempt to view this object as a tree.
111 /// Returns `None` if the object is not actually a tree.
112 pub fn as_tree(&self) -> Option
<&Tree
<'repo
>> {
113 self.cast(ObjectType
::Tree
)
116 /// Attempt to consume this object and return a tree.
118 /// Returns `Err(self)` if this object is not actually a tree.
119 pub fn into_tree(self) -> Result
<Tree
<'repo
>, Object
<'repo
>> {
120 self.cast_into(ObjectType
::Tree
)
123 /// Attempt to view this object as a blob.
125 /// Returns `None` if the object is not actually a blob.
126 pub fn as_blob(&self) -> Option
<&Blob
<'repo
>> {
127 self.cast(ObjectType
::Blob
)
130 /// Attempt to consume this object and return a blob.
132 /// Returns `Err(self)` if this object is not actually a blob.
133 pub fn into_blob(self) -> Result
<Blob
<'repo
>, Object
<'repo
>> {
134 self.cast_into(ObjectType
::Blob
)
137 /// Describes a commit
139 /// Performs a describe operation on this commitish object.
140 pub fn describe(&self, opts
: &DescribeOptions
) -> Result
<Describe
<'_
>, Error
> {
141 let mut ret
= ptr
::null_mut();
143 try_call
!(raw
::git_describe_commit(&mut ret
, self.raw
, opts
.raw()));
144 Ok(Binding
::from_raw(ret
))
148 fn cast
<T
>(&self, kind
: ObjectType
) -> Option
<&T
> {
149 assert_eq
!(mem
::size_of
::<Object
<'_
>>(), mem
::size_of
::<T
>());
150 if self.kind() == Some(kind
) {
151 unsafe { Some(&*(self as *const _ as *const T)) }
157 fn cast_into
<T
>(self, kind
: ObjectType
) -> Result
<T
, Object
<'repo
>> {
158 assert_eq
!(mem
::size_of_val(&self), mem
::size_of
::<T
>());
159 if self.kind() == Some(kind
) {
161 let other
= ptr
::read(&self as *const _
as *const T
);
171 /// This trait is useful to export cast_or_panic into crate but not outside
172 pub trait CastOrPanic
{
173 fn cast_or_panic
<T
>(self, kind
: ObjectType
) -> T
;
176 impl<'repo
> CastOrPanic
for Object
<'repo
> {
177 fn cast_or_panic
<T
>(self, kind
: ObjectType
) -> T
{
178 assert_eq
!(mem
::size_of_val(&self), mem
::size_of
::<T
>());
179 if self.kind() == Some(kind
) {
181 let other
= ptr
::read(&self as *const _
as *const T
);
187 let akind
= match self.kind() {
188 Some(akind
) => akind
.str(),
190 buf
= format
!("unknown ({})", unsafe { raw::git_object_type(&*self.raw) }
);
195 "Expected object {} to be {} but it is {}",
204 impl<'repo
> Clone
for Object
<'repo
> {
205 fn clone(&self) -> Object
<'repo
> {
206 let mut raw
= ptr
::null_mut();
208 let rc
= raw
::git_object_dup(&mut raw
, self.raw
);
210 Binding
::from_raw(raw
)
215 impl<'repo
> std
::fmt
::Debug
for Object
<'repo
> {
216 fn fmt(&self, f
: &mut std
::fmt
::Formatter
<'_
>) -> Result
<(), std
::fmt
::Error
> {
217 let mut ds
= f
.debug_struct("Object");
219 Some(kind
) => ds
.field("kind", &kind
),
222 &format
!("Unknow ({})", unsafe { raw::git_object_type(&*self.raw) }
),
225 ds
.field("id", &self.id());
230 impl<'repo
> Binding
for Object
<'repo
> {
231 type Raw
= *mut raw
::git_object
;
233 unsafe fn from_raw(raw
: *mut raw
::git_object
) -> Object
<'repo
> {
236 _marker
: marker
::PhantomData
,
239 fn raw(&self) -> *mut raw
::git_object
{
244 impl<'repo
> Drop
for Object
<'repo
> {
246 unsafe { raw::git_object_free(self.raw) }