ib_matcher/syntax/ev.rs
1/*!
2# IbEverythingExt flavour
3Parse a pattern according to the syntax used by [IbEverythingExt](https://github.com/Chaoses-Ib/IbEverythingExt).
4
5See [`Pattern::parse_ev`].
6
7## Example
8```
9// cargo add ib-matcher --features syntax-ev,pinyin
10use ib_matcher::{matcher::{IbMatcher, PinyinMatchConfig, pattern::Pattern}, pinyin::PinyinNotation};
11
12let matcher = IbMatcher::builder(Pattern::parse_ev("pinyin;py").call())
13 .pinyin(PinyinMatchConfig::notations(PinyinNotation::Ascii))
14 .build();
15assert!(matcher.is_match("拼音搜索"));
16assert!(matcher.is_match("pinyin") == false);
17```
18
19## With `Regex`
20```
21use ib_matcher::{regex::lita::Regex, matcher::{MatchConfig, pattern::Pattern}};
22
23let re = Regex::builder()
24 .ib(MatchConfig::builder().pinyin(Default::default()).build())
25 .ib_parser(&mut |pattern| Pattern::parse_ev(pattern).call())
26 .build("pinyin;py")
27 .unwrap();
28assert!(re.is_match("拼音搜索"));
29assert!(re.is_match("pinyin") == false);
30```
31
32## With [`glob`](super::glob)
33```
34use ib_matcher::{
35 matcher::{MatchConfig, pattern::Pattern},
36 regex::lita::Regex,
37 syntax::glob::{parse_wildcard_path, PathSeparator}
38};
39
40let re = Regex::builder()
41 .ib(MatchConfig::builder().pinyin(Default::default()).build())
42 .ib_parser(&mut |pattern| Pattern::parse_ev(pattern).call())
43 .build_from_hir(
44 parse_wildcard_path()
45 .separator(PathSeparator::Windows)
46 .call(r"pinyin;py**sou;py"),
47 )
48 .unwrap();
49assert!(re.is_match(r"C:\拼音\System32\搜索.exe"));
50assert!(re.is_match(r"C:\pinyin\System32\搜索.exe") == false);
51assert!(re.is_match(r"C:\pinyin\System32\sousuo.exe") == false);
52```
53*/
54
55use bon::bon;
56
57use crate::matcher::pattern::{LangOnly, Pattern};
58
59#[bon]
60impl<'a> Pattern<'a, str> {
61 /// Parse a pattern according to the syntax used by [IbEverythingExt](https://github.com/Chaoses-Ib/IbEverythingExt).
62 ///
63 /// - `;en`, `;py` and `;rm` postmodifiers are mutually exclusive. If multiple are present, only the last one will be considered as a postmodifier.
64 ///
65 /// Only UTF-8 pattern is supported at the moment.
66 ///
67 /// ## Example
68 /// ```
69 /// use ib_matcher::{matcher::{IbMatcher, PinyinMatchConfig, pattern::Pattern}, pinyin::PinyinNotation};
70 ///
71 /// let matcher = IbMatcher::builder(Pattern::parse_ev("pinyin;py").call())
72 /// .pinyin(PinyinMatchConfig::notations(PinyinNotation::Ascii))
73 /// .build();
74 /// assert!(matcher.is_match("拼音搜索"));
75 /// assert!(matcher.is_match("pinyin") == false);
76 /// ```
77 #[builder]
78 pub fn parse_ev(
79 #[builder(start_fn)] mut pattern: &'a str,
80
81 /// `;en` (English) postmodifier that disables both pinyin and romaji match, if any.
82 #[builder(default = true)]
83 postmodifier_en: bool,
84 /// `;py` (pinyin) postmodifier that indicates the pattern should be matched as pinyin only.
85 #[builder(default = true)]
86 postmodifier_py: bool,
87 /// `;rm` (romaji) postmodifier that indicates the pattern should be matched as romaji only.
88 #[builder(default = true)]
89 postmodifier_rm: bool,
90 ) -> Self {
91 let mut lang_only = None;
92 if let Some(s) = pattern.strip_suffix(";en").filter(|_| postmodifier_en) {
93 lang_only = Some(LangOnly::English);
94 pattern = s;
95 } else if let Some(s) = pattern.strip_suffix(";py").filter(|_| postmodifier_py) {
96 lang_only = Some(LangOnly::Pinyin);
97 pattern = s;
98 } else if let Some(s) = pattern.strip_suffix(";rm").filter(|_| postmodifier_rm) {
99 lang_only = Some(LangOnly::Romaji);
100 pattern = s;
101 }
102
103 Self { pattern, lang_only }
104 }
105}
106
107// #[bon]
108// impl<'a, 'f1, HaystackStr, S: ib_matcher_builder::State> IbMatcherBuilder<'a, 'f1, HaystackStr, S>
109// where
110// HaystackStr: EncodedStr + ?Sized,
111// {
112// #[builder(finish_fn = build)]
113// pub fn parse_ev(self, case_insensitive: bool) -> IbMatcher<'a, HaystackStr>
114// where
115// S: ib_matcher_builder::IsComplete,
116// {
117// dbg!(&self.pattern.as_bytes());
118// self.build()
119// }
120// }
121
122#[cfg(test)]
123mod tests {
124 use crate::{
125 matcher::{IbMatcher, MatchConfig, PinyinMatchConfig},
126 pinyin::PinyinNotation,
127 regex::lita::Regex,
128 syntax::glob::{parse_wildcard_path, PathSeparator},
129 };
130
131 use super::*;
132
133 #[test]
134 fn lang_only() {
135 let p = Pattern::parse_ev("pinyin").call();
136 assert!(p.lang_only.is_none());
137
138 let p = Pattern::parse_ev("pinyin;en").call();
139 assert_eq!(p.lang_only, Some(LangOnly::English));
140
141 let p = Pattern::parse_ev("pinyin;py").call();
142 assert_eq!(p.lang_only, Some(LangOnly::Pinyin));
143
144 let p = Pattern::parse_ev("pinyin;rm").call();
145 assert_eq!(p.lang_only, Some(LangOnly::Romaji));
146
147 let p = Pattern::parse_ev("pinyin;en;py").call();
148 assert_eq!(p.pattern, "pinyin;en");
149 assert_eq!(p.lang_only, Some(LangOnly::Pinyin));
150
151 let matcher = IbMatcher::builder(Pattern::parse_ev("pinyin;py").call())
152 .pinyin(PinyinMatchConfig::notations(PinyinNotation::Ascii))
153 .build();
154 assert!(matcher.is_match("拼音搜索"));
155 assert!(matcher.is_match("pinyin") == false);
156 }
157
158 #[test]
159 fn re() {
160 let re = Regex::builder()
161 .ib(MatchConfig::builder().pinyin(Default::default()).build())
162 .ib_parser(&mut |pattern| Pattern::parse_ev(pattern).call())
163 .build("pinyin;py")
164 .unwrap();
165 assert!(re.is_match("拼音搜索"));
166 assert!(re.is_match("pinyin") == false);
167 }
168
169 #[test]
170 fn glob() {
171 let re = Regex::builder()
172 .ib(MatchConfig::builder().pinyin(Default::default()).build())
173 .ib_parser(&mut |pattern| Pattern::parse_ev(pattern).call())
174 .build_from_hir(
175 parse_wildcard_path()
176 .separator(PathSeparator::Windows)
177 .call(r"pinyin;py**sou;py"),
178 )
179 .unwrap();
180 assert!(re.is_match(r"C:\拼音\System32\搜索.exe"));
181 assert!(re.is_match(r"C:\pinyin\System32\搜索.exe") == false);
182 assert!(re.is_match(r"C:\pinyin\System32\sousuo.exe") == false);
183 }
184}