1 use {
2     super::*,
3     crate::{
4         content_search::ContentMatch,
5         errors::PatternError,
6     },
7     bet::BeTree,
8     std::{
9         path::Path,
10     },
11 };
12 
13 /// a pattern for filtering and sorting files.
14 #[derive(Debug, Clone)]
15 pub enum Pattern {
16     None,
17     NameExact(ExactPattern),
18     NameFuzzy(FuzzyPattern),
19     NameRegex(RegexPattern),
20     NameTokens(TokPattern),
21     PathExact(ExactPattern),
22     PathFuzzy(FuzzyPattern),
23     PathRegex(RegexPattern),
24     PathTokens(TokPattern),
25     ContentExact(ContentExactPattern),
26     ContentRegex(ContentRegexPattern),
27     Composite(CompositePattern),
28 }
29 
30 impl Pattern {
31 
new( raw_expr: &BeTree<PatternOperator, PatternParts>, search_modes: &SearchModeMap, ) -> Result<Self, PatternError>32     pub fn new(
33         raw_expr: &BeTree<PatternOperator, PatternParts>,
34         search_modes: &SearchModeMap,
35     ) -> Result<Self, PatternError> {
36         let expr: BeTree<PatternOperator, Pattern> = raw_expr
37             .try_map_atoms::<_, PatternError, _>(|pattern_parts| {
38                 let core = pattern_parts.core();
39                 Ok(
40                     if core.is_empty() {
41                         Pattern::None
42                     } else {
43                         let parts_mode = pattern_parts.mode();
44                         let mode = search_modes.search_mode(parts_mode)?;
45                         let flags = pattern_parts.flags();
46                         match mode {
47                             SearchMode::NameExact => Self::NameExact(
48                                 ExactPattern::from(core)
49                             ),
50                             SearchMode::NameFuzzy => Self::NameFuzzy(
51                                 FuzzyPattern::from(core)
52                             ),
53                             SearchMode::NameRegex => Self::NameRegex(
54                                 RegexPattern::from(core, flags.unwrap_or(""))?
55                             ),
56                             SearchMode::NameTokens => Self::NameTokens(
57                                 TokPattern::new(core)
58                             ),
59                             SearchMode::PathExact => Self::PathExact(
60                                 ExactPattern::from(core)
61                             ),
62                             SearchMode::PathFuzzy => Self::PathFuzzy(
63                                 FuzzyPattern::from(core)
64                             ),
65                             SearchMode::PathRegex => Self::PathRegex(
66                                 RegexPattern::from(core, flags.unwrap_or(""))?
67                             ),
68                             SearchMode::PathTokens => Self::PathTokens(
69                                 TokPattern::new(core)
70                             ),
71                             SearchMode::ContentExact => Self::ContentExact(
72                                 ContentExactPattern::from(core)
73                             ),
74                             SearchMode::ContentRegex => Self::ContentRegex(
75                                 ContentRegexPattern::from(core, flags.unwrap_or(""))?
76                             ),
77                         }
78                     }
79                 )
80             })?;
81         Ok(if expr.is_empty() {
82             Pattern::None
83         } else if expr.is_atomic() {
84             expr.atoms().pop().unwrap()
85         } else {
86             Self::Composite(CompositePattern::new(expr))
87         })
88     }
89 
object(&self) -> PatternObject90     pub fn object(&self) -> PatternObject {
91         let mut object = PatternObject::default();
92         match self {
93             Self::None => {}
94             Self::NameExact(_) | Self::NameFuzzy(_) | Self::NameRegex(_) | Self::NameTokens(_) => {
95                 object.name = true;
96             }
97             Self::PathExact(_) | Self::PathFuzzy(_) | Self::PathRegex(_) | Self::PathTokens(_) => {
98                 object.subpath = true;
99             }
100             Self::ContentExact(_) | Self::ContentRegex(_) => {
101                 object.content = true;
102             }
103             Self::Composite(cp) => {
104                 for atom in cp.expr.iter_atoms() {
105                     object |= atom.object();
106                 }
107             }
108         }
109         object
110     }
111 
search_string( &self, candidate: &str, ) -> Option<NameMatch>112     pub fn search_string(
113         &self,
114         candidate: &str,
115     ) -> Option<NameMatch> {
116         match self {
117             Self::NameExact(ep) | Self::PathExact(ep) => ep.find(candidate),
118             Self::NameFuzzy(fp) | Self::PathFuzzy(fp) => fp.find(candidate),
119             Self::NameRegex(rp) | Self::PathRegex(rp) => rp.find(candidate),
120             Self::NameTokens(tp) | Self::PathTokens(tp) => tp.find(candidate),
121             Self::Composite(cp) => cp.search_string(candidate),
122             _ => None,
123         }
124     }
125 
126     /// find the content to show next to the name of the file
127     /// when the search involved a content filtering
search_content( &self, candidate: &Path, desired_len: usize, ) -> Option<ContentMatch>128     pub fn search_content(
129         &self,
130         candidate: &Path,
131         desired_len: usize, // available space for content match display
132     ) -> Option<ContentMatch> {
133         match self {
134             Self::ContentExact(cp) => cp.get_content_match(candidate, desired_len),
135             Self::ContentRegex(cp) => cp.get_content_match(candidate, desired_len),
136             Self::Composite(cp) => cp.search_content(candidate, desired_len),
137             _ => None,
138         }
139     }
140 
141     /// get the line of the first match, if any
get_match_line_count( &self, path: &Path, ) -> Option<usize>142     pub fn get_match_line_count(
143         &self,
144         path: &Path,
145     ) -> Option<usize> {
146         match self {
147             Self::ContentExact(cp) => cp.get_match_line_count(path),
148             Self::ContentRegex(cp) => cp.get_match_line_count(path),
149             Self::Composite(cp) => cp.get_match_line_count(path),
150             _ => None,
151         }
152     }
153 
score_of(&self, candidate: Candidate) -> Option<i32>154     pub fn score_of(&self, candidate: Candidate) -> Option<i32> {
155         match self {
156             Self::NameExact(ep) => ep.score_of(candidate.name),
157             Self::NameFuzzy(fp) => fp.score_of(candidate.name),
158             Self::NameRegex(rp) => rp.find(candidate.name).map(|m| m.score),
159             Self::NameTokens(tp) => tp.score_of(candidate.name),
160             Self::PathExact(ep) => ep.score_of(candidate.subpath),
161             Self::PathFuzzy(fp) => fp.score_of(candidate.subpath),
162             Self::PathRegex(rp) => rp.find(candidate.subpath).map(|m| m.score),
163             Self::PathTokens(tp) => tp.score_of(candidate.subpath),
164             Self::ContentExact(cp) => cp.score_of(candidate),
165             Self::ContentRegex(cp) => cp.score_of(candidate),
166             Self::Composite(cp) => cp.score_of(candidate),
167             Self::None => Some(1),
168         }
169     }
170 
score_of_string(&self, candidate: &str) -> Option<i32>171     pub fn score_of_string(&self, candidate: &str) -> Option<i32> {
172         match self {
173             Self::NameExact(ep) => ep.score_of(candidate),
174             Self::NameFuzzy(fp) => fp.score_of(candidate),
175             Self::NameRegex(rp) => rp.find(candidate).map(|m| m.score),
176             Self::NameTokens(tp) => tp.score_of(candidate),
177             Self::PathExact(ep) => ep.score_of(candidate),
178             Self::PathFuzzy(fp) => fp.score_of(candidate),
179             Self::PathRegex(rp) => rp.find(candidate).map(|m| m.score),
180             Self::PathTokens(tp) => tp.score_of(candidate),
181             Self::ContentExact(_) => None, // this isn't suitable
182             Self::ContentRegex(_) => None, // this isn't suitable
183             Self::Composite(cp) => cp.score_of_string(candidate),
184             Self::None => Some(1),
185         }
186     }
187 
is_some(&self) -> bool188     pub fn is_some(&self) -> bool {
189         !self.is_empty()
190     }
191 
192     /// an empty pattern is one which doesn't discriminate
193     /// (it accepts everything)
is_empty(&self) -> bool194     pub fn is_empty(&self) -> bool {
195         match self {
196             Self::NameExact(ep) | Self::PathExact(ep) => ep.is_empty(),
197             Self::ContentExact(ep) => ep.is_empty(),
198             Self::NameFuzzy(fp) | Self::PathFuzzy(fp) => fp.is_empty(),
199             Self::NameRegex(rp) | Self::PathRegex(rp) => rp.is_empty(),
200             Self::ContentRegex(rp) => rp.is_empty(),
201             Self::NameTokens(tp) | Self::PathTokens(tp) => tp.is_empty(),
202             Self::Composite(cp) => cp.is_empty(),
203             Self::None => true,
204         }
205     }
206 
207     /// whether the scores are more than just 0 or 1.
208     /// When it's the case, the tree builder will look for more matching results
209     /// in order to select the best ones.
has_real_scores(&self) -> bool210     pub fn has_real_scores(&self) -> bool {
211         match self {
212             Self::NameExact(_) | Self::NameFuzzy(_) => true,
213             Self::PathExact(_) | Self::PathFuzzy(_) => true,
214             Self::Composite(cp) => cp.has_real_scores(),
215             _ => false,
216         }
217     }
218 
219 }
220 
221