summaryrefslogtreecommitdiffstats
path: root/vendor/git2/src/treebuilder.rs
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/git2/src/treebuilder.rs')
-rw-r--r--vendor/git2/src/treebuilder.rs234
1 files changed, 234 insertions, 0 deletions
diff --git a/vendor/git2/src/treebuilder.rs b/vendor/git2/src/treebuilder.rs
new file mode 100644
index 0000000..1548a04
--- /dev/null
+++ b/vendor/git2/src/treebuilder.rs
@@ -0,0 +1,234 @@
+use std::marker;
+use std::ptr;
+
+use libc::{c_int, c_void};
+
+use crate::util::{Binding, IntoCString};
+use crate::{panic, raw, tree, Error, Oid, Repository, TreeEntry};
+
+/// Constructor for in-memory trees (low-level)
+///
+/// You probably want to use [`build::TreeUpdateBuilder`] instead.
+///
+/// This is the more raw of the two tree update facilities. It
+/// handles only one level of a nested tree structure at a time. Each
+/// path passed to `insert` etc. must be a single component.
+///
+/// [`build::TreeUpdateBuilder`]: crate::build::TreeUpdateBuilder
+pub struct TreeBuilder<'repo> {
+ raw: *mut raw::git_treebuilder,
+ _marker: marker::PhantomData<&'repo Repository>,
+}
+
+impl<'repo> TreeBuilder<'repo> {
+ /// Clear all the entries in the builder
+ pub fn clear(&mut self) -> Result<(), Error> {
+ unsafe {
+ try_call!(raw::git_treebuilder_clear(self.raw));
+ }
+ Ok(())
+ }
+
+ /// Get the number of entries
+ pub fn len(&self) -> usize {
+ unsafe { raw::git_treebuilder_entrycount(self.raw) as usize }
+ }
+
+ /// Return `true` if there is no entry
+ pub fn is_empty(&self) -> bool {
+ self.len() == 0
+ }
+
+ /// Get en entry from the builder from its filename
+ pub fn get<P>(&self, filename: P) -> Result<Option<TreeEntry<'_>>, Error>
+ where
+ P: IntoCString,
+ {
+ let filename = filename.into_c_string()?;
+ unsafe {
+ let ret = raw::git_treebuilder_get(self.raw, filename.as_ptr());
+ if ret.is_null() {
+ Ok(None)
+ } else {
+ Ok(Some(tree::entry_from_raw_const(ret)))
+ }
+ }
+ }
+
+ /// Add or update an entry in the builder
+ ///
+ /// No attempt is made to ensure that the provided Oid points to
+ /// an object of a reasonable type (or any object at all).
+ ///
+ /// The mode given must be one of 0o040000, 0o100644, 0o100755, 0o120000 or
+ /// 0o160000 currently.
+ pub fn insert<P: IntoCString>(
+ &mut self,
+ filename: P,
+ oid: Oid,
+ filemode: i32,
+ ) -> Result<TreeEntry<'_>, Error> {
+ let filename = filename.into_c_string()?;
+ let filemode = filemode as raw::git_filemode_t;
+
+ let mut ret = ptr::null();
+ unsafe {
+ try_call!(raw::git_treebuilder_insert(
+ &mut ret,
+ self.raw,
+ filename,
+ oid.raw(),
+ filemode
+ ));
+ Ok(tree::entry_from_raw_const(ret))
+ }
+ }
+
+ /// Remove an entry from the builder by its filename
+ pub fn remove<P: IntoCString>(&mut self, filename: P) -> Result<(), Error> {
+ let filename = filename.into_c_string()?;
+ unsafe {
+ try_call!(raw::git_treebuilder_remove(self.raw, filename));
+ }
+ Ok(())
+ }
+
+ /// Selectively remove entries from the tree
+ ///
+ /// Values for which the filter returns `true` will be kept. Note
+ /// that this behavior is different from the libgit2 C interface.
+ pub fn filter<F>(&mut self, mut filter: F) -> Result<(), Error>
+ where
+ F: FnMut(&TreeEntry<'_>) -> bool,
+ {
+ let mut cb: &mut FilterCb<'_> = &mut filter;
+ let ptr = &mut cb as *mut _;
+ let cb: raw::git_treebuilder_filter_cb = Some(filter_cb);
+ unsafe {
+ try_call!(raw::git_treebuilder_filter(self.raw, cb, ptr as *mut _));
+ panic::check();
+ }
+ Ok(())
+ }
+
+ /// Write the contents of the TreeBuilder as a Tree object and
+ /// return its Oid
+ pub fn write(&self) -> Result<Oid, Error> {
+ let mut raw = raw::git_oid {
+ id: [0; raw::GIT_OID_RAWSZ],
+ };
+ unsafe {
+ try_call!(raw::git_treebuilder_write(&mut raw, self.raw()));
+ Ok(Binding::from_raw(&raw as *const _))
+ }
+ }
+}
+
+type FilterCb<'a> = dyn FnMut(&TreeEntry<'_>) -> bool + 'a;
+
+extern "C" fn filter_cb(entry: *const raw::git_tree_entry, payload: *mut c_void) -> c_int {
+ let ret = panic::wrap(|| unsafe {
+ // There's no way to return early from git_treebuilder_filter.
+ if panic::panicked() {
+ true
+ } else {
+ let entry = tree::entry_from_raw_const(entry);
+ let payload = payload as *mut &mut FilterCb<'_>;
+ (*payload)(&entry)
+ }
+ });
+ if ret == Some(false) {
+ 1
+ } else {
+ 0
+ }
+}
+
+impl<'repo> Binding for TreeBuilder<'repo> {
+ type Raw = *mut raw::git_treebuilder;
+
+ unsafe fn from_raw(raw: *mut raw::git_treebuilder) -> TreeBuilder<'repo> {
+ TreeBuilder {
+ raw,
+ _marker: marker::PhantomData,
+ }
+ }
+ fn raw(&self) -> *mut raw::git_treebuilder {
+ self.raw
+ }
+}
+
+impl<'repo> Drop for TreeBuilder<'repo> {
+ fn drop(&mut self) {
+ unsafe { raw::git_treebuilder_free(self.raw) }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::ObjectType;
+
+ #[test]
+ fn smoke() {
+ let (_td, repo) = crate::test::repo_init();
+
+ let mut builder = repo.treebuilder(None).unwrap();
+ assert_eq!(builder.len(), 0);
+ let blob = repo.blob(b"data").unwrap();
+ {
+ let entry = builder.insert("a", blob, 0o100644).unwrap();
+ assert_eq!(entry.kind(), Some(ObjectType::Blob));
+ }
+ builder.insert("b", blob, 0o100644).unwrap();
+ assert_eq!(builder.len(), 2);
+ builder.remove("a").unwrap();
+ assert_eq!(builder.len(), 1);
+ assert_eq!(builder.get("b").unwrap().unwrap().id(), blob);
+ builder.clear().unwrap();
+ assert_eq!(builder.len(), 0);
+ }
+
+ #[test]
+ fn write() {
+ let (_td, repo) = crate::test::repo_init();
+
+ let mut builder = repo.treebuilder(None).unwrap();
+ let data = repo.blob(b"data").unwrap();
+ builder.insert("name", data, 0o100644).unwrap();
+ let tree = builder.write().unwrap();
+ let tree = repo.find_tree(tree).unwrap();
+ let entry = tree.get(0).unwrap();
+ assert_eq!(entry.name(), Some("name"));
+ let blob = entry.to_object(&repo).unwrap();
+ let blob = blob.as_blob().unwrap();
+ assert_eq!(blob.content(), b"data");
+
+ let builder = repo.treebuilder(Some(&tree)).unwrap();
+ assert_eq!(builder.len(), 1);
+ }
+
+ #[test]
+ fn filter() {
+ let (_td, repo) = crate::test::repo_init();
+
+ let mut builder = repo.treebuilder(None).unwrap();
+ let blob = repo.blob(b"data").unwrap();
+ let tree = {
+ let head = repo.head().unwrap().peel(ObjectType::Commit).unwrap();
+ let head = head.as_commit().unwrap();
+ head.tree_id()
+ };
+ builder.insert("blob", blob, 0o100644).unwrap();
+ builder.insert("dir", tree, 0o040000).unwrap();
+ builder.insert("dir2", tree, 0o040000).unwrap();
+
+ builder.filter(|_| true).unwrap();
+ assert_eq!(builder.len(), 3);
+ builder
+ .filter(|e| e.kind().unwrap() != ObjectType::Blob)
+ .unwrap();
+ assert_eq!(builder.len(), 2);
+ builder.filter(|_| false).unwrap();
+ assert_eq!(builder.len(), 0);
+ }
+}