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}