]> git.proxmox.com Git - rustc.git/blob - src/vendor/git2/src/tree.rs
New upstream version 1.19.0+dfsg1
[rustc.git] / src / vendor / git2 / src / tree.rs
1 use std::mem;
2 use std::cmp::Ordering;
3 use std::ffi::CString;
4 use std::ops::Range;
5 use std::marker;
6 use std::path::Path;
7 use std::ptr;
8 use std::str;
9 use libc;
10
11 use {raw, Oid, Repository, Error, Object, ObjectType};
12 use util::{Binding, IntoCString};
13
14 /// A structure to represent a git [tree][1]
15 ///
16 /// [1]: http://git-scm.com/book/en/Git-Internals-Git-Objects
17 pub struct Tree<'repo> {
18 raw: *mut raw::git_tree,
19 _marker: marker::PhantomData<Object<'repo>>,
20 }
21
22 /// A structure representing an entry inside of a tree. An entry is borrowed
23 /// from a tree.
24 pub struct TreeEntry<'tree> {
25 raw: *mut raw::git_tree_entry,
26 owned: bool,
27 _marker: marker::PhantomData<&'tree raw::git_tree_entry>,
28 }
29
30 /// An iterator over the entries in a tree.
31 pub struct TreeIter<'tree> {
32 range: Range<usize>,
33 tree: &'tree Tree<'tree>,
34 }
35
36 impl<'repo> Tree<'repo> {
37 /// Get the id (SHA1) of a repository object
38 pub fn id(&self) -> Oid {
39 unsafe { Binding::from_raw(raw::git_tree_id(&*self.raw)) }
40 }
41
42 /// Get the number of entries listed in this tree.
43 pub fn len(&self) -> usize {
44 unsafe { raw::git_tree_entrycount(&*self.raw) as usize }
45 }
46
47 /// Return `true` if there is not entry
48 pub fn is_empty(&self) -> bool {
49 self.len() == 0
50 }
51
52 /// Returns an iterator over the entries in this tree.
53 pub fn iter(&self) -> TreeIter {
54 TreeIter { range: 0..self.len(), tree: self }
55 }
56
57 /// Lookup a tree entry by SHA value.
58 pub fn get_id(&self, id: Oid) -> Option<TreeEntry> {
59 unsafe {
60 let ptr = raw::git_tree_entry_byid(&*self.raw(), &*id.raw());
61 if ptr.is_null() {
62 None
63 } else {
64 Some(entry_from_raw_const(ptr))
65 }
66 }
67 }
68
69 /// Lookup a tree entry by its position in the tree
70 pub fn get(&self, n: usize) -> Option<TreeEntry> {
71 unsafe {
72 let ptr = raw::git_tree_entry_byindex(&*self.raw(),
73 n as libc::size_t);
74 if ptr.is_null() {
75 None
76 } else {
77 Some(entry_from_raw_const(ptr))
78 }
79 }
80 }
81
82 /// Lookup a tree entry by its filename
83 pub fn get_name(&self, filename: &str) -> Option<TreeEntry> {
84 let filename = CString::new(filename).unwrap();
85 unsafe {
86 let ptr = call!(raw::git_tree_entry_byname(&*self.raw(), filename));
87 if ptr.is_null() {
88 None
89 } else {
90 Some(entry_from_raw_const(ptr))
91 }
92 }
93 }
94
95 /// Retrieve a tree entry contained in a tree or in any of its subtrees,
96 /// given its relative path.
97 pub fn get_path(&self, path: &Path) -> Result<TreeEntry<'static>, Error> {
98 let path = try!(path.into_c_string());
99 let mut ret = ptr::null_mut();
100 unsafe {
101 try_call!(raw::git_tree_entry_bypath(&mut ret, &*self.raw(), path));
102 Ok(Binding::from_raw(ret))
103 }
104 }
105
106 /// Casts this Tree to be usable as an `Object`
107 pub fn as_object(&self) -> &Object<'repo> {
108 unsafe {
109 &*(self as *const _ as *const Object<'repo>)
110 }
111 }
112
113 /// Consumes Commit to be returned as an `Object`
114 pub fn into_object(self) -> Object<'repo> {
115 assert_eq!(mem::size_of_val(&self), mem::size_of::<Object>());
116 unsafe {
117 mem::transmute(self)
118 }
119 }
120 }
121
122 impl<'repo> Binding for Tree<'repo> {
123 type Raw = *mut raw::git_tree;
124
125 unsafe fn from_raw(raw: *mut raw::git_tree) -> Tree<'repo> {
126 Tree { raw: raw, _marker: marker::PhantomData }
127 }
128 fn raw(&self) -> *mut raw::git_tree { self.raw }
129 }
130
131 impl<'repo> Drop for Tree<'repo> {
132 fn drop(&mut self) {
133 unsafe { raw::git_tree_free(self.raw) }
134 }
135 }
136
137 impl<'repo, 'iter> IntoIterator for &'iter Tree<'repo> {
138 type Item = TreeEntry<'iter>;
139 type IntoIter = TreeIter<'iter>;
140 fn into_iter(self) -> Self::IntoIter {
141 self.iter()
142 }
143 }
144
145 /// Create a new tree entry from the raw pointer provided.
146 ///
147 /// The lifetime of the entry is tied to the tree provided and the function
148 /// is unsafe because the validity of the pointer cannot be guaranteed.
149 pub unsafe fn entry_from_raw_const<'tree>(raw: *const raw::git_tree_entry)
150 -> TreeEntry<'tree> {
151 TreeEntry {
152 raw: raw as *mut raw::git_tree_entry,
153 owned: false,
154 _marker: marker::PhantomData,
155 }
156 }
157
158 impl<'tree> TreeEntry<'tree> {
159 /// Get the id of the object pointed by the entry
160 pub fn id(&self) -> Oid {
161 unsafe { Binding::from_raw(raw::git_tree_entry_id(&*self.raw)) }
162 }
163
164 /// Get the filename of a tree entry
165 ///
166 /// Returns `None` if the name is not valid utf-8
167 pub fn name(&self) -> Option<&str> {
168 str::from_utf8(self.name_bytes()).ok()
169 }
170
171 /// Get the filename of a tree entry
172 pub fn name_bytes(&self) -> &[u8] {
173 unsafe {
174 ::opt_bytes(self, raw::git_tree_entry_name(&*self.raw())).unwrap()
175 }
176 }
177
178 /// Convert a tree entry to the object it points to.
179 pub fn to_object<'a>(&self, repo: &'a Repository)
180 -> Result<Object<'a>, Error> {
181 let mut ret = ptr::null_mut();
182 unsafe {
183 try_call!(raw::git_tree_entry_to_object(&mut ret, repo.raw(),
184 &*self.raw()));
185 Ok(Binding::from_raw(ret))
186 }
187 }
188
189 /// Get the type of the object pointed by the entry
190 pub fn kind(&self) -> Option<ObjectType> {
191 ObjectType::from_raw(unsafe { raw::git_tree_entry_type(&*self.raw) })
192 }
193
194 /// Get the UNIX file attributes of a tree entry
195 pub fn filemode(&self) -> i32 {
196 unsafe { raw::git_tree_entry_filemode(&*self.raw) as i32 }
197 }
198
199 /// Get the raw UNIX file attributes of a tree entry
200 pub fn filemode_raw(&self) -> i32 {
201 unsafe { raw::git_tree_entry_filemode_raw(&*self.raw) as i32 }
202 }
203
204 /// Convert this entry of any lifetime into an owned signature with a static
205 /// lifetime.
206 ///
207 /// This will use the `Clone::clone` implementation under the hood.
208 pub fn to_owned(&self) -> TreeEntry<'static> {
209 unsafe {
210 let me = mem::transmute::<&TreeEntry<'tree>, &TreeEntry<'static>>(self);
211 me.clone()
212 }
213 }
214 }
215
216 impl<'a> Binding for TreeEntry<'a> {
217 type Raw = *mut raw::git_tree_entry;
218 unsafe fn from_raw(raw: *mut raw::git_tree_entry) -> TreeEntry<'a> {
219 TreeEntry {
220 raw: raw,
221 owned: true,
222 _marker: marker::PhantomData,
223 }
224 }
225 fn raw(&self) -> *mut raw::git_tree_entry { self.raw }
226 }
227
228 impl<'a> Clone for TreeEntry<'a> {
229 fn clone(&self) -> TreeEntry<'a> {
230 let mut ret = ptr::null_mut();
231 unsafe {
232 assert_eq!(raw::git_tree_entry_dup(&mut ret, &*self.raw()), 0);
233 Binding::from_raw(ret)
234 }
235 }
236 }
237
238 impl<'a> PartialOrd for TreeEntry<'a> {
239 fn partial_cmp(&self, other: &TreeEntry<'a>) -> Option<Ordering> {
240 Some(self.cmp(other))
241 }
242 }
243 impl<'a> Ord for TreeEntry<'a> {
244 fn cmp(&self, other: &TreeEntry<'a>) -> Ordering {
245 match unsafe { raw::git_tree_entry_cmp(&*self.raw(), &*other.raw()) } {
246 0 => Ordering::Equal,
247 n if n < 0 => Ordering::Less,
248 _ => Ordering::Greater,
249 }
250 }
251 }
252
253 impl<'a> PartialEq for TreeEntry<'a> {
254 fn eq(&self, other: &TreeEntry<'a>) -> bool {
255 self.cmp(other) == Ordering::Equal
256 }
257 }
258 impl<'a> Eq for TreeEntry<'a> {}
259
260 impl<'a> Drop for TreeEntry<'a> {
261 fn drop(&mut self) {
262 if self.owned {
263 unsafe { raw::git_tree_entry_free(self.raw) }
264 }
265 }
266 }
267
268 impl<'tree> Iterator for TreeIter<'tree> {
269 type Item = TreeEntry<'tree>;
270 fn next(&mut self) -> Option<TreeEntry<'tree>> {
271 self.range.next().and_then(|i| self.tree.get(i))
272 }
273 fn size_hint(&self) -> (usize, Option<usize>) { self.range.size_hint() }
274 }
275 impl<'tree> DoubleEndedIterator for TreeIter<'tree> {
276 fn next_back(&mut self) -> Option<TreeEntry<'tree>> {
277 self.range.next_back().and_then(|i| self.tree.get(i))
278 }
279 }
280 impl<'tree> ExactSizeIterator for TreeIter<'tree> {}
281
282 #[cfg(test)]
283 mod tests {
284 use {Repository,Tree,TreeEntry,ObjectType,Object};
285 use tempdir::TempDir;
286 use std::fs::File;
287 use std::io::prelude::*;
288 use std::path::Path;
289
290 pub struct TestTreeIter<'a> {
291 entries: Vec<TreeEntry<'a>>,
292 repo: &'a Repository,
293 }
294
295 impl<'a> Iterator for TestTreeIter<'a> {
296 type Item = TreeEntry<'a>;
297
298 fn next(&mut self) -> Option<TreeEntry<'a> > {
299 if self.entries.is_empty() {
300 None
301 } else {
302 let entry = self.entries.remove(0);
303
304 match entry.kind() {
305 Some(ObjectType::Tree) => {
306 let obj: Object<'a> = entry.to_object(self.repo).unwrap();
307
308 let tree: &Tree<'a> = obj.as_tree().unwrap();
309
310 for entry in tree.iter() {
311 self.entries.push(entry.to_owned());
312 }
313 }
314 _ => {}
315 }
316
317 Some(entry)
318 }
319 }
320 }
321
322 fn tree_iter<'repo>(tree: &Tree<'repo>, repo: &'repo Repository)
323 -> TestTreeIter<'repo> {
324 let mut initial = vec![];
325
326 for entry in tree.iter() {
327 initial.push(entry.to_owned());
328 }
329
330 TestTreeIter {
331 entries: initial,
332 repo: repo,
333 }
334 }
335
336 #[test]
337 fn smoke_tree_iter() {
338 let (td, repo) = ::test::repo_init();
339
340 setup_repo(&td, &repo);
341
342 let head = repo.head().unwrap();
343 let target = head.target().unwrap();
344 let commit = repo.find_commit(target).unwrap();
345
346 let tree = repo.find_tree(commit.tree_id()).unwrap();
347 assert_eq!(tree.id(), commit.tree_id());
348 assert_eq!(tree.len(), 1);
349
350 for entry in tree_iter(&tree, &repo) {
351 println!("iter entry {:?}", entry.name());
352 }
353 }
354
355 fn setup_repo(td: &TempDir, repo: &Repository) {
356 let mut index = repo.index().unwrap();
357 File::create(&td.path().join("foo")).unwrap().write_all(b"foo").unwrap();
358 index.add_path(Path::new("foo")).unwrap();
359 let id = index.write_tree().unwrap();
360 let sig = repo.signature().unwrap();
361 let tree = repo.find_tree(id).unwrap();
362 let parent = repo.find_commit(repo.head().unwrap().target()
363 .unwrap()).unwrap();
364 repo.commit(Some("HEAD"), &sig, &sig, "another commit",
365 &tree, &[&parent]).unwrap();
366 }
367
368 #[test]
369 fn smoke() {
370 let (td, repo) = ::test::repo_init();
371
372 setup_repo(&td, &repo);
373
374 let head = repo.head().unwrap();
375 let target = head.target().unwrap();
376 let commit = repo.find_commit(target).unwrap();
377
378 let tree = repo.find_tree(commit.tree_id()).unwrap();
379 assert_eq!(tree.id(), commit.tree_id());
380 assert_eq!(tree.len(), 1);
381 {
382 let e1 = tree.get(0).unwrap();
383 assert!(e1 == tree.get_id(e1.id()).unwrap());
384 assert!(e1 == tree.get_name("foo").unwrap());
385 assert!(e1 == tree.get_path(Path::new("foo")).unwrap());
386 assert_eq!(e1.name(), Some("foo"));
387 e1.to_object(&repo).unwrap();
388 }
389 tree.into_object();
390
391 repo.find_object(commit.tree_id(), None).unwrap().as_tree().unwrap();
392 repo.find_object(commit.tree_id(), None).unwrap().into_tree().ok().unwrap();
393 }
394 }