summaryrefslogtreecommitdiffstats
path: root/ignore/src/lib.rs
diff options
context:
space:
mode:
Diffstat (limited to 'ignore/src/lib.rs')
-rw-r--r--ignore/src/lib.rs300
1 files changed, 300 insertions, 0 deletions
diff --git a/ignore/src/lib.rs b/ignore/src/lib.rs
new file mode 100644
index 00000000..a3aa0c8f
--- /dev/null
+++ b/ignore/src/lib.rs
@@ -0,0 +1,300 @@
+/*!
+The ignore crate provides a fast recursive directory iterator that respects
+various filters such as globs, file types and `.gitignore` files. The precise
+matching rules and precedence is explained in the documentation for
+`WalkBuilder`.
+
+Secondarily, this crate exposes gitignore and file type matchers for use cases
+that demand more fine-grained control.
+
+# Example
+
+This example shows the most basic usage of this crate. This code will
+recursively traverse the current directory while automatically filtering out
+files and directories according to ignore globs found in files like
+`.ignore` and `.gitignore`:
+
+
+```rust,no_run
+use ignore::Walk;
+
+for result in Walk::new("./") {
+ // Each item yielded by the iterator is either a directory entry or an
+ // error, so either print the path or the error.
+ match result {
+ Ok(entry) => println!("{}", entry.path().display()),
+ Err(err) => println!("ERROR: {}", err),
+ }
+}
+```
+
+# Example: advanced
+
+By default, the recursive directory iterator will ignore hidden files and
+directories. This can be disabled by building the iterator with `WalkBuilder`:
+
+```rust,no_run
+use ignore::WalkBuilder;
+
+for result in WalkBuilder::new("./").hidden(false).build() {
+ println!("{:?}", result);
+}
+```
+
+See the documentation for `WalkBuilder` for many other options.
+*/
+
+extern crate globset;
+#[macro_use]
+extern crate lazy_static;
+#[macro_use]
+extern crate log;
+extern crate memchr;
+extern crate regex;
+#[cfg(test)]
+extern crate tempdir;
+extern crate thread_local;
+extern crate walkdir;
+
+use std::error;
+use std::fmt;
+use std::io;
+use std::path::{Path, PathBuf};
+
+pub use walk::{DirEntry, Walk, WalkBuilder};
+
+mod dir;
+pub mod gitignore;
+mod pathutil;
+pub mod overrides;
+pub mod types;
+mod walk;
+
+/// Represents an error that can occur when parsing a gitignore file.
+#[derive(Debug)]
+pub enum Error {
+ /// A collection of "soft" errors. These occur when adding an ignore
+ /// file partially succeeded.
+ Partial(Vec<Error>),
+ /// An error associated with a specific line number.
+ WithLineNumber { line: u64, err: Box<Error> },
+ /// An error associated with a particular file path.
+ WithPath { path: PathBuf, err: Box<Error> },
+ /// An error that occurs when doing I/O, such as reading an ignore file.
+ Io(io::Error),
+ /// An error that occurs when trying to parse a glob.
+ Glob(String),
+ /// A type selection for a file type that is not defined.
+ UnrecognizedFileType(String),
+ /// A user specified file type definition could not be parsed.
+ InvalidDefinition,
+}
+
+impl Error {
+ /// Returns true if this is a partial error.
+ ///
+ /// A partial error occurs when only some operations failed while others
+ /// may have succeeded. For example, an ignore file may contain an invalid
+ /// glob among otherwise valid globs.
+ pub fn is_partial(&self) -> bool {
+ match *self {
+ Error::Partial(_) => true,
+ Error::WithLineNumber { ref err, .. } => err.is_partial(),
+ Error::WithPath { ref err, .. } => err.is_partial(),
+ _ => false,
+ }
+ }
+
+ /// Returns true if this error is exclusively an I/O error.
+ pub fn is_io(&self) -> bool {
+ match *self {
+ Error::Partial(ref errs) => errs.len() == 1 && errs[0].is_io(),
+ Error::WithLineNumber { ref err, .. } => err.is_io(),
+ Error::WithPath { ref err, .. } => err.is_io(),
+ Error::Io(_) => true,
+ Error::Glob(_) => false,
+ Error::UnrecognizedFileType(_) => false,
+ Error::InvalidDefinition => false,
+ }
+ }
+
+ /// Turn an error into a tagged error with the given file path.
+ fn with_path<P: AsRef<Path>>(self, path: P) -> Error {
+ Error::WithPath {
+ path: path.as_ref().to_path_buf(),
+ err: Box::new(self),
+ }
+ }
+
+ /// Turn an error into a tagged error with the given file path and line
+ /// number. If path is empty, then it is omitted from the error.
+ fn tagged<P: AsRef<Path>>(self, path: P, lineno: u64) -> Error {
+ let errline = Error::WithLineNumber {
+ line: lineno,
+ err: Box::new(self),
+ };
+ if path.as_ref().as_os_str().is_empty() {
+ return errline;
+ }
+ errline.with_path(path)
+ }
+}
+
+impl error::Error for Error {
+ fn description(&self) -> &str {
+ match *self {
+ Error::Partial(_) => "partial error",
+ Error::WithLineNumber { ref err, .. } => err.description(),
+ Error::WithPath { ref err, .. } => err.description(),
+ Error::Io(ref err) => err.description(),
+ Error::Glob(ref msg) => msg,
+ Error::UnrecognizedFileType(_) => "unrecognized file type",
+ Error::InvalidDefinition => "invalid definition",
+ }
+ }
+}
+
+impl fmt::Display for Error {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match *self {
+ Error::Partial(ref errs) => {
+ let msgs: Vec<String> =
+ errs.iter().map(|err| err.to_string()).collect();
+ write!(f, "{}", msgs.join("\n"))
+ }
+ Error::WithLineNumber { line, ref err } => {
+ write!(f, "line {}: {}", line, err)
+ }
+ Error::WithPath { ref path, ref err } => {
+ write!(f, "{}: {}", path.display(), err)
+ }
+ Error::Io(ref err) => err.fmt(f),
+ Error::Glob(ref msg) => write!(f, "{}", msg),
+ Error::UnrecognizedFileType(ref ty) => {
+ write!(f, "unrecognized file type: {}", ty)
+ }
+ Error::InvalidDefinition => {
+ write!(f, "invalid definition (format is type:glob, e.g., \
+ html:*.html)")
+ }
+ }
+ }
+}
+
+impl From<io::Error> for Error {
+ fn from(err: io::Error) -> Error {
+ Error::Io(err)
+ }
+}
+
+#[derive(Debug, Default)]
+struct PartialErrorBuilder(Vec<Error>);
+
+impl PartialErrorBuilder {
+ fn push(&mut self, err: Error) {
+ self.0.push(err);
+ }
+
+ fn push_ignore_io(&mut self, err: Error) {
+ if !err.is_io() {
+ self.push(err);
+ }
+ }
+
+ fn maybe_push(&mut self, err: Option<Error>) {
+ if let Some(err) = err {
+ self.push(err);
+ }
+ }
+
+ fn maybe_push_ignore_io(&mut self, err: Option<Error>) {
+ if let Some(err) = err {
+ self.push_ignore_io(err);
+ }
+ }
+
+ fn into_error_option(mut self) -> Option<Error> {
+ if self.0.is_empty() {
+ None
+ } else if self.0.len() == 1 {
+ Some(self.0.pop().unwrap())
+ } else {
+ Some(Error::Partial(self.0))
+ }
+ }
+}
+
+/// The result of a glob match.
+///
+/// The type parameter `T` typically refers to a type that provides more
+/// information about a particular match. For example, it might identify
+/// the specific gitignore file and the specific glob pattern that caused
+/// the match.
+#[derive(Clone, Debug)]
+pub enum Match<T> {
+ /// The path didn't match any glob.
+ None,
+ /// The highest precedent glob matched indicates the path should be
+ /// ignored.
+ Ignore(T),
+ /// The highest precedent glob matched indicates the path should be
+ /// whitelisted.
+ Whitelist(T),
+}
+
+impl<T> Match<T> {
+ /// Returns true if the match result didn't match any globs.
+ pub fn is_none(&self) -> bool {
+ match *self {
+ Match::None => true,
+ Match::Ignore(_) | Match::Whitelist(_) => false,
+ }
+ }
+
+ /// Returns true if the match result implies the path should be ignored.
+ pub fn is_ignore(&self) -> bool {
+ match *self {
+ Match::Ignore(_) => true,
+ Match::None | Match::Whitelist(_) => false,
+ }
+ }
+
+ /// Returns true if the match result implies the path should be
+ /// whitelisted.
+ pub fn is_whitelist(&self) -> bool {
+ match *self {
+ Match::Whitelist(_) => true,
+ Match::None | Match::Ignore(_) => false,
+ }
+ }
+
+ /// Inverts the match so that `Ignore` becomes `Whitelist` and
+ /// `Whitelist` becomes `Ignore`. A non-match remains the same.
+ pub fn invert(self) -> Match<T> {
+ match self {
+ Match::None => Match::None,
+ Match::Ignore(t) => Match::Whitelist(t),
+ Match::Whitelist(t) => Match::Ignore(t),
+ }
+ }
+
+ /// Return the value inside this match if it exists.
+ pub fn inner(&self) -> Option<&T> {
+ match *self {
+ Match::None => None,
+ Match::Ignore(ref t) => Some(t),
+ Match::Whitelist(ref t) => Some(t),
+ }
+ }
+
+ /// Apply the given function to the value inside this match.
+ ///
+ /// If the match has no value, then return the match unchanged.
+ pub fn map<U, F: FnOnce(T) -> U>(self, f: F) -> Match<U> {
+ match self {
+ Match::None => Match::None,
+ Match::Ignore(t) => Match::Ignore(f(t)),
+ Match::Whitelist(t) => Match::Whitelist(f(t)),
+ }
+ }
+}