diff --git a/src/find/matchers/mod.rs b/src/find/matchers/mod.rs index 803552a..ee97e87 100644 --- a/src/find/matchers/mod.rs +++ b/src/find/matchers/mod.rs @@ -11,6 +11,7 @@ mod glob; mod lname; mod logical_matchers; mod name; +mod path; mod perm; mod printer; mod printf; @@ -35,6 +36,7 @@ use self::logical_matchers::{ AndMatcherBuilder, FalseMatcher, ListMatcherBuilder, NotMatcher, TrueMatcher, }; use self::name::NameMatcher; +use self::path::PathMatcher; use self::perm::PermMatcher; use self::printer::{PrintDelimiter, Printer}; use self::printf::Printf; @@ -296,6 +298,13 @@ fn build_matcher_tree( i += 1; Some(NameMatcher::new(args[i], args[i - 1].starts_with("-i")).into_box()) } + "-path" | "-ipath" | "-wholename" | "-iwholename" => { + if i >= args.len() - 1 { + return Err(From::from(format!("missing argument to {}", args[i]))); + } + i += 1; + Some(PathMatcher::new(args[i], args[i - 1].starts_with("-i")).into_box()) + } "-regextype" => { if i >= args.len() - 1 { return Err(From::from(format!("missing argument to {}", args[i]))); diff --git a/src/find/matchers/path.rs b/src/find/matchers/path.rs new file mode 100644 index 0000000..58e2a97 --- /dev/null +++ b/src/find/matchers/path.rs @@ -0,0 +1,82 @@ +// Copyright 2017 Google Inc. +// +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +use walkdir::DirEntry; + +use super::glob::Pattern; +use super::{Matcher, MatcherIO}; + +/// This matcher makes a comparison of the path against a shell wildcard +/// pattern. See `glob::Pattern` for details on the exact syntax. +pub struct PathMatcher { + pattern: Pattern, +} + +impl PathMatcher { + pub fn new(pattern_string: &str, caseless: bool) -> Self { + let pattern = Pattern::new(pattern_string, caseless); + Self { pattern } + } +} + +impl Matcher for PathMatcher { + fn matches(&self, file_info: &DirEntry, _: &mut MatcherIO) -> bool { + let path = file_info.path().to_string_lossy(); + self.pattern.matches(&path) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::find::matchers::tests::get_dir_entry_for; + use crate::find::matchers::Matcher; + use crate::find::tests::FakeDependencies; + + // Variants of fix_up_slashes that properly escape the forward slashes for + // being in a glob. + #[cfg(windows)] + fn fix_up_glob_slashes(re: &str) -> String { + re.replace("/", "\\\\") + } + + #[cfg(not(windows))] + fn fix_up_glob_slashes(re: &str) -> String { + re.to_owned() + } + + #[test] + fn matching_against_whole_path() { + let abbbc = get_dir_entry_for("test_data/simple", "abbbc"); + let matcher = PathMatcher::new(&fix_up_glob_slashes("test_*/*/a*c"), false); + let deps = FakeDependencies::new(); + assert!(matcher.matches(&abbbc, &mut deps.new_matcher_io())); + } + + #[test] + fn not_matching_against_just_name() { + let abbbc = get_dir_entry_for("test_data/simple", "abbbc"); + let matcher = PathMatcher::new("a*c", false); + let deps = FakeDependencies::new(); + assert!(!matcher.matches(&abbbc, &mut deps.new_matcher_io())); + } + + #[test] + fn not_matching_against_wrong_case() { + let abbbc = get_dir_entry_for("test_data/simple", "abbbc"); + let matcher = PathMatcher::new(&fix_up_glob_slashes("test_*/*/A*C"), false); + let deps = FakeDependencies::new(); + assert!(!matcher.matches(&abbbc, &mut deps.new_matcher_io())); + } + + #[test] + fn caseless_matching() { + let abbbc = get_dir_entry_for("test_data/simple", "abbbc"); + let matcher = PathMatcher::new(&fix_up_glob_slashes("test_*/*/A*C"), true); + let deps = FakeDependencies::new(); + assert!(matcher.matches(&abbbc, &mut deps.new_matcher_io())); + } +} diff --git a/src/find/matchers/regex.rs b/src/find/matchers/regex.rs index 0627a6d..b29e3e5 100644 --- a/src/find/matchers/regex.rs +++ b/src/find/matchers/regex.rs @@ -130,7 +130,7 @@ mod tests { // being in a regex. #[cfg(windows)] fn fix_up_regex_slashes(re: &str) -> String { - re.replace("/", "\\\\") + re.replace("/", r"\\") } #[cfg(not(windows))]