use std::iter::IntoIterator;
use std::marker;
use std::ops::Range;
use std::path::Path;
use std::ptr;
use libc::size_t;
use {raw, Error, Diff, Tree, PathspecFlags, Index, Repository, DiffDelta, IntoCString};
use util::Binding;
pub struct Pathspec {
raw: *mut raw::git_pathspec,
}
pub struct PathspecMatchList<'ps> {
raw: *mut raw::git_pathspec_match_list,
_marker: marker::PhantomData<&'ps Pathspec>,
}
pub struct PathspecEntries<'list> {
range: Range<usize>,
list: &'list PathspecMatchList<'list>,
}
pub struct PathspecDiffEntries<'list> {
range: Range<usize>,
list: &'list PathspecMatchList<'list>,
}
pub struct PathspecFailedEntries<'list> {
range: Range<usize>,
list: &'list PathspecMatchList<'list>,
}
impl Pathspec {
pub fn new<I, T>(specs: I) -> Result<Pathspec, Error>
where T: IntoCString, I: IntoIterator<Item=T> {
let (_a, _b, arr) = try!(::util::iter2cstrs(specs));
unsafe {
let mut ret = ptr::null_mut();
try_call!(raw::git_pathspec_new(&mut ret, &arr));
Ok(Binding::from_raw(ret))
}
}
pub fn match_diff(&self, diff: &Diff, flags: PathspecFlags)
-> Result<PathspecMatchList, Error> {
let mut ret = ptr::null_mut();
unsafe {
try_call!(raw::git_pathspec_match_diff(&mut ret, diff.raw(),
flags.bits(), self.raw));
Ok(Binding::from_raw(ret))
}
}
pub fn match_tree(&self, tree: &Tree, flags: PathspecFlags)
-> Result<PathspecMatchList, Error> {
let mut ret = ptr::null_mut();
unsafe {
try_call!(raw::git_pathspec_match_tree(&mut ret, tree.raw(),
flags.bits(), self.raw));
Ok(Binding::from_raw(ret))
}
}
pub fn match_index(&self, index: &Index, flags: PathspecFlags)
-> Result<PathspecMatchList, Error> {
let mut ret = ptr::null_mut();
unsafe {
try_call!(raw::git_pathspec_match_index(&mut ret, index.raw(),
flags.bits(), self.raw));
Ok(Binding::from_raw(ret))
}
}
pub fn match_workdir(&self, repo: &Repository, flags: PathspecFlags)
-> Result<PathspecMatchList, Error> {
let mut ret = ptr::null_mut();
unsafe {
try_call!(raw::git_pathspec_match_workdir(&mut ret, repo.raw(),
flags.bits(), self.raw));
Ok(Binding::from_raw(ret))
}
}
pub fn matches_path(&self, path: &Path, flags: PathspecFlags) -> bool {
let path = path.into_c_string().unwrap();
unsafe {
raw::git_pathspec_matches_path(&*self.raw, flags.bits(),
path.as_ptr()) == 1
}
}
}
impl Binding for Pathspec {
type Raw = *mut raw::git_pathspec;
unsafe fn from_raw(raw: *mut raw::git_pathspec) -> Pathspec {
Pathspec { raw: raw }
}
fn raw(&self) -> *mut raw::git_pathspec { self.raw }
}
impl Drop for Pathspec {
fn drop(&mut self) {
unsafe { raw::git_pathspec_free(self.raw) }
}
}
impl<'ps> PathspecMatchList<'ps> {
fn entrycount(&self) -> usize {
unsafe { raw::git_pathspec_match_list_entrycount(&*self.raw) as usize }
}
fn failed_entrycount(&self) -> usize {
unsafe { raw::git_pathspec_match_list_failed_entrycount(&*self.raw) as usize }
}
pub fn entries(&self) -> PathspecEntries {
let n = self.entrycount();
let n = if n > 0 && self.entry(0).is_none() {0} else {n};
PathspecEntries { range: 0..n, list: self }
}
pub fn entry(&self, i: usize) -> Option<&[u8]> {
unsafe {
let ptr = raw::git_pathspec_match_list_entry(&*self.raw, i as size_t);
::opt_bytes(self, ptr)
}
}
pub fn diff_entries(&self) -> PathspecDiffEntries {
let n = self.entrycount();
let n = if n > 0 && self.diff_entry(0).is_none() {0} else {n};
PathspecDiffEntries { range: 0..n, list: self }
}
pub fn diff_entry(&self, i: usize) -> Option<DiffDelta> {
unsafe {
let ptr = raw::git_pathspec_match_list_diff_entry(&*self.raw,
i as size_t);
Binding::from_raw_opt(ptr as *mut _)
}
}
pub fn failed_entries(&self) -> PathspecFailedEntries {
let n = self.failed_entrycount();
let n = if n > 0 && self.failed_entry(0).is_none() {0} else {n};
PathspecFailedEntries { range: 0..n, list: self }
}
pub fn failed_entry(&self, i: usize) -> Option<&[u8]> {
unsafe {
let ptr = raw::git_pathspec_match_list_failed_entry(&*self.raw,
i as size_t);
::opt_bytes(self, ptr)
}
}
}
impl<'ps> Binding for PathspecMatchList<'ps> {
type Raw = *mut raw::git_pathspec_match_list;
unsafe fn from_raw(raw: *mut raw::git_pathspec_match_list)
-> PathspecMatchList<'ps> {
PathspecMatchList { raw: raw, _marker: marker::PhantomData }
}
fn raw(&self) -> *mut raw::git_pathspec_match_list { self.raw }
}
impl<'ps> Drop for PathspecMatchList<'ps> {
fn drop(&mut self) {
unsafe { raw::git_pathspec_match_list_free(self.raw) }
}
}
impl<'list> Iterator for PathspecEntries<'list> {
type Item = &'list [u8];
fn next(&mut self) -> Option<&'list [u8]> {
self.range.next().and_then(|i| self.list.entry(i))
}
fn size_hint(&self) -> (usize, Option<usize>) { self.range.size_hint() }
}
impl<'list> DoubleEndedIterator for PathspecEntries<'list> {
fn next_back(&mut self) -> Option<&'list [u8]> {
self.range.next_back().and_then(|i| self.list.entry(i))
}
}
impl<'list> ExactSizeIterator for PathspecEntries<'list> {}
impl<'list> Iterator for PathspecDiffEntries<'list> {
type Item = DiffDelta<'list>;
fn next(&mut self) -> Option<DiffDelta<'list>> {
self.range.next().and_then(|i| self.list.diff_entry(i))
}
fn size_hint(&self) -> (usize, Option<usize>) { self.range.size_hint() }
}
impl<'list> DoubleEndedIterator for PathspecDiffEntries<'list> {
fn next_back(&mut self) -> Option<DiffDelta<'list>> {
self.range.next_back().and_then(|i| self.list.diff_entry(i))
}
}
impl<'list> ExactSizeIterator for PathspecDiffEntries<'list> {}
impl<'list> Iterator for PathspecFailedEntries<'list> {
type Item = &'list [u8];
fn next(&mut self) -> Option<&'list [u8]> {
self.range.next().and_then(|i| self.list.failed_entry(i))
}
fn size_hint(&self) -> (usize, Option<usize>) { self.range.size_hint() }
}
impl<'list> DoubleEndedIterator for PathspecFailedEntries<'list> {
fn next_back(&mut self) -> Option<&'list [u8]> {
self.range.next_back().and_then(|i| self.list.failed_entry(i))
}
}
impl<'list> ExactSizeIterator for PathspecFailedEntries<'list> {}
#[cfg(test)]
mod tests {
use PathspecFlags;
use super::Pathspec;
use std::fs::File;
use std::path::Path;
#[test]
fn smoke() {
let ps = Pathspec::new(["a"].iter()).unwrap();
assert!(ps.matches_path(Path::new("a"), PathspecFlags::DEFAULT));
assert!(ps.matches_path(Path::new("a/b"), PathspecFlags::DEFAULT));
assert!(!ps.matches_path(Path::new("b"), PathspecFlags::DEFAULT));
assert!(!ps.matches_path(Path::new("ab/c"), PathspecFlags::DEFAULT));
let (td, repo) = ::test::repo_init();
let list = ps.match_workdir(&repo, PathspecFlags::DEFAULT).unwrap();
assert_eq!(list.entries().len(), 0);
assert_eq!(list.diff_entries().len(), 0);
assert_eq!(list.failed_entries().len(), 0);
File::create(&td.path().join("a")).unwrap();
let list = ps.match_workdir(&repo, ::PathspecFlags::FIND_FAILURES).unwrap();
assert_eq!(list.entries().len(), 1);
assert_eq!(list.entries().next(), Some("a".as_bytes()));
}
}