4 use libc
::{c_int, c_void}
;
6 use crate::util
::{Binding, IntoCString}
;
7 use crate::{panic, raw, tree, Error, Oid, Repository, TreeEntry}
;
9 /// Constructor for in-memory trees (low-level)
11 /// You probably want to use [`build::TreeUpdateBuilder`] instead.
13 /// This is the more raw of the two tree update facilities. It
14 /// handles only one level of a nested tree structure at a time. Each
15 /// path passed to `insert` etc. must be a single component.
17 /// [`build::TreeUpdateBuilder`]: crate::build::TreeUpdateBuilder
18 pub struct TreeBuilder
<'repo
> {
19 raw
: *mut raw
::git_treebuilder
,
20 _marker
: marker
::PhantomData
<&'repo Repository
>,
23 impl<'repo
> TreeBuilder
<'repo
> {
24 /// Clear all the entries in the builder
25 pub fn clear(&mut self) -> Result
<(), Error
> {
27 try_call
!(raw
::git_treebuilder_clear(self.raw
));
32 /// Get the number of entries
33 pub fn len(&self) -> usize {
34 unsafe { raw::git_treebuilder_entrycount(self.raw) as usize }
37 /// Return `true` if there is no entry
38 pub fn is_empty(&self) -> bool
{
42 /// Get en entry from the builder from its filename
43 pub fn get
<P
>(&self, filename
: P
) -> Result
<Option
<TreeEntry
<'_
>>, Error
>
47 let filename
= filename
.into_c_string()?
;
49 let ret
= raw
::git_treebuilder_get(self.raw
, filename
.as_ptr());
53 Ok(Some(tree
::entry_from_raw_const(ret
)))
58 /// Add or update an entry in the builder
60 /// No attempt is made to ensure that the provided Oid points to
61 /// an object of a reasonable type (or any object at all).
63 /// The mode given must be one of 0o040000, 0o100644, 0o100755, 0o120000 or
64 /// 0o160000 currently.
65 pub fn insert
<P
: IntoCString
>(
70 ) -> Result
<TreeEntry
<'_
>, Error
> {
71 let filename
= filename
.into_c_string()?
;
72 let filemode
= filemode
as raw
::git_filemode_t
;
74 let mut ret
= ptr
::null();
76 try_call
!(raw
::git_treebuilder_insert(
83 Ok(tree
::entry_from_raw_const(ret
))
87 /// Remove an entry from the builder by its filename
88 pub fn remove
<P
: IntoCString
>(&mut self, filename
: P
) -> Result
<(), Error
> {
89 let filename
= filename
.into_c_string()?
;
91 try_call
!(raw
::git_treebuilder_remove(self.raw
, filename
));
96 /// Selectively remove entries from the tree
98 /// Values for which the filter returns `true` will be kept. Note
99 /// that this behavior is different from the libgit2 C interface.
100 pub fn filter
<F
>(&mut self, mut filter
: F
) -> Result
<(), Error
>
102 F
: FnMut(&TreeEntry
<'_
>) -> bool
,
104 let mut cb
: &mut FilterCb
<'_
> = &mut filter
;
105 let ptr
= &mut cb
as *mut _
;
106 let cb
: raw
::git_treebuilder_filter_cb
= Some(filter_cb
);
108 try_call
!(raw
::git_treebuilder_filter(self.raw
, cb
, ptr
as *mut _
));
114 /// Write the contents of the TreeBuilder as a Tree object and
116 pub fn write(&self) -> Result
<Oid
, Error
> {
117 let mut raw
= raw
::git_oid
{
118 id
: [0; raw
::GIT_OID_RAWSZ
],
121 try_call
!(raw
::git_treebuilder_write(&mut raw
, self.raw()));
122 Ok(Binding
::from_raw(&raw
as *const _
))
127 type FilterCb
<'a
> = dyn FnMut(&TreeEntry
<'_
>) -> bool
+ 'a
;
129 extern "C" fn filter_cb(entry
: *const raw
::git_tree_entry
, payload
: *mut c_void
) -> c_int
{
130 let ret
= panic
::wrap(|| unsafe {
131 // There's no way to return early from git_treebuilder_filter.
132 if panic
::panicked() {
135 let entry
= tree
::entry_from_raw_const(entry
);
136 let payload
= payload
as *mut &mut FilterCb
<'_
>;
140 if ret
== Some(false) {
147 impl<'repo
> Binding
for TreeBuilder
<'repo
> {
148 type Raw
= *mut raw
::git_treebuilder
;
150 unsafe fn from_raw(raw
: *mut raw
::git_treebuilder
) -> TreeBuilder
<'repo
> {
153 _marker
: marker
::PhantomData
,
156 fn raw(&self) -> *mut raw
::git_treebuilder
{
161 impl<'repo
> Drop
for TreeBuilder
<'repo
> {
163 unsafe { raw::git_treebuilder_free(self.raw) }
169 use crate::ObjectType
;
173 let (_td
, repo
) = crate::test
::repo_init();
175 let mut builder
= repo
.treebuilder(None
).unwrap();
176 assert_eq
!(builder
.len(), 0);
177 let blob
= repo
.blob(b
"data").unwrap();
179 let entry
= builder
.insert("a", blob
, 0o100644).unwrap();
180 assert_eq
!(entry
.kind(), Some(ObjectType
::Blob
));
182 builder
.insert("b", blob
, 0o100644).unwrap();
183 assert_eq
!(builder
.len(), 2);
184 builder
.remove("a").unwrap();
185 assert_eq
!(builder
.len(), 1);
186 assert_eq
!(builder
.get("b").unwrap().unwrap().id(), blob
);
187 builder
.clear().unwrap();
188 assert_eq
!(builder
.len(), 0);
193 let (_td
, repo
) = crate::test
::repo_init();
195 let mut builder
= repo
.treebuilder(None
).unwrap();
196 let data
= repo
.blob(b
"data").unwrap();
197 builder
.insert("name", data
, 0o100644).unwrap();
198 let tree
= builder
.write().unwrap();
199 let tree
= repo
.find_tree(tree
).unwrap();
200 let entry
= tree
.get(0).unwrap();
201 assert_eq
!(entry
.name(), Some("name"));
202 let blob
= entry
.to_object(&repo
).unwrap();
203 let blob
= blob
.as_blob().unwrap();
204 assert_eq
!(blob
.content(), b
"data");
206 let builder
= repo
.treebuilder(Some(&tree
)).unwrap();
207 assert_eq
!(builder
.len(), 1);
212 let (_td
, repo
) = crate::test
::repo_init();
214 let mut builder
= repo
.treebuilder(None
).unwrap();
215 let blob
= repo
.blob(b
"data").unwrap();
217 let head
= repo
.head().unwrap().peel(ObjectType
::Commit
).unwrap();
218 let head
= head
.as_commit().unwrap();
221 builder
.insert("blob", blob
, 0o100644).unwrap();
222 builder
.insert("dir", tree
, 0o040000).unwrap();
223 builder
.insert("dir2", tree
, 0o040000).unwrap();
225 builder
.filter(|_
| true).unwrap();
226 assert_eq
!(builder
.len(), 3);
228 .filter(|e
| e
.kind().unwrap() != ObjectType
::Blob
)
230 assert_eq
!(builder
.len(), 2);
231 builder
.filter(|_
| false).unwrap();
232 assert_eq
!(builder
.len(), 0);