1use std::marker;
2use std::mem;
3use std::ptr;
4
5use crate::util::Binding;
6use crate::{raw, Blob, Buf, Commit, Error, ObjectType, Oid, Repository, Tag, Tree};
7use crate::{Describe, DescribeOptions};
8
9pub struct Object<'repo> {
13 raw: *mut raw::git_object,
14 _marker: marker::PhantomData<&'repo Repository>,
15}
16
17impl<'repo> Object<'repo> {
18 pub fn id(&self) -> Oid {
20 unsafe { Binding::from_raw(raw::git_object_id(&*self.raw)) }
21 }
22
23 pub fn kind(&self) -> Option<ObjectType> {
27 ObjectType::from_raw(unsafe { raw::git_object_type(&*self.raw) })
28 }
29
30 pub fn peel(&self, kind: ObjectType) -> Result<Object<'repo>, Error> {
36 let mut raw = ptr::null_mut();
37 unsafe {
38 try_call!(raw::git_object_peel(&mut raw, &*self.raw(), kind));
39 Ok(Binding::from_raw(raw))
40 }
41 }
42
43 pub fn peel_to_blob(&self) -> Result<Blob<'repo>, Error> {
45 self.peel(ObjectType::Blob)
46 .map(|o| o.cast_or_panic(ObjectType::Blob))
47 }
48
49 pub fn peel_to_commit(&self) -> Result<Commit<'repo>, Error> {
51 self.peel(ObjectType::Commit)
52 .map(|o| o.cast_or_panic(ObjectType::Commit))
53 }
54
55 pub fn peel_to_tag(&self) -> Result<Tag<'repo>, Error> {
57 self.peel(ObjectType::Tag)
58 .map(|o| o.cast_or_panic(ObjectType::Tag))
59 }
60
61 pub fn peel_to_tree(&self) -> Result<Tree<'repo>, Error> {
63 self.peel(ObjectType::Tree)
64 .map(|o| o.cast_or_panic(ObjectType::Tree))
65 }
66
67 pub fn short_id(&self) -> Result<Buf, Error> {
74 unsafe {
75 let buf = Buf::new();
76 try_call!(raw::git_object_short_id(buf.raw(), &*self.raw()));
77 Ok(buf)
78 }
79 }
80
81 pub fn as_commit(&self) -> Option<&Commit<'repo>> {
85 self.cast(ObjectType::Commit)
86 }
87
88 pub fn into_commit(self) -> Result<Commit<'repo>, Object<'repo>> {
92 self.cast_into(ObjectType::Commit)
93 }
94
95 pub fn as_tag(&self) -> Option<&Tag<'repo>> {
99 self.cast(ObjectType::Tag)
100 }
101
102 pub fn into_tag(self) -> Result<Tag<'repo>, Object<'repo>> {
106 self.cast_into(ObjectType::Tag)
107 }
108
109 pub fn as_tree(&self) -> Option<&Tree<'repo>> {
113 self.cast(ObjectType::Tree)
114 }
115
116 pub fn into_tree(self) -> Result<Tree<'repo>, Object<'repo>> {
120 self.cast_into(ObjectType::Tree)
121 }
122
123 pub fn as_blob(&self) -> Option<&Blob<'repo>> {
127 self.cast(ObjectType::Blob)
128 }
129
130 pub fn into_blob(self) -> Result<Blob<'repo>, Object<'repo>> {
134 self.cast_into(ObjectType::Blob)
135 }
136
137 pub fn describe(&self, opts: &DescribeOptions) -> Result<Describe<'_>, Error> {
141 let mut ret = ptr::null_mut();
142 unsafe {
143 try_call!(raw::git_describe_commit(&mut ret, self.raw, opts.raw()));
144 Ok(Binding::from_raw(ret))
145 }
146 }
147
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)) }
152 } else {
153 None
154 }
155 }
156
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) {
160 Ok(unsafe {
161 let other = ptr::read(&self as *const _ as *const T);
162 mem::forget(self);
163 other
164 })
165 } else {
166 Err(self)
167 }
168 }
169}
170
171pub trait CastOrPanic {
173 fn cast_or_panic<T>(self, kind: ObjectType) -> T;
174}
175
176impl<'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) {
180 unsafe {
181 let other = ptr::read(&self as *const _ as *const T);
182 mem::forget(self);
183 other
184 }
185 } else {
186 let buf;
187 let akind = match self.kind() {
188 Some(akind) => akind.str(),
189 None => {
190 buf = format!("unknown ({})", unsafe { raw::git_object_type(&*self.raw) });
191 &buf
192 }
193 };
194 panic!(
195 "Expected object {} to be {} but it is {}",
196 self.id(),
197 kind.str(),
198 akind
199 )
200 }
201 }
202}
203
204impl<'repo> Clone for Object<'repo> {
205 fn clone(&self) -> Object<'repo> {
206 let mut raw = ptr::null_mut();
207 unsafe {
208 let rc = raw::git_object_dup(&mut raw, self.raw);
209 assert_eq!(rc, 0);
210 Binding::from_raw(raw)
211 }
212 }
213}
214
215impl<'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");
218 match self.kind() {
219 Some(kind) => ds.field("kind", &kind),
220 None => ds.field(
221 "kind",
222 &format!("Unknow ({})", unsafe { raw::git_object_type(&*self.raw) }),
223 ),
224 };
225 ds.field("id", &self.id());
226 ds.finish()
227 }
228}
229
230impl<'repo> Binding for Object<'repo> {
231 type Raw = *mut raw::git_object;
232
233 unsafe fn from_raw(raw: *mut raw::git_object) -> Object<'repo> {
234 Object {
235 raw,
236 _marker: marker::PhantomData,
237 }
238 }
239 fn raw(&self) -> *mut raw::git_object {
240 self.raw
241 }
242}
243
244impl<'repo> Drop for Object<'repo> {
245 fn drop(&mut self) {
246 unsafe { raw::git_object_free(self.raw) }
247 }
248}