summaryrefslogtreecommitdiffstats
path: root/src/main.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/main.rs')
-rw-r--r--src/main.rs90
1 files changed, 82 insertions, 8 deletions
diff --git a/src/main.rs b/src/main.rs
index 4d90cae7..da0d8efc 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -30,6 +30,8 @@ use docopt::Docopt;
use grep::Grep;
use walkdir::{WalkDir, WalkDirIterator};
+use ignore::Ignore;
+
macro_rules! errored {
($($tt:tt)*) => {
return Err(From::from(format!($($tt)*)));
@@ -43,7 +45,9 @@ macro_rules! eprintln {
}}
}
+mod gitignore;
mod glob;
+mod ignore;
pub type Result<T> = result::Result<T, Box<Error + Send + Sync>>;
@@ -72,24 +76,40 @@ impl Args {
if self.arg_path.is_empty() {
return errored!("Searching stdin is not currently supported.");
}
+ let mut stdout = io::BufWriter::new(io::stdout());
+ let mut ig = Ignore::new();
for p in &self.arg_path {
- let mut it = WalkDir::new(p).into_iter();
+ let mut it = WalkEventIter::from(WalkDir::new(p));
loop {
- let ent = match it.next() {
+ let ev = match it.next() {
None => break,
+ Some(Ok(ev)) => ev,
Some(Err(err)) => {
eprintln!("{}", err);
continue;
}
- Some(Ok(ent)) => ent,
};
- if is_hidden(&ent) {
- if ent.file_type().is_dir() {
- it.skip_current_dir();
+ match ev {
+ WalkEvent::Exit => {
+ ig.pop();
+ }
+ WalkEvent::Dir(ent) => {
+ try!(ig.push(ent.path()));
+ if is_hidden(&ent) || ig.ignored(ent.path(), true) {
+ // if is_hidden(&ent) {
+ it.it.skip_current_dir();
+ continue;
+ }
+ }
+ WalkEvent::File(ent) => {
+ if is_hidden(&ent) || ig.ignored(ent.path(), false) {
+ // if is_hidden(&ent) {
+ continue;
+ }
+ let _ = writeln!(
+ &mut stdout, "{}", ent.path().display());
}
- continue;
}
- println!("{}", ent.path().display());
}
}
Ok(0)
@@ -108,6 +128,60 @@ impl Args {
}
}
+/// WalkEventIter transforms a WalkDir iterator into an iterator that more
+/// accurately describes the directory tree. Namely, it emits events that are
+/// one of three types: directory, file or "exit." An "exit" event means that
+/// the entire contents of a directory have been enumerated.
+struct WalkEventIter {
+ depth: usize,
+ it: walkdir::Iter,
+ next: Option<result::Result<walkdir::DirEntry, walkdir::Error>>,
+}
+
+#[derive(Debug)]
+enum WalkEvent {
+ Dir(walkdir::DirEntry),
+ File(walkdir::DirEntry),
+ Exit,
+}
+
+impl From<walkdir::WalkDir> for WalkEventIter {
+ fn from(it: walkdir::WalkDir) -> WalkEventIter {
+ WalkEventIter { depth: 0, it: it.into_iter(), next: None }
+ }
+}
+
+impl Iterator for WalkEventIter {
+ type Item = io::Result<WalkEvent>;
+
+ fn next(&mut self) -> Option<io::Result<WalkEvent>> {
+ let dent = self.next.take().or_else(|| self.it.next());
+ let depth = match dent {
+ None => 0,
+ Some(Ok(ref dent)) => dent.depth(),
+ Some(Err(ref err)) => err.depth(),
+ };
+ if depth < self.depth {
+ self.depth -= 1;
+ self.next = dent;
+ return Some(Ok(WalkEvent::Exit));
+ }
+ self.depth = depth;
+ match dent {
+ None => None,
+ Some(Err(err)) => Some(Err(From::from(err))),
+ Some(Ok(dent)) => {
+ if dent.file_type().is_dir() {
+ self.depth += 1;
+ Some(Ok(WalkEvent::Dir(dent)))
+ } else {
+ Some(Ok(WalkEvent::File(dent)))
+ }
+ }
+ }
+ }
+}
+
fn is_hidden(ent: &walkdir::DirEntry) -> bool {
ent.depth() > 0 &&
ent.file_name().to_str().map(|s| s.starts_with(".")).unwrap_or(false)