use libc::{c_int, c_uint, c_void};
use std::ffi::CString;
use std::marker;
use crate::util::Binding;
use crate::{panic, raw, Error, Oid, Repository, Sort};
pub struct Revwalk<'repo> {
raw: *mut raw::git_revwalk,
_marker: marker::PhantomData<&'repo Repository>,
}
pub struct RevwalkWithHideCb<'repo, 'cb, C>
where
C: FnMut(Oid) -> bool,
{
revwalk: Revwalk<'repo>,
_marker: marker::PhantomData<&'cb C>,
}
extern "C" fn revwalk_hide_cb<C>(commit_id: *const raw::git_oid, payload: *mut c_void) -> c_int
where
C: FnMut(Oid) -> bool,
{
panic::wrap(|| unsafe {
let hide_cb = payload as *mut C;
if (*hide_cb)(Oid::from_raw(commit_id)) {
1
} else {
0
}
})
.unwrap_or(-1)
}
impl<'repo, 'cb, C: FnMut(Oid) -> bool> RevwalkWithHideCb<'repo, 'cb, C> {
pub fn into_inner(mut self) -> Result<Revwalk<'repo>, Error> {
self.revwalk.reset()?;
Ok(self.revwalk)
}
}
impl<'repo> Revwalk<'repo> {
pub fn reset(&mut self) -> Result<(), Error> {
unsafe {
try_call!(raw::git_revwalk_reset(self.raw()));
}
Ok(())
}
pub fn set_sorting(&mut self, sort_mode: Sort) -> Result<(), Error> {
unsafe {
try_call!(raw::git_revwalk_sorting(
self.raw(),
sort_mode.bits() as c_uint
));
}
Ok(())
}
pub fn simplify_first_parent(&mut self) -> Result<(), Error> {
unsafe {
try_call!(raw::git_revwalk_simplify_first_parent(self.raw));
}
Ok(())
}
pub fn push(&mut self, oid: Oid) -> Result<(), Error> {
unsafe {
try_call!(raw::git_revwalk_push(self.raw(), oid.raw()));
}
Ok(())
}
pub fn push_head(&mut self) -> Result<(), Error> {
unsafe {
try_call!(raw::git_revwalk_push_head(self.raw()));
}
Ok(())
}
pub fn push_glob(&mut self, glob: &str) -> Result<(), Error> {
let glob = CString::new(glob)?;
unsafe {
try_call!(raw::git_revwalk_push_glob(self.raw, glob));
}
Ok(())
}
pub fn push_range(&mut self, range: &str) -> Result<(), Error> {
let range = CString::new(range)?;
unsafe {
try_call!(raw::git_revwalk_push_range(self.raw, range));
}
Ok(())
}
pub fn push_ref(&mut self, reference: &str) -> Result<(), Error> {
let reference = CString::new(reference)?;
unsafe {
try_call!(raw::git_revwalk_push_ref(self.raw, reference));
}
Ok(())
}
pub fn hide(&mut self, oid: Oid) -> Result<(), Error> {
unsafe {
try_call!(raw::git_revwalk_hide(self.raw(), oid.raw()));
}
Ok(())
}
pub fn with_hide_callback<'cb, C>(
self,
callback: &'cb mut C,
) -> Result<RevwalkWithHideCb<'repo, 'cb, C>, Error>
where
C: FnMut(Oid) -> bool,
{
let r = RevwalkWithHideCb {
revwalk: self,
_marker: marker::PhantomData,
};
unsafe {
raw::git_revwalk_add_hide_cb(
r.revwalk.raw(),
Some(revwalk_hide_cb::<C>),
callback as *mut _ as *mut c_void,
);
};
Ok(r)
}
pub fn hide_head(&mut self) -> Result<(), Error> {
unsafe {
try_call!(raw::git_revwalk_hide_head(self.raw()));
}
Ok(())
}
pub fn hide_glob(&mut self, glob: &str) -> Result<(), Error> {
let glob = CString::new(glob)?;
unsafe {
try_call!(raw::git_revwalk_hide_glob(self.raw, glob));
}
Ok(())
}
pub fn hide_ref(&mut self, reference: &str) -> Result<(), Error> {
let reference = CString::new(reference)?;
unsafe {
try_call!(raw::git_revwalk_hide_ref(self.raw, reference));
}
Ok(())
}
}
impl<'repo> Binding for Revwalk<'repo> {
type Raw = *mut raw::git_revwalk;
unsafe fn from_raw(raw: *mut raw::git_revwalk) -> Revwalk<'repo> {
Revwalk {
raw,
_marker: marker::PhantomData,
}
}
fn raw(&self) -> *mut raw::git_revwalk {
self.raw
}
}
impl<'repo> Drop for Revwalk<'repo> {
fn drop(&mut self) {
unsafe { raw::git_revwalk_free(self.raw) }
}
}
impl<'repo> Iterator for Revwalk<'repo> {
type Item = Result<Oid, Error>;
fn next(&mut self) -> Option<Result<Oid, Error>> {
let mut out: raw::git_oid = raw::git_oid {
id: [0; raw::GIT_OID_RAWSZ],
};
unsafe {
try_call_iter!(raw::git_revwalk_next(&mut out, self.raw()));
Some(Ok(Binding::from_raw(&out as *const _)))
}
}
}
impl<'repo, 'cb, C: FnMut(Oid) -> bool> Iterator for RevwalkWithHideCb<'repo, 'cb, C> {
type Item = Result<Oid, Error>;
fn next(&mut self) -> Option<Result<Oid, Error>> {
let out = self.revwalk.next();
crate::panic::check();
out
}
}
#[cfg(test)]
mod tests {
#[test]
fn smoke() {
let (_td, repo) = crate::test::repo_init();
let head = repo.head().unwrap();
let target = head.target().unwrap();
let mut walk = repo.revwalk().unwrap();
walk.push(target).unwrap();
let oids: Vec<crate::Oid> = walk.by_ref().collect::<Result<Vec<_>, _>>().unwrap();
assert_eq!(oids.len(), 1);
assert_eq!(oids[0], target);
walk.reset().unwrap();
walk.push_head().unwrap();
assert_eq!(walk.by_ref().count(), 1);
walk.reset().unwrap();
walk.push_head().unwrap();
walk.hide_head().unwrap();
assert_eq!(walk.by_ref().count(), 0);
}
#[test]
fn smoke_hide_cb() {
let (_td, repo) = crate::test::repo_init();
let head = repo.head().unwrap();
let target = head.target().unwrap();
let mut walk = repo.revwalk().unwrap();
walk.push(target).unwrap();
let oids: Vec<crate::Oid> = walk.by_ref().collect::<Result<Vec<_>, _>>().unwrap();
assert_eq!(oids.len(), 1);
assert_eq!(oids[0], target);
walk.reset().unwrap();
walk.push_head().unwrap();
assert_eq!(walk.by_ref().count(), 1);
walk.reset().unwrap();
walk.push_head().unwrap();
let mut hide_cb = |oid| oid == target;
let mut walk = walk.with_hide_callback(&mut hide_cb).unwrap();
assert_eq!(walk.by_ref().count(), 0);
let mut walk = walk.into_inner().unwrap();
walk.push_head().unwrap();
assert_eq!(walk.by_ref().count(), 1);
}
}