]> git.proxmox.com Git - rustc.git/blob - vendor/git2/src/object.rs
New upstream version 1.70.0+dfsg2
[rustc.git] / vendor / git2 / src / object.rs
1 use std::marker;
2 use std::mem;
3 use std::ptr;
4
5 use crate::util::Binding;
6 use crate::{raw, Blob, Buf, Commit, Error, ObjectType, Oid, Repository, Tag, Tree};
7 use crate::{Describe, DescribeOptions};
8
9 /// A structure to represent a git [object][1]
10 ///
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>,
15 }
16
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)) }
21 }
22
23 /// Get the object type of an object.
24 ///
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) })
28 }
29
30 /// Recursively peel an object until an object of the specified type is met.
31 ///
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();
37 unsafe {
38 try_call!(raw::git_object_peel(&mut raw, &*self.raw(), kind));
39 Ok(Binding::from_raw(raw))
40 }
41 }
42
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))
47 }
48
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))
53 }
54
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))
59 }
60
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))
65 }
66
67 /// Get a short abbreviated OID string for the object
68 ///
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
72 /// repository).
73 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 /// Attempt to view this object as a commit.
82 ///
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)
86 }
87
88 /// Attempt to consume this object and return a commit.
89 ///
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)
93 }
94
95 /// Attempt to view this object as a tag.
96 ///
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)
100 }
101
102 /// Attempt to consume this object and return a tag.
103 ///
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)
107 }
108
109 /// Attempt to view this object as a tree.
110 ///
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)
114 }
115
116 /// Attempt to consume this object and return a tree.
117 ///
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)
121 }
122
123 /// Attempt to view this object as a blob.
124 ///
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)
128 }
129
130 /// Attempt to consume this object and return a blob.
131 ///
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)
135 }
136
137 /// Describes a commit
138 ///
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();
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
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;
174 }
175
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) {
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
204 impl<'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
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");
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
230 impl<'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
244 impl<'repo> Drop for Object<'repo> {
245 fn drop(&mut self) {
246 unsafe { raw::git_object_free(self.raw) }
247 }
248 }