use std::ffi::CString;
use std::iter::Range;
use std::kinds::marker;
use std::path::BytesContainer;
use libc::size_t;
use {raw, Error, Tree, PathspecFlags, Index, Repository, DiffDelta};
pub struct Pathspec {
raw: *mut raw::git_pathspec,
}
pub struct PathspecMatchList<'ps> {
raw: *mut raw::git_pathspec_match_list,
marker: marker::ContravariantLifetime<'ps>,
}
pub struct PathspecEntries<'list> {
range: Range<uint>,
list: &'list PathspecMatchList<'list>,
}
pub struct PathspecDiffEntries<'list> {
range: Range<uint>,
list: &'list PathspecMatchList<'list>,
}
pub struct PathspecFailedEntries<'list> {
range: Range<uint>,
list: &'list PathspecMatchList<'list>,
}
impl Pathspec {
pub fn new<I, T>(specs: I) -> Result<Pathspec, Error>
where T: BytesContainer, I: Iterator<Item=T>
{
let (_a, _b, arr) = ::util::iter2cstrs(specs);
let mut ret = 0 as *mut raw::git_pathspec;
unsafe {
try_call!(raw::git_pathspec_new(&mut ret, &arr));
Ok(Pathspec::from_raw(ret))
}
}
pub unsafe fn from_raw(raw: *mut raw::git_pathspec) -> Pathspec {
Pathspec { raw: raw }
}
pub fn match_tree(&self, tree: &Tree, flags: PathspecFlags)
-> Result<PathspecMatchList, Error> {
let mut ret = 0 as *mut raw::git_pathspec_match_list;
unsafe {
try_call!(raw::git_pathspec_match_tree(&mut ret, tree.raw(),
flags.bits(), self.raw));
Ok(PathspecMatchList::from_raw(ret))
}
}
pub fn match_index(&self, index: &Index, flags: PathspecFlags)
-> Result<PathspecMatchList, Error> {
let mut ret = 0 as *mut raw::git_pathspec_match_list;
unsafe {
try_call!(raw::git_pathspec_match_index(&mut ret, index.raw(),
flags.bits(), self.raw));
Ok(PathspecMatchList::from_raw(ret))
}
}
pub fn match_workdir(&self, repo: &Repository, flags: PathspecFlags)
-> Result<PathspecMatchList, Error> {
let mut ret = 0 as *mut raw::git_pathspec_match_list;
unsafe {
try_call!(raw::git_pathspec_match_workdir(&mut ret, repo.raw(),
flags.bits(), self.raw));
Ok(PathspecMatchList::from_raw(ret))
}
}
pub fn matches_path(&self, path: &Path, flags: PathspecFlags) -> bool {
let path = CString::from_slice(path.as_vec());
unsafe {
raw::git_pathspec_matches_path(&*self.raw, flags.bits(),
path.as_ptr()) == 1
}
}
}
#[unsafe_destructor]
impl Drop for Pathspec {
fn drop(&mut self) {
unsafe { raw::git_pathspec_free(self.raw) }
}
}
impl<'ps> PathspecMatchList<'ps> {
pub unsafe fn from_raw(raw: *mut raw::git_pathspec_match_list)
-> PathspecMatchList<'ps> {
PathspecMatchList {
raw: raw,
marker: marker::ContravariantLifetime,
}
}
fn entrycount(&self) -> uint {
unsafe { raw::git_pathspec_match_list_entrycount(&*self.raw) as uint }
}
fn failed_entrycount(&self) -> uint {
unsafe { raw::git_pathspec_match_list_failed_entrycount(&*self.raw) as uint }
}
pub fn entries(&self) -> PathspecEntries {
let n = self.entrycount();
let n = if n > 0 && self.entry(0).is_none() {0} else {n};
PathspecEntries { range: range(0, n), list: self }
}
pub fn entry(&self, i: uint) -> 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: range(0, n), list: self }
}
pub fn diff_entry(&self, i: uint) -> Option<DiffDelta> {
unsafe {
let ptr = raw::git_pathspec_match_list_diff_entry(&*self.raw,
i as size_t);
if ptr.is_null() {
None
} else {
Some(DiffDelta::from_raw(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: range(0, n), list: self }
}
pub fn failed_entry(&self, i: uint) -> Option<&[u8]> {
unsafe {
let ptr = raw::git_pathspec_match_list_failed_entry(&*self.raw,
i as size_t);
::opt_bytes(self, ptr)
}
}
}
#[unsafe_destructor]
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) -> (uint, Option<uint>) { 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) -> (uint, Option<uint>) { 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) -> (uint, Option<uint>) { 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 PATHSPEC_DEFAULT;
use super::Pathspec;
use std::io::File;
#[test]
fn smoke() {
let ps = Pathspec::new(["a"].iter()).unwrap();
assert!(ps.matches_path(&Path::new("a"), PATHSPEC_DEFAULT));
assert!(ps.matches_path(&Path::new("a/b"), PATHSPEC_DEFAULT));
assert!(!ps.matches_path(&Path::new("b"), PATHSPEC_DEFAULT));
assert!(!ps.matches_path(&Path::new("ab/c"), PATHSPEC_DEFAULT));
let (td, repo) = ::test::repo_init();
let list = ps.match_workdir(&repo, PATHSPEC_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, ::PATHSPEC_FIND_FAILURES).unwrap();
assert_eq!(list.entries().len(), 1);
assert_eq!(list.entries().next(), Some(b"a"));
}
}