Skip to main content

findutils/find/matchers/
name.rs

1// Copyright 2017 Google Inc.
2//
3// Use of this source code is governed by a MIT-style
4// license that can be found in the LICENSE file or at
5// https://opensource.org/licenses/MIT.
6
7use super::glob::Pattern;
8use super::{Matcher, MatcherIO, WalkEntry};
9
10/// This matcher makes a comparison of the name against a shell wildcard
11/// pattern. See `glob::Pattern` for details on the exact syntax.
12pub struct NameMatcher {
13    pattern: Pattern,
14}
15
16impl NameMatcher {
17    pub fn new(pattern_string: &str, caseless: bool) -> Self {
18        let pattern = Pattern::new(pattern_string, caseless);
19        Self { pattern }
20    }
21}
22
23impl Matcher for NameMatcher {
24    fn matches(&self, file_info: &WalkEntry, _: &mut MatcherIO) -> bool {
25        let name = file_info.file_name().to_string_lossy();
26
27        #[cfg(unix)]
28        if name.len() > 1 && name.chars().all(|x| x == '/') {
29            self.pattern.matches("/")
30        } else {
31            self.pattern.matches(&name)
32        }
33
34        #[cfg(windows)]
35        self.pattern.matches(&name)
36    }
37}
38
39#[cfg(test)]
40mod tests {
41    use super::*;
42    use crate::find::matchers::tests::get_dir_entry_for;
43    use crate::find::tests::FakeDependencies;
44    use std::io::ErrorKind;
45
46    #[cfg(unix)]
47    use std::os::unix::fs::symlink;
48
49    #[cfg(windows)]
50    use std::os::windows::fs::symlink_file;
51
52    fn create_file_link() {
53        #[cfg(unix)]
54        if let Err(e) = symlink("abbbc", "test_data/links/link-f") {
55            assert!(
56                e.kind() == ErrorKind::AlreadyExists,
57                "Failed to create sym link: {e:?}"
58            );
59        }
60        #[cfg(windows)]
61        if let Err(e) = symlink_file("abbbc", "test_data/links/link-f") {
62            assert!(
63                e.kind() == ErrorKind::AlreadyExists,
64                "Failed to create sym link: {:?}",
65                e
66            );
67        }
68    }
69
70    #[test]
71    fn matching_with_wrong_case_returns_false() {
72        let abbbc = get_dir_entry_for("test_data/simple", "abbbc");
73        let matcher = NameMatcher::new("A*C", false);
74        let deps = FakeDependencies::new();
75        assert!(!matcher.matches(&abbbc, &mut deps.new_matcher_io()));
76    }
77
78    #[test]
79    fn matching_with_right_case_returns_true() {
80        let abbbc = get_dir_entry_for("test_data/simple", "abbbc");
81        let matcher = NameMatcher::new("abb?c", false);
82        let deps = FakeDependencies::new();
83        assert!(matcher.matches(&abbbc, &mut deps.new_matcher_io()));
84    }
85
86    #[test]
87    fn not_matching_returns_false() {
88        let abbbc = get_dir_entry_for("test_data/simple", "abbbc");
89        let matcher = NameMatcher::new("shouldn't match", false);
90        let deps = FakeDependencies::new();
91        assert!(!matcher.matches(&abbbc, &mut deps.new_matcher_io()));
92    }
93
94    #[test]
95    fn matches_against_link_file_name() {
96        create_file_link();
97
98        let link_f = get_dir_entry_for("test_data/links", "link-f");
99        let matcher = NameMatcher::new("link?f", false);
100        let deps = FakeDependencies::new();
101        assert!(matcher.matches(&link_f, &mut deps.new_matcher_io()));
102    }
103
104    #[test]
105    fn caseless_matching_with_wrong_case_returns_true() {
106        let abbbc = get_dir_entry_for("test_data/simple", "abbbc");
107        let matcher = NameMatcher::new("A*C", true);
108        let deps = FakeDependencies::new();
109        assert!(matcher.matches(&abbbc, &mut deps.new_matcher_io()));
110    }
111
112    #[test]
113    fn caseless_matching_with_right_case_returns_true() {
114        let abbbc = get_dir_entry_for("test_data/simple", "abbbc");
115        let matcher = NameMatcher::new("abb?c", true);
116        let deps = FakeDependencies::new();
117        assert!(matcher.matches(&abbbc, &mut deps.new_matcher_io()));
118    }
119
120    #[test]
121    fn caseless_not_matching_returns_false() {
122        let abbbc = get_dir_entry_for("test_data/simple", "abbbc");
123        let matcher = NameMatcher::new("shouldn't match", true);
124        let deps = FakeDependencies::new();
125        assert!(!matcher.matches(&abbbc, &mut deps.new_matcher_io()));
126    }
127
128    #[test]
129    fn caseless_matches_against_link_file_name() {
130        create_file_link();
131
132        let link_f = get_dir_entry_for("test_data/links", "link-f");
133        let matcher = NameMatcher::new("linK?f", true);
134        let deps = FakeDependencies::new();
135        assert!(matcher.matches(&link_f, &mut deps.new_matcher_io()));
136    }
137
138    #[test]
139    #[cfg(unix)]
140    fn slash_match_returns_true() {
141        let dir_to_match = get_dir_entry_for("///", "");
142        let matcher = NameMatcher::new("/", true);
143        let deps = FakeDependencies::new();
144        assert!(matcher.matches(&dir_to_match, &mut deps.new_matcher_io()));
145    }
146
147    #[test]
148    #[cfg(unix)]
149    fn only_one_slash() {
150        let dir_to_match = get_dir_entry_for("/", "");
151        let matcher = NameMatcher::new("/", false);
152        let deps = FakeDependencies::new();
153        assert!(matcher.matches(&dir_to_match, &mut deps.new_matcher_io()));
154    }
155}