From b633bdfb8da6177154fae4665eb71581c70c8064 Mon Sep 17 00:00:00 2001 From: Gilad Naaman Date: Mon, 29 Jan 2018 19:37:15 +0200 Subject: [PATCH 1/2] Added GlobSet::iter_at to iterate over files. --- Cargo.lock | 1 + globset/Cargo.toml | 1 + globset/src/lib.rs | 51 +++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 52 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index be583d37e..f6c964687 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -109,6 +109,7 @@ dependencies = [ "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", "memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "regex 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", + "walkdir 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] diff --git a/globset/Cargo.toml b/globset/Cargo.toml index 96d47e3fd..65b5e9627 100644 --- a/globset/Cargo.toml +++ b/globset/Cargo.toml @@ -24,6 +24,7 @@ fnv = "1.0" log = "0.3" memchr = "2" regex = "0.2.1" +walkdir = "2.0" [dev-dependencies] glob = "0.2" diff --git a/globset/src/lib.rs b/globset/src/lib.rs index dd6922e6d..f16464c3e 100644 --- a/globset/src/lib.rs +++ b/globset/src/lib.rs @@ -104,6 +104,7 @@ extern crate fnv; extern crate log; extern crate memchr; extern crate regex; +extern crate walkdir; use std::borrow::Cow; use std::collections::{BTreeMap, HashMap}; @@ -111,7 +112,7 @@ use std::error::Error as StdError; use std::ffi::{OsStr, OsString}; use std::fmt; use std::hash; -use std::path::Path; +use std::path::{Path, PathBuf}; use std::str; use aho_corasick::{Automaton, AcAutomaton, FullAcAutomaton}; @@ -417,6 +418,44 @@ impl GlobSet { ], }) } + + /// Iterate files and directories matching this globset at the given directory. + pub fn iter_at>(&self, dir: P) -> GlobWalker { + GlobWalker { + glob: self, + base: dir.as_ref().into(), + walker: walkdir::WalkDir::new(dir).into_iter() + } + } +} + +/// An iterator for recursively yielding glob matches. +/// +/// The order of elements yielded by this iterator is unspecified. +pub struct GlobWalker<'a> { + glob: &'a GlobSet, + base: PathBuf, + walker: walkdir::IntoIter, +} + +impl<'a> Iterator for GlobWalker<'a> { + type Item = walkdir::DirEntry; + + fn next(&mut self) -> Option { + for entry in &mut self.walker { + if let Ok(entry) = entry { + // Strip the common base directory so that the matcher will be + // able to recognize the file name. + // `unwrap` here is safe, since walkdir returns the files with relation + // to the given base-dir. + if self.glob.is_match(entry.path().strip_prefix(&*self.base).unwrap()) { + return Some(entry) + } + } + } + + None + } } /// GlobSetBuilder builds a group of patterns that can be used to @@ -832,4 +871,14 @@ mod tests { assert!(!set.is_match("")); assert!(!set.is_match("a")); } + + #[test] + fn gilnaa() { + let mut builder = GlobSetBuilder::new(); + builder.add(Glob::new("src/**/*.rs").unwrap()); + builder.add(Glob::new("*.c").unwrap()); + builder.add(Glob::new("**/lib.rs").unwrap()); + let set = builder.build().unwrap(); + set.iter_at("/home/gilnaa/florp").for_each(|x| println!("== {:?}", x)); + } } From 60a3f4e082d80f4b5ebad381ea81bfd50b9e8570 Mon Sep 17 00:00:00 2001 From: Gilad Naaman Date: Mon, 29 Jan 2018 23:19:08 +0200 Subject: [PATCH 2/2] Added a globwalk test. --- Cargo.lock | 1 + globset/Cargo.toml | 1 + globset/src/lib.rs | 58 ++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 58 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f6c964687..36fa84273 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -109,6 +109,7 @@ dependencies = [ "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", "memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "regex 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", + "tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "walkdir 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", ] diff --git a/globset/Cargo.toml b/globset/Cargo.toml index 65b5e9627..ab3feb07e 100644 --- a/globset/Cargo.toml +++ b/globset/Cargo.toml @@ -28,6 +28,7 @@ walkdir = "2.0" [dev-dependencies] glob = "0.2" +tempdir = "0.3" [features] simd-accel = ["regex/simd-accel"] diff --git a/globset/src/lib.rs b/globset/src/lib.rs index f16464c3e..b4d40a67f 100644 --- a/globset/src/lib.rs +++ b/globset/src/lib.rs @@ -106,6 +106,9 @@ extern crate memchr; extern crate regex; extern crate walkdir; +#[cfg(test)] +extern crate tempdir; + use std::borrow::Cow; use std::collections::{BTreeMap, HashMap}; use std::error::Error as StdError; @@ -843,6 +846,8 @@ impl RequiredExtensionStrategyBuilder { mod tests { use super::GlobSetBuilder; use glob::Glob; + use ::tempdir::TempDir; + use ::std::fs::{File, create_dir_all}; #[test] fn set_works() { @@ -872,13 +877,62 @@ mod tests { assert!(!set.is_match("a")); } + fn touch(dir: &TempDir, names: &[&str]) { + for name in names { + File::create(dir.path().join(name)).expect("Failed to create a test file"); + } + } + #[test] - fn gilnaa() { + fn do_the_globwalk() { + let dir = TempDir::new("globset_walkdir").expect("Failed to create temporary folder"); + let dir_path = dir.path(); + create_dir_all(dir_path.join("src/some_mod")).expect(""); + create_dir_all(dir_path.join("tests")).expect(""); + create_dir_all(dir_path.join("contrib")).expect(""); + + touch(&dir, &[ + "a.rs", + "b.rs", + "avocado.rs", + "lib.c", + "src/hello.rs", + "src/world.rs", + "src/some_mod/unexpected.rs", + "src/cruel.txt", + "contrib/README.md", + "contrib/README.rst", + "contrib/lib.rs", + ][..]); + + let mut builder = GlobSetBuilder::new(); builder.add(Glob::new("src/**/*.rs").unwrap()); builder.add(Glob::new("*.c").unwrap()); builder.add(Glob::new("**/lib.rs").unwrap()); + builder.add(Glob::new("**/*.{md,rst}").unwrap()); let set = builder.build().unwrap(); - set.iter_at("/home/gilnaa/florp").for_each(|x| println!("== {:?}", x)); + + let mut expected = vec!["src/some_mod/unexpected.rs", + "src/world.rs", + "src/hello.rs", + "lib.c", + "contrib/lib.rs", + "contrib/README.md", + "contrib/README.rst"]; + + for matched_file in set.iter_at(dir_path) { + let path = matched_file.path().strip_prefix(dir_path).unwrap().to_str().unwrap(); + + let del_idx = if let Some(idx) = expected.iter().position(|n| &path == n) { + idx + } else { + panic!("Iterated file is unexpected: {}", path); + }; + expected.remove(del_idx); + } + + let empty: &[&str] = &[][..]; + assert_eq!(expected, empty); } }