glob.rs (ripgrep-12.1.1) | : | glob.rs (ripgrep-13.0.0) | ||
---|---|---|---|---|
use std::fmt; | use std::fmt; | |||
use std::hash; | use std::hash; | |||
use std::iter; | use std::iter; | |||
use std::ops::{Deref, DerefMut}; | use std::ops::{Deref, DerefMut}; | |||
use std::path::{is_separator, Path}; | use std::path::{is_separator, Path}; | |||
use std::str; | use std::str; | |||
use regex; | use regex; | |||
use regex::bytes::Regex; | use regex::bytes::Regex; | |||
use {new_regex, Candidate, Error, ErrorKind}; | use crate::{new_regex, Candidate, Error, ErrorKind}; | |||
/// Describes a matching strategy for a particular pattern. | /// Describes a matching strategy for a particular pattern. | |||
/// | /// | |||
/// This provides a way to more quickly determine whether a pattern matches | /// This provides a way to more quickly determine whether a pattern matches | |||
/// a particular file path in a way that scales with a large number of | /// a particular file path in a way that scales with a large number of | |||
/// patterns. For example, if many patterns are of the form `*.ext`, then it's | /// patterns. For example, if many patterns are of the form `*.ext`, then it's | |||
/// possible to test whether any of those patterns matches by looking up a | /// possible to test whether any of those patterns matches by looking up a | |||
/// file path's extension in a hash table. | /// file path's extension in a hash table. | |||
#[derive(Clone, Debug, Eq, PartialEq)] | #[derive(Clone, Debug, Eq, PartialEq)] | |||
pub enum MatchStrategy { | pub enum MatchStrategy { | |||
skipping to change at line 101 | skipping to change at line 101 | |||
} | } | |||
impl hash::Hash for Glob { | impl hash::Hash for Glob { | |||
fn hash<H: hash::Hasher>(&self, state: &mut H) { | fn hash<H: hash::Hasher>(&self, state: &mut H) { | |||
self.glob.hash(state); | self.glob.hash(state); | |||
self.opts.hash(state); | self.opts.hash(state); | |||
} | } | |||
} | } | |||
impl fmt::Display for Glob { | impl fmt::Display for Glob { | |||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |||
self.glob.fmt(f) | self.glob.fmt(f) | |||
} | } | |||
} | } | |||
impl str::FromStr for Glob { | impl str::FromStr for Glob { | |||
type Err = Error; | type Err = Error; | |||
fn from_str(glob: &str) -> Result<Self, Self::Err> { | fn from_str(glob: &str) -> Result<Self, Self::Err> { | |||
Self::new(glob) | Self::new(glob) | |||
} | } | |||
skipping to change at line 130 | skipping to change at line 130 | |||
re: Regex, | re: Regex, | |||
} | } | |||
impl GlobMatcher { | impl GlobMatcher { | |||
/// Tests whether the given path matches this pattern or not. | /// Tests whether the given path matches this pattern or not. | |||
pub fn is_match<P: AsRef<Path>>(&self, path: P) -> bool { | pub fn is_match<P: AsRef<Path>>(&self, path: P) -> bool { | |||
self.is_match_candidate(&Candidate::new(path.as_ref())) | self.is_match_candidate(&Candidate::new(path.as_ref())) | |||
} | } | |||
/// Tests whether the given path matches this pattern or not. | /// Tests whether the given path matches this pattern or not. | |||
pub fn is_match_candidate(&self, path: &Candidate) -> bool { | pub fn is_match_candidate(&self, path: &Candidate<'_>) -> bool { | |||
self.re.is_match(&path.path) | self.re.is_match(&path.path) | |||
} | } | |||
/// Returns the `Glob` used to compile this matcher. | /// Returns the `Glob` used to compile this matcher. | |||
pub fn glob(&self) -> &Glob { | pub fn glob(&self) -> &Glob { | |||
&self.pat | &self.pat | |||
} | } | |||
} | } | |||
/// A strategic matcher for a single pattern. | /// A strategic matcher for a single pattern. | |||
skipping to change at line 160 | skipping to change at line 160 | |||
} | } | |||
#[cfg(test)] | #[cfg(test)] | |||
impl GlobStrategic { | impl GlobStrategic { | |||
/// Tests whether the given path matches this pattern or not. | /// Tests whether the given path matches this pattern or not. | |||
fn is_match<P: AsRef<Path>>(&self, path: P) -> bool { | fn is_match<P: AsRef<Path>>(&self, path: P) -> bool { | |||
self.is_match_candidate(&Candidate::new(path.as_ref())) | self.is_match_candidate(&Candidate::new(path.as_ref())) | |||
} | } | |||
/// Tests whether the given path matches this pattern or not. | /// Tests whether the given path matches this pattern or not. | |||
fn is_match_candidate(&self, candidate: &Candidate) -> bool { | fn is_match_candidate(&self, candidate: &Candidate<'_>) -> bool { | |||
let byte_path = &*candidate.path; | let byte_path = &*candidate.path; | |||
match self.strategy { | match self.strategy { | |||
MatchStrategy::Literal(ref lit) => lit.as_bytes() == byte_path, | MatchStrategy::Literal(ref lit) => lit.as_bytes() == byte_path, | |||
MatchStrategy::BasenameLiteral(ref lit) => { | MatchStrategy::BasenameLiteral(ref lit) => { | |||
lit.as_bytes() == &*candidate.basename | lit.as_bytes() == &*candidate.basename | |||
} | } | |||
MatchStrategy::Extension(ref ext) => { | MatchStrategy::Extension(ref ext) => { | |||
ext.as_bytes() == &*candidate.ext | ext.as_bytes() == &*candidate.ext | |||
} | } | |||
skipping to change at line 370 | skipping to change at line 370 | |||
_ => return None, | _ => return None, | |||
} | } | |||
} | } | |||
if lit.is_empty() { | if lit.is_empty() { | |||
None | None | |||
} else { | } else { | |||
Some(lit) | Some(lit) | |||
} | } | |||
} | } | |||
/// This is like `ext`, but returns an extension even if it isn't sufficent | /// This is like `ext`, but returns an extension even if it isn't sufficient | |||
/// to imply a match. Namely, if an extension is returned, then it is | /// to imply a match. Namely, if an extension is returned, then it is | |||
/// necessary but not sufficient for a match. | /// necessary but not sufficient for a match. | |||
fn required_ext(&self) -> Option<String> { | fn required_ext(&self) -> Option<String> { | |||
if self.opts.case_insensitive { | if self.opts.case_insensitive { | |||
return None; | return None; | |||
} | } | |||
// We don't care at all about the beginning of this pattern. All we | // We don't care at all about the beginning of this pattern. All we | |||
// need to check for is if it ends with a literal of the form `.ext`. | // need to check for is if it ends with a literal of the form `.ext`. | |||
let mut ext: Vec<char> = vec![]; // built in reverse | let mut ext: Vec<char> = vec![]; // built in reverse | |||
for t in self.tokens.iter().rev() { | for t in self.tokens.iter().rev() { | |||
skipping to change at line 406 | skipping to change at line 406 | |||
Some(ext.into_iter().collect()) | Some(ext.into_iter().collect()) | |||
} | } | |||
} | } | |||
/// Returns a literal prefix of this pattern if the entire pattern matches | /// Returns a literal prefix of this pattern if the entire pattern matches | |||
/// if the literal prefix matches. | /// if the literal prefix matches. | |||
fn prefix(&self) -> Option<String> { | fn prefix(&self) -> Option<String> { | |||
if self.opts.case_insensitive { | if self.opts.case_insensitive { | |||
return None; | return None; | |||
} | } | |||
let end = match self.tokens.last() { | let (end, need_sep) = match self.tokens.last() { | |||
Some(&Token::ZeroOrMore) => { | Some(&Token::ZeroOrMore) => { | |||
if self.opts.literal_separator { | if self.opts.literal_separator { | |||
// If a trailing `*` can't match a `/`, then we can't | // If a trailing `*` can't match a `/`, then we can't | |||
// assume a match of the prefix corresponds to a match | // assume a match of the prefix corresponds to a match | |||
// of the overall pattern. e.g., `foo/*` with | // of the overall pattern. e.g., `foo/*` with | |||
// `literal_separator` enabled matches `foo/bar` but not | // `literal_separator` enabled matches `foo/bar` but not | |||
// `foo/bar/baz`, even though `foo/bar/baz` has a `foo/` | // `foo/bar/baz`, even though `foo/bar/baz` has a `foo/` | |||
// literal prefix. | // literal prefix. | |||
return None; | return None; | |||
} | } | |||
self.tokens.len() - 1 | (self.tokens.len() - 1, false) | |||
} | } | |||
_ => self.tokens.len(), | Some(&Token::RecursiveSuffix) => (self.tokens.len() - 1, true), | |||
_ => (self.tokens.len(), false), | ||||
}; | }; | |||
let mut lit = String::new(); | let mut lit = String::new(); | |||
for t in &self.tokens[0..end] { | for t in &self.tokens[0..end] { | |||
match *t { | match *t { | |||
Token::Literal(c) => lit.push(c), | Token::Literal(c) => lit.push(c), | |||
_ => return None, | _ => return None, | |||
} | } | |||
} | } | |||
if need_sep { | ||||
lit.push('/'); | ||||
} | ||||
if lit.is_empty() { | if lit.is_empty() { | |||
None | None | |||
} else { | } else { | |||
Some(lit) | Some(lit) | |||
} | } | |||
} | } | |||
/// Returns a literal suffix of this pattern if the entire pattern matches | /// Returns a literal suffix of this pattern if the entire pattern matches | |||
/// if the literal suffix matches. | /// if the literal suffix matches. | |||
/// | /// | |||
skipping to change at line 615 | skipping to change at line 619 | |||
/// Toggle whether the pattern matches case insensitively or not. | /// Toggle whether the pattern matches case insensitively or not. | |||
/// | /// | |||
/// This is disabled by default. | /// This is disabled by default. | |||
pub fn case_insensitive(&mut self, yes: bool) -> &mut GlobBuilder<'a> { | pub fn case_insensitive(&mut self, yes: bool) -> &mut GlobBuilder<'a> { | |||
self.opts.case_insensitive = yes; | self.opts.case_insensitive = yes; | |||
self | self | |||
} | } | |||
/// Toggle whether a literal `/` is required to match a path separator. | /// Toggle whether a literal `/` is required to match a path separator. | |||
/// | ||||
/// By default this is false: `*` and `?` will match `/`. | ||||
pub fn literal_separator(&mut self, yes: bool) -> &mut GlobBuilder<'a> { | pub fn literal_separator(&mut self, yes: bool) -> &mut GlobBuilder<'a> { | |||
self.opts.literal_separator = yes; | self.opts.literal_separator = yes; | |||
self | self | |||
} | } | |||
/// When enabled, a back slash (`\`) may be used to escape | /// When enabled, a back slash (`\`) may be used to escape | |||
/// special characters in a glob pattern. Additionally, this will | /// special characters in a glob pattern. Additionally, this will | |||
/// prevent `\` from being interpreted as a path separator on all | /// prevent `\` from being interpreted as a path separator on all | |||
/// platforms. | /// platforms. | |||
/// | /// | |||
skipping to change at line 686 | skipping to change at line 692 | |||
if options.literal_separator { | if options.literal_separator { | |||
re.push_str("[^/]*"); | re.push_str("[^/]*"); | |||
} else { | } else { | |||
re.push_str(".*"); | re.push_str(".*"); | |||
} | } | |||
} | } | |||
Token::RecursivePrefix => { | Token::RecursivePrefix => { | |||
re.push_str("(?:/?|.*/)"); | re.push_str("(?:/?|.*/)"); | |||
} | } | |||
Token::RecursiveSuffix => { | Token::RecursiveSuffix => { | |||
re.push_str("(?:/?|/.*)"); | re.push_str("/.*"); | |||
} | } | |||
Token::RecursiveZeroOrMore => { | Token::RecursiveZeroOrMore => { | |||
re.push_str("(?:/|/.*/)"); | re.push_str("(?:/|/.*/)"); | |||
} | } | |||
Token::Class { negated, ref ranges } => { | Token::Class { negated, ref ranges } => { | |||
re.push('['); | re.push('['); | |||
if negated { | if negated { | |||
re.push('^'); | re.push('^'); | |||
} | } | |||
for r in ranges { | for r in ranges { | |||
skipping to change at line 1012 | skipping to change at line 1018 | |||
if needle.len() > haystack.len() { | if needle.len() > haystack.len() { | |||
return false; | return false; | |||
} | } | |||
needle == &haystack[haystack.len() - needle.len()..] | needle == &haystack[haystack.len() - needle.len()..] | |||
} | } | |||
#[cfg(test)] | #[cfg(test)] | |||
mod tests { | mod tests { | |||
use super::Token::*; | use super::Token::*; | |||
use super::{Glob, GlobBuilder, Token}; | use super::{Glob, GlobBuilder, Token}; | |||
use {ErrorKind, GlobSetBuilder}; | use crate::{ErrorKind, GlobSetBuilder}; | |||
#[derive(Clone, Copy, Debug, Default)] | #[derive(Clone, Copy, Debug, Default)] | |||
struct Options { | struct Options { | |||
casei: Option<bool>, | casei: Option<bool>, | |||
litsep: Option<bool>, | litsep: Option<bool>, | |||
bsesc: Option<bool>, | bsesc: Option<bool>, | |||
} | } | |||
macro_rules! syntax { | macro_rules! syntax { | |||
($name:ident, $pat:expr, $tokens:expr) => { | ($name:ident, $pat:expr, $tokens:expr) => { | |||
skipping to change at line 1225 | skipping to change at line 1231 | |||
toregex!(re9, "[+]", r"^[\+]$"); | toregex!(re9, "[+]", r"^[\+]$"); | |||
toregex!(re10, "+", r"^\+$"); | toregex!(re10, "+", r"^\+$"); | |||
toregex!(re11, "☃", r"^\xe2\x98\x83$"); | toregex!(re11, "☃", r"^\xe2\x98\x83$"); | |||
toregex!(re12, "**", r"^.*$"); | toregex!(re12, "**", r"^.*$"); | |||
toregex!(re13, "**/", r"^.*$"); | toregex!(re13, "**/", r"^.*$"); | |||
toregex!(re14, "**/*", r"^(?:/?|.*/).*$"); | toregex!(re14, "**/*", r"^(?:/?|.*/).*$"); | |||
toregex!(re15, "**/**", r"^.*$"); | toregex!(re15, "**/**", r"^.*$"); | |||
toregex!(re16, "**/**/*", r"^(?:/?|.*/).*$"); | toregex!(re16, "**/**/*", r"^(?:/?|.*/).*$"); | |||
toregex!(re17, "**/**/**", r"^.*$"); | toregex!(re17, "**/**/**", r"^.*$"); | |||
toregex!(re18, "**/**/**/*", r"^(?:/?|.*/).*$"); | toregex!(re18, "**/**/**/*", r"^(?:/?|.*/).*$"); | |||
toregex!(re19, "a/**", r"^a(?:/?|/.*)$"); | toregex!(re19, "a/**", r"^a/.*$"); | |||
toregex!(re20, "a/**/**", r"^a(?:/?|/.*)$"); | toregex!(re20, "a/**/**", r"^a/.*$"); | |||
toregex!(re21, "a/**/**/**", r"^a(?:/?|/.*)$"); | toregex!(re21, "a/**/**/**", r"^a/.*$"); | |||
toregex!(re22, "a/**/b", r"^a(?:/|/.*/)b$"); | toregex!(re22, "a/**/b", r"^a(?:/|/.*/)b$"); | |||
toregex!(re23, "a/**/**/b", r"^a(?:/|/.*/)b$"); | toregex!(re23, "a/**/**/b", r"^a(?:/|/.*/)b$"); | |||
toregex!(re24, "a/**/**/**/b", r"^a(?:/|/.*/)b$"); | toregex!(re24, "a/**/**/**/b", r"^a(?:/|/.*/)b$"); | |||
toregex!(re25, "**/b", r"^(?:/?|.*/)b$"); | toregex!(re25, "**/b", r"^(?:/?|.*/)b$"); | |||
toregex!(re26, "**/**/b", r"^(?:/?|.*/)b$"); | toregex!(re26, "**/**/b", r"^(?:/?|.*/)b$"); | |||
toregex!(re27, "**/**/**/b", r"^(?:/?|.*/)b$"); | toregex!(re27, "**/**/**/b", r"^(?:/?|.*/)b$"); | |||
toregex!(re28, "a**", r"^a.*.*$"); | toregex!(re28, "a**", r"^a.*.*$"); | |||
toregex!(re29, "**a", r"^.*.*a$"); | toregex!(re29, "**a", r"^.*.*a$"); | |||
toregex!(re30, "a**b", r"^a.*.*b$"); | toregex!(re30, "a**b", r"^a.*.*b$"); | |||
toregex!(re31, "***", r"^.*.*.*$"); | toregex!(re31, "***", r"^.*.*.*$"); | |||
skipping to change at line 1273 | skipping to change at line 1279 | |||
matches!(matchrec11, "some/**/**/needle.txt", "some/one/two/needle.txt"); | matches!(matchrec11, "some/**/**/needle.txt", "some/one/two/needle.txt"); | |||
matches!(matchrec12, "some/**/**/needle.txt", "some/other/needle.txt"); | matches!(matchrec12, "some/**/**/needle.txt", "some/other/needle.txt"); | |||
matches!(matchrec13, "**/test", "one/two/test"); | matches!(matchrec13, "**/test", "one/two/test"); | |||
matches!(matchrec14, "**/test", "one/test"); | matches!(matchrec14, "**/test", "one/test"); | |||
matches!(matchrec15, "**/test", "test"); | matches!(matchrec15, "**/test", "test"); | |||
matches!(matchrec16, "/**/test", "/one/two/test"); | matches!(matchrec16, "/**/test", "/one/two/test"); | |||
matches!(matchrec17, "/**/test", "/one/test"); | matches!(matchrec17, "/**/test", "/one/test"); | |||
matches!(matchrec18, "/**/test", "/test"); | matches!(matchrec18, "/**/test", "/test"); | |||
matches!(matchrec19, "**/.*", ".abc"); | matches!(matchrec19, "**/.*", ".abc"); | |||
matches!(matchrec20, "**/.*", "abc/.abc"); | matches!(matchrec20, "**/.*", "abc/.abc"); | |||
matches!(matchrec21, ".*/**", ".abc"); | matches!(matchrec21, "**/foo/bar", "foo/bar"); | |||
matches!(matchrec22, ".*/**", ".abc/abc"); | matches!(matchrec22, ".*/**", ".abc/abc"); | |||
matches!(matchrec23, "foo/**", "foo"); | matches!(matchrec23, "test/**", "test/"); | |||
matches!(matchrec24, "**/foo/bar", "foo/bar"); | matches!(matchrec24, "test/**", "test/one"); | |||
matches!(matchrec25, "some/*/needle.txt", "some/one/needle.txt"); | matches!(matchrec25, "test/**", "test/one/two"); | |||
matches!(matchrec26, "some/*/needle.txt", "some/one/needle.txt"); | ||||
matches!(matchrange1, "a[0-9]b", "a0b"); | matches!(matchrange1, "a[0-9]b", "a0b"); | |||
matches!(matchrange2, "a[0-9]b", "a9b"); | matches!(matchrange2, "a[0-9]b", "a9b"); | |||
matches!(matchrange3, "a[!0-9]b", "a_b"); | matches!(matchrange3, "a[!0-9]b", "a_b"); | |||
matches!(matchrange4, "[a-z123]", "1"); | matches!(matchrange4, "[a-z123]", "1"); | |||
matches!(matchrange5, "[1a-z23]", "1"); | matches!(matchrange5, "[1a-z23]", "1"); | |||
matches!(matchrange6, "[123a-z]", "1"); | matches!(matchrange6, "[123a-z]", "1"); | |||
matches!(matchrange7, "[abc-]", "-"); | matches!(matchrange7, "[abc-]", "-"); | |||
matches!(matchrange8, "[-abc]", "-"); | matches!(matchrange8, "[-abc]", "-"); | |||
matches!(matchrange9, "[-a-c]", "b"); | matches!(matchrange9, "[-a-c]", "b"); | |||
skipping to change at line 1403 | skipping to change at line 1410 | |||
"some/*/needle.txt", | "some/*/needle.txt", | |||
"some/one/two/needle.txt", | "some/one/two/needle.txt", | |||
SLASHLIT | SLASHLIT | |||
); | ); | |||
nmatches!( | nmatches!( | |||
matchrec32, | matchrec32, | |||
"some/*/needle.txt", | "some/*/needle.txt", | |||
"some/one/two/three/needle.txt", | "some/one/two/three/needle.txt", | |||
SLASHLIT | SLASHLIT | |||
); | ); | |||
nmatches!(matchrec33, ".*/**", ".abc"); | ||||
nmatches!(matchrec34, "foo/**", "foo"); | ||||
macro_rules! extract { | macro_rules! extract { | |||
($which:ident, $name:ident, $pat:expr, $expect:expr) => { | ($which:ident, $name:ident, $pat:expr, $expect:expr) => { | |||
extract!($which, $name, $pat, $expect, Options::default()); | extract!($which, $name, $pat, $expect, Options::default()); | |||
}; | }; | |||
($which:ident, $name:ident, $pat:expr, $expect:expr, $options:expr) => { | ($which:ident, $name:ident, $pat:expr, $expect:expr, $options:expr) => { | |||
#[test] | #[test] | |||
fn $name() { | fn $name() { | |||
let mut builder = GlobBuilder::new($pat); | let mut builder = GlobBuilder::new($pat); | |||
if let Some(casei) = $options.casei { | if let Some(casei) = $options.casei { | |||
skipping to change at line 1507 | skipping to change at line 1516 | |||
required_ext!(extract_req_ext4, "/foo/bar/.rs", Some(s(".rs"))); | required_ext!(extract_req_ext4, "/foo/bar/.rs", Some(s(".rs"))); | |||
required_ext!(extract_req_ext5, ".rs", Some(s(".rs"))); | required_ext!(extract_req_ext5, ".rs", Some(s(".rs"))); | |||
required_ext!(extract_req_ext6, "./rs", None); | required_ext!(extract_req_ext6, "./rs", None); | |||
required_ext!(extract_req_ext7, "foo", None); | required_ext!(extract_req_ext7, "foo", None); | |||
required_ext!(extract_req_ext8, ".foo/", None); | required_ext!(extract_req_ext8, ".foo/", None); | |||
required_ext!(extract_req_ext9, "foo/", None); | required_ext!(extract_req_ext9, "foo/", None); | |||
prefix!(extract_prefix1, "/foo", Some(s("/foo"))); | prefix!(extract_prefix1, "/foo", Some(s("/foo"))); | |||
prefix!(extract_prefix2, "/foo/*", Some(s("/foo/"))); | prefix!(extract_prefix2, "/foo/*", Some(s("/foo/"))); | |||
prefix!(extract_prefix3, "**/foo", None); | prefix!(extract_prefix3, "**/foo", None); | |||
prefix!(extract_prefix4, "foo/**", None); | prefix!(extract_prefix4, "foo/**", Some(s("foo/"))); | |||
suffix!(extract_suffix1, "**/foo/bar", Some((s("/foo/bar"), true))); | suffix!(extract_suffix1, "**/foo/bar", Some((s("/foo/bar"), true))); | |||
suffix!(extract_suffix2, "*/foo/bar", Some((s("/foo/bar"), false))); | suffix!(extract_suffix2, "*/foo/bar", Some((s("/foo/bar"), false))); | |||
suffix!(extract_suffix3, "*/foo/bar", None, SLASHLIT); | suffix!(extract_suffix3, "*/foo/bar", None, SLASHLIT); | |||
suffix!(extract_suffix4, "foo/bar", Some((s("foo/bar"), false))); | suffix!(extract_suffix4, "foo/bar", Some((s("foo/bar"), false))); | |||
suffix!(extract_suffix5, "*.foo", Some((s(".foo"), false))); | suffix!(extract_suffix5, "*.foo", Some((s(".foo"), false))); | |||
suffix!(extract_suffix6, "*.foo", None, SLASHLIT); | suffix!(extract_suffix6, "*.foo", None, SLASHLIT); | |||
suffix!(extract_suffix7, "**/*_test", Some((s("_test"), false))); | suffix!(extract_suffix7, "**/*_test", Some((s("_test"), false))); | |||
baseliteral!(extract_baselit1, "**/foo", Some(s("foo"))); | baseliteral!(extract_baselit1, "**/foo", Some(s("foo"))); | |||
End of changes. 17 change blocks. | ||||
18 lines changed or deleted | 27 lines changed or added |