From 4993d29a16b26b016b498765cc5636e73b479367 Mon Sep 17 00:00:00 2001 From: piegames Date: Fri, 12 Nov 2021 18:39:03 +0100 Subject: globset: add 'escape' routine Fixes #2060, Closes #2061 --- crates/globset/src/glob.rs | 20 ++++++++++++++------ crates/globset/src/lib.rs | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 6 deletions(-) diff --git a/crates/globset/src/glob.rs b/crates/globset/src/glob.rs index e025ddf9..cda39cab 100644 --- a/crates/globset/src/glob.rs +++ b/crates/globset/src/glob.rs @@ -639,9 +639,9 @@ impl<'a> GlobBuilder<'a> { } /// Toggle whether an empty pattern in a list of alternates is accepted. - /// + /// /// For example, if this is set then the glob `foo{,.txt}` will match both `foo` and `foo.txt`. - /// + /// /// By default this is false. pub fn empty_alternates(&mut self, yes: bool) -> &mut GlobBuilder<'a> { self.opts.empty_alternates = yes; @@ -1222,12 +1222,20 @@ mod tests { Options { casei: Some(true), litsep: None, bsesc: None, ealtre: None }; const SLASHLIT: Options = Options { casei: None, litsep: Some(true), bsesc: None, ealtre: None }; - const NOBSESC: Options = - Options { casei: None, litsep: None, bsesc: Some(false), ealtre: None }; + const NOBSESC: Options = Options { + casei: None, + litsep: None, + bsesc: Some(false), + ealtre: None, + }; const BSESC: Options = Options { casei: None, litsep: None, bsesc: Some(true), ealtre: None }; - const EALTRE: Options = - Options { casei: None, litsep: None, bsesc: Some(true), ealtre: Some(true) }; + const EALTRE: Options = Options { + casei: None, + litsep: None, + bsesc: Some(true), + ealtre: Some(true), + }; toregex!(re_casei, "a", "(?i)^a$", &CASEI); diff --git a/crates/globset/src/lib.rs b/crates/globset/src/lib.rs index dca0f7e0..7a357489 100644 --- a/crates/globset/src/lib.rs +++ b/crates/globset/src/lib.rs @@ -880,6 +880,29 @@ impl RequiredExtensionStrategyBuilder { } } +/// Escape meta-characters within the given glob pattern. +/// +/// The escaping works by surrounding meta-characters with brackets. For +/// example, `*` becomes `[*]`. +pub fn escape(s: &str) -> String { + let mut escaped = String::with_capacity(s.len()); + for c in s.chars() { + match c { + // note that ! does not need escaping because it is only special + // inside brackets + '?' | '*' | '[' | ']' => { + escaped.push('['); + escaped.push(c); + escaped.push(']'); + } + c => { + escaped.push(c); + } + } + } + escaped +} + #[cfg(test)] mod tests { use super::{GlobSet, GlobSetBuilder}; @@ -919,4 +942,16 @@ mod tests { assert!(!set.is_match("")); assert!(!set.is_match("a")); } + + #[test] + fn escape() { + use super::escape; + assert_eq!("foo", escape("foo")); + assert_eq!("foo[*]", escape("foo*")); + assert_eq!("[[][]]", escape("[]")); + assert_eq!("[*][?]", escape("*?")); + assert_eq!("src/[*][*]/[*].rs", escape("src/**/*.rs")); + assert_eq!("bar[[]ab[]]baz", escape("bar[ab]baz")); + assert_eq!("bar[[]!![]]!baz", escape("bar[!!]!baz")); + } } -- cgit v1.2.3