From 3c17c22ef64e78064d8c621b118d7cdb3652fa76 Mon Sep 17 00:00:00 2001 From: Thayne McCombs Date: Thu, 19 Sep 2024 01:09:57 -0600 Subject: [PATCH 1/2] globset: add matches_all method This returns true if all globs in the set match the supplied file. Fixes: #2869 --- crates/globset/src/lib.rs | 80 ++++++++++++++++++++++++++++++++------- 1 file changed, 66 insertions(+), 14 deletions(-) diff --git a/crates/globset/src/lib.rs b/crates/globset/src/lib.rs index b1fc696af..53e60b6be 100644 --- a/crates/globset/src/lib.rs +++ b/crates/globset/src/lib.rs @@ -351,6 +351,27 @@ impl GlobSet { false } + /// Returns true if ALL globs in this set match the path given. + pub fn matches_all>(&self, path: P) -> bool { + self.matches_all_candidate(&Candidate::new(path.as_ref())) + } + + /// Returns ture if all globs in this set match the path given. + /// + /// This takes a Candidate as input, which can be used to amortize the + /// cost of peparing a path for matching. + /// + /// This will return true if the set of globs is empty, as in that case all `0` of + /// the globs will match. + pub fn matches_all_candidate(&self, path: &Candidate<'_>) -> bool { + for strat in &self.strats { + if !strat.is_match(path) { + return false; + } + } + true + } + /// Returns the sequence number of every glob pattern that matches the /// given path. pub fn matches>(&self, path: P) -> Vec { @@ -461,20 +482,33 @@ impl GlobSet { required_exts.0.len(), regexes.literals.len() ); - Ok(GlobSet { - len: pats.len(), - strats: vec![ - GlobSetMatchStrategy::Extension(exts), - GlobSetMatchStrategy::BasenameLiteral(base_lits), - GlobSetMatchStrategy::Literal(lits), - GlobSetMatchStrategy::Suffix(suffixes.suffix()), - GlobSetMatchStrategy::Prefix(prefixes.prefix()), - GlobSetMatchStrategy::RequiredExtension( - required_exts.build()?, - ), - GlobSetMatchStrategy::Regex(regexes.regex_set()?), - ], - }) + let mut strats = Vec::with_capacity(7); + // Only add strategies that are populated + if !exts.0.is_empty() { + strats.push(GlobSetMatchStrategy::Extension(exts)); + } + if !base_lits.0.is_empty() { + strats.push(GlobSetMatchStrategy::BasenameLiteral(base_lits)); + } + if !lits.0.is_empty() { + strats.push(GlobSetMatchStrategy::Literal(lits)); + } + if !suffixes.is_empty() { + strats.push(GlobSetMatchStrategy::Suffix(suffixes.suffix())); + } + if !prefixes.is_empty() { + strats.push(GlobSetMatchStrategy::Prefix(prefixes.prefix())); + } + if !required_exts.0.is_empty() { + strats.push(GlobSetMatchStrategy::RequiredExtension( + required_exts.build()?, + )); + } + if !regexes.is_empty() { + strats.push(GlobSetMatchStrategy::Regex(regexes.regex_set()?)); + } + + Ok(GlobSet { len: pats.len(), strats }) } } @@ -892,6 +926,10 @@ impl MultiStrategyBuilder { patset: Arc::new(Pool::new(create)), }) } + + fn is_empty(&self) -> bool { + self.literals.is_empty() + } } #[derive(Clone, Debug)] @@ -979,6 +1017,7 @@ mod tests { let set = GlobSetBuilder::new().build().unwrap(); assert!(!set.is_match("")); assert!(!set.is_match("a")); + assert!(set.matches_all("a")); } #[test] @@ -1019,4 +1058,17 @@ mod tests { let matches = set.matches("nada"); assert_eq!(0, matches.len()); } + + #[test] + fn matches_all_works() { + let mut builder = GlobSetBuilder::new(); + builder.add(Glob::new("src/*").unwrap()); + builder.add(Glob::new("**/*.rs").unwrap()); + let set = builder.build().unwrap(); + + println!("matches={:?}", set.matches("src/foo.rs")); + assert!(set.matches_all("src/foo.rs")); + assert!(!set.matches_all("src/bar.c")); + assert!(!set.matches_all("test.rs")); + } } From c26831ce39d3cfa36db8f913998ab8b52be4d38c Mon Sep 17 00:00:00 2001 From: Thayne McCombs Date: Fri, 20 Sep 2024 00:49:04 -0600 Subject: [PATCH 2/2] fixup! globset: add matches_all method --- crates/globset/src/lib.rs | 39 +++++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/crates/globset/src/lib.rs b/crates/globset/src/lib.rs index 53e60b6be..a2779df9e 100644 --- a/crates/globset/src/lib.rs +++ b/crates/globset/src/lib.rs @@ -351,18 +351,34 @@ impl GlobSet { false } - /// Returns true if ALL globs in this set match the path given. + /// Returns true if all globs in this set match the path given. + /// + /// This will return true if the set of globs is empty, as in that case all + /// `0` of the globs will match. + /// + /// ``` + /// use globset::{Glob, GlobSetBuilder}; + /// + /// let mut builder = GlobSetBuilder::new(); + /// builder.add(Glob::new("src/*").unwrap()); + /// builder.add(Glob::new("**/*.rs").unwrap()); + /// let set = builder.build().unwrap(); + /// + /// assert!(set.matches_all("src/foo.rs")); + /// assert!(!set.matches_all("src/bar.c")); + /// assert!(!set.matches_all("test.rs")); + /// ``` pub fn matches_all>(&self, path: P) -> bool { self.matches_all_candidate(&Candidate::new(path.as_ref())) } /// Returns ture if all globs in this set match the path given. /// - /// This takes a Candidate as input, which can be used to amortize the - /// cost of peparing a path for matching. + /// This takes a Candidate as input, which can be used to amortize the cost + /// of peparing a path for matching. /// - /// This will return true if the set of globs is empty, as in that case all `0` of - /// the globs will match. + /// This will return true if the set of globs is empty, as in that case all + /// `0` of the globs will match. pub fn matches_all_candidate(&self, path: &Candidate<'_>) -> bool { for strat in &self.strats { if !strat.is_match(path) { @@ -1058,17 +1074,4 @@ mod tests { let matches = set.matches("nada"); assert_eq!(0, matches.len()); } - - #[test] - fn matches_all_works() { - let mut builder = GlobSetBuilder::new(); - builder.add(Glob::new("src/*").unwrap()); - builder.add(Glob::new("**/*.rs").unwrap()); - let set = builder.build().unwrap(); - - println!("matches={:?}", set.matches("src/foo.rs")); - assert!(set.matches_all("src/foo.rs")); - assert!(!set.matches_all("src/bar.c")); - assert!(!set.matches_all("test.rs")); - } }