diff options
Diffstat (limited to 'vendor/git2/src/pathspec.rs')
-rw-r--r-- | vendor/git2/src/pathspec.rs | 368 |
1 files changed, 368 insertions, 0 deletions
diff --git a/vendor/git2/src/pathspec.rs b/vendor/git2/src/pathspec.rs new file mode 100644 index 0000000..48174fc --- /dev/null +++ b/vendor/git2/src/pathspec.rs @@ -0,0 +1,368 @@ +use libc::size_t; +use std::iter::{FusedIterator, IntoIterator}; +use std::marker; +use std::ops::Range; +use std::path::Path; +use std::ptr; + +use crate::util::{path_to_repo_path, Binding}; +use crate::{raw, Diff, DiffDelta, Error, Index, IntoCString, PathspecFlags, Repository, Tree}; + +/// Structure representing a compiled pathspec used for matching against various +/// structures. +pub struct Pathspec { + raw: *mut raw::git_pathspec, +} + +/// List of filenames matching a pathspec. +pub struct PathspecMatchList<'ps> { + raw: *mut raw::git_pathspec_match_list, + _marker: marker::PhantomData<&'ps Pathspec>, +} + +/// Iterator over the matched paths in a pathspec. +pub struct PathspecEntries<'list> { + range: Range<usize>, + list: &'list PathspecMatchList<'list>, +} + +/// Iterator over the matching diff deltas. +pub struct PathspecDiffEntries<'list> { + range: Range<usize>, + list: &'list PathspecMatchList<'list>, +} + +/// Iterator over the failed list of pathspec items that did not match. +pub struct PathspecFailedEntries<'list> { + range: Range<usize>, + list: &'list PathspecMatchList<'list>, +} + +impl Pathspec { + /// Creates a new pathspec from a list of specs to match against. + pub fn new<I, T>(specs: I) -> Result<Pathspec, Error> + where + T: IntoCString, + I: IntoIterator<Item = T>, + { + crate::init(); + let (_a, _b, arr) = crate::util::iter2cstrs_paths(specs)?; + unsafe { + let mut ret = ptr::null_mut(); + try_call!(raw::git_pathspec_new(&mut ret, &arr)); + Ok(Binding::from_raw(ret)) + } + } + + /// Match a pathspec against files in a diff. + /// + /// The list returned contains the list of all matched filenames (unless you + /// pass `PATHSPEC_FAILURES_ONLY` in the flags) and may also contain the + /// list of pathspecs with no match if the `PATHSPEC_FIND_FAILURES` flag is + /// specified. + 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)) + } + } + + /// Match a pathspec against files in a tree. + /// + /// The list returned contains the list of all matched filenames (unless you + /// pass `PATHSPEC_FAILURES_ONLY` in the flags) and may also contain the + /// list of pathspecs with no match if the `PATHSPEC_FIND_FAILURES` flag is + /// specified. + 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)) + } + } + + /// This matches the pathspec against the files in the repository index. + /// + /// The list returned contains the list of all matched filenames (unless you + /// pass `PATHSPEC_FAILURES_ONLY` in the flags) and may also contain the + /// list of pathspecs with no match if the `PATHSPEC_FIND_FAILURES` flag is + /// specified. + 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)) + } + } + + /// Match a pathspec against the working directory of a repository. + /// + /// This matches the pathspec against the current files in the working + /// directory of the repository. It is an error to invoke this on a bare + /// repo. This handles git ignores (i.e. ignored files will not be + /// considered to match the pathspec unless the file is tracked in the + /// index). + /// + /// The list returned contains the list of all matched filenames (unless you + /// pass `PATHSPEC_FAILURES_ONLY` in the flags) and may also contain the + /// list of pathspecs with no match if the `PATHSPEC_FIND_FAILURES` flag is + /// specified. + 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)) + } + } + + /// Try to match a path against a pathspec + /// + /// Unlike most of the other pathspec matching functions, this will not fall + /// back on the native case-sensitivity for your platform. You must + /// explicitly pass flags to control case sensitivity or else this will fall + /// back on being case sensitive. + pub fn matches_path(&self, path: &Path, flags: PathspecFlags) -> bool { + let path = path_to_repo_path(path).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 } + } + 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 } + } + + /// Returns an iterator over the matching filenames in this list. + 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, + } + } + + /// Get a matching filename by position. + /// + /// If this list was generated from a diff, then the return value will + /// always be `None. + pub fn entry(&self, i: usize) -> Option<&[u8]> { + unsafe { + let ptr = raw::git_pathspec_match_list_entry(&*self.raw, i as size_t); + crate::opt_bytes(self, ptr) + } + } + + /// Returns an iterator over the matching diff entries in this list. + 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, + } + } + + /// Get a matching diff delta by position. + /// + /// If the list was not generated from a diff, then the return value will + /// always be `None`. + 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 _) + } + } + + /// Returns an iterator over the non-matching entries in this list. + 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, + } + } + + /// Get an original pathspec string that had no matches. + 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); + crate::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, + _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> FusedIterator for PathspecEntries<'list> {} +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> FusedIterator for PathspecDiffEntries<'list> {} +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> FusedIterator for PathspecFailedEntries<'list> {} +impl<'list> ExactSizeIterator for PathspecFailedEntries<'list> {} + +#[cfg(test)] +mod tests { + use super::Pathspec; + use crate::PathspecFlags; + 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) = crate::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, crate::PathspecFlags::FIND_FAILURES) + .unwrap(); + assert_eq!(list.entries().len(), 1); + assert_eq!(list.entries().next(), Some("a".as_bytes())); + } +} |