From a3e129e2e01e879ca1be975cd512b0ba03ddac09 Mon Sep 17 00:00:00 2001 From: Stephan Dilly Date: Mon, 20 Jul 2020 22:49:19 +0200 Subject: [PATCH] add tag_foreach to repo (#594) (#595) * add tag_foreach to repo (#594) * switch to raw bytes for tag name --- src/lib.rs | 1 + src/repo.rs | 21 +++++++++++++++ src/tagforeach.rs | 69 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 91 insertions(+) create mode 100644 src/tagforeach.rs diff --git a/src/lib.rs b/src/lib.rs index 180dd47b18..262486c61e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -679,6 +679,7 @@ mod stash; mod status; mod submodule; mod tag; +mod tagforeach; mod time; mod tree; mod treebuilder; diff --git a/src/repo.rs b/src/repo.rs index c635d6fdb5..0a8fc088c8 100644 --- a/src/repo.rs +++ b/src/repo.rs @@ -14,6 +14,7 @@ use crate::diff::{ use crate::oid_array::OidArray; use crate::stash::{stash_cb, StashApplyOptions, StashCbData}; use crate::string_array::StringArray; +use crate::tagforeach::{tag_foreach_cb, TagForeachCB, TagForeachData}; use crate::util::{self, path_to_repo_path, Binding}; use crate::CherrypickOptions; use crate::RevertOptions; @@ -1731,6 +1732,26 @@ impl Repository { } } + /// iterate over all tags calling `cb` on each. + /// the callback is provided the tag id and name + pub fn tag_foreach(&self, cb: T) -> Result<(), Error> + where + T: FnMut(Oid, &[u8]) -> bool, + { + let mut data = TagForeachData { + cb: Box::new(cb) as TagForeachCB<'_>, + }; + + unsafe { + raw::git_tag_foreach( + self.raw, + Some(tag_foreach_cb), + (&mut data) as *mut _ as *mut _, + ); + } + Ok(()) + } + /// Updates files in the index and the working tree to match the content of /// the commit pointed at by HEAD. pub fn checkout_head(&self, opts: Option<&mut CheckoutBuilder<'_>>) -> Result<(), Error> { diff --git a/src/tagforeach.rs b/src/tagforeach.rs new file mode 100644 index 0000000000..c8018a44cf --- /dev/null +++ b/src/tagforeach.rs @@ -0,0 +1,69 @@ +//! git_tag_foreach support +//! see original: https://libgit2.org/libgit2/#HEAD/group/tag/git_tag_foreach + +use crate::{panic, raw, util::Binding, Oid}; +use libc::{c_char, c_int}; +use raw::git_oid; +use std::ffi::{c_void, CStr}; + +/// boxed callback type +pub(crate) type TagForeachCB<'a> = Box bool + 'a>; + +/// helper type to be able to pass callback to payload +pub(crate) struct TagForeachData<'a> { + /// callback + pub(crate) cb: TagForeachCB<'a>, +} + +/// c callback forwarding to rust callback inside `TagForeachData` +/// see original: https://libgit2.org/libgit2/#HEAD/group/callback/git_tag_foreach_cb +pub(crate) extern "C" fn tag_foreach_cb( + name: *const c_char, + oid: *mut git_oid, + payload: *mut c_void, +) -> c_int { + panic::wrap(|| unsafe { + let id: Oid = Binding::from_raw(oid as *const _); + + let name = CStr::from_ptr(name); + let name = name.to_bytes(); + + let payload = &mut *(payload as *mut TagForeachData<'_>); + let cb = &mut payload.cb; + + let res = cb(id, name); + + if res { + 0 + } else { + -1 + } + }) + .unwrap_or(-1) +} + +#[cfg(test)] +mod tests { + + #[test] + fn smoke() { + let (_td, repo) = crate::test::repo_init(); + let head = repo.head().unwrap(); + let id = head.target().unwrap(); + assert!(repo.find_tag(id).is_err()); + + let obj = repo.find_object(id, None).unwrap(); + let sig = repo.signature().unwrap(); + let tag_id = repo.tag("foo", &obj, &sig, "msg", false).unwrap(); + + let mut tags = Vec::new(); + repo.tag_foreach(|id, name| { + tags.push((id, String::from_utf8(name.into()).unwrap())); + true + }) + .unwrap(); + + assert_eq!(tags[0].0, tag_id); + assert_eq!(tags[0].1, "refs/tags/foo"); + } +}