1#![no_std]
2#![doc = include_str!("../README.md")]
3
4#[doc = include_str!("../README.md")]
5pub trait MatchOne: FirstElem {
6 type Pattern: ?Sized;
7
8 fn match_one(&self, pattern: &Self::Pattern) -> Option<Self::Out>;
25
26 fn is_match_one(&self, pattern: &Self::Pattern) -> bool {
28 self.match_one(pattern).is_some()
29 }
30}
31
32impl<T: MatchOne> MatchOne for Option<T> {
33 type Pattern = T::Pattern;
34
35 fn match_one(&self, pattern: &Self::Pattern) -> Option<Self::Out> {
36 self.as_ref().and_then(|val| val.match_one(pattern))
37 }
38}
39
40impl<T: MatchOne + ?Sized> MatchOne for &T {
41 type Pattern = T::Pattern;
42
43 fn match_one(&self, pattern: &Self::Pattern) -> Option<Self::Out> {
44 (**self).match_one(pattern)
45 }
46}
47
48impl<T: MatchOne + ?Sized> MatchOne for &mut T {
49 type Pattern = T::Pattern;
50
51 fn match_one(&self, pattern: &Self::Pattern) -> Option<Self::Out> {
52 (**self).match_one(pattern)
53 }
54}
55
56impl MatchOne for char {
57 type Pattern = str;
58
59 fn match_one(&self, pattern: &Self::Pattern) -> Option<Self::Out> {
60 match_impl(pattern.chars(), self).then_some(*self)
61 }
62}
63
64impl MatchOne for u8 {
65 type Pattern = [u8];
66
67 fn match_one(&self, pattern: &Self::Pattern) -> Option<Self::Out> {
68 match_impl(pattern.iter().copied(), self).then_some(*self)
69 }
70}
71
72impl MatchOne for str {
73 type Pattern = str;
74
75 fn match_one(&self, pattern: &Self::Pattern) -> Option<Self::Out> {
76 self.chars().next().match_one(pattern)
77 }
78}
79
80impl MatchOne for () {
81 type Pattern = ();
82
83 fn match_one(&self, _pattern: &Self::Pattern) -> Option<Self::Out> {
84 None
85 }
86}
87
88impl<T: MatchOne> MatchOne for [T] {
89 type Pattern = T::Pattern;
90
91 fn match_one(&self, pattern: &Self::Pattern) -> Option<Self::Out> {
92 self.first().match_one(pattern)
93 }
94}
95
96impl<T: MatchOne, const N: usize> MatchOne for [T; N] {
97 type Pattern = T::Pattern;
98
99 fn match_one(&self, pattern: &Self::Pattern) -> Option<Self::Out> {
100 self.first().match_one(pattern)
101 }
102}
103
104macro_rules! impl_first_elem_trivial {
105 ($($ty:ty),+ $(,)?) => {
106 $(
107 impl FirstElem for $ty {
108 type Out = $ty;
109
110 fn first_elem(&self) -> Option<Self::Out> {
111 Some(*self)
112 }
113 }
114 )+
115 };
116}
117
118pub trait FirstElem {
120 type Out;
121
122 fn first_elem(&self) -> Option<Self::Out>;
123}
124
125impl_first_elem_trivial! {
126 u8,
127 u16,
128 u32,
129 u64,
130 u128,
131 i8,
132 i16,
133 i32,
134 i64,
135 i128,
136 char,
137 f32,
138 f64,
139}
140
141impl FirstElem for () {
142 type Out = core::convert::Infallible;
143
144 fn first_elem(&self) -> Option<Self::Out> {
145 None
146 }
147}
148
149impl<T: FirstElem + ?Sized> FirstElem for &T {
150 type Out = T::Out;
151
152 fn first_elem(&self) -> Option<Self::Out> {
153 (**self).first_elem()
154 }
155}
156
157impl<T: FirstElem + ?Sized> FirstElem for &mut T {
158 type Out = T::Out;
159
160 fn first_elem(&self) -> Option<Self::Out> {
161 (**self).first_elem()
162 }
163}
164
165impl<T: FirstElem> FirstElem for Option<T> {
166 type Out = T::Out;
167
168 fn first_elem(&self) -> Option<Self::Out> {
169 self.as_ref().and_then(T::first_elem)
170 }
171}
172
173impl FirstElem for str {
174 type Out = char;
175
176 fn first_elem(&self) -> Option<Self::Out> {
177 self.chars().next()
178 }
179}
180
181impl<T: FirstElem> FirstElem for [T] {
182 type Out = T::Out;
183
184 fn first_elem(&self) -> Option<Self::Out> {
185 self.first().and_then(T::first_elem)
186 }
187}
188
189impl<T: FirstElem, const N: usize> FirstElem for [T; N] {
190 type Out = T::Out;
191
192 fn first_elem(&self) -> Option<Self::Out> {
193 self.first().and_then(T::first_elem)
194 }
195}
196
197pub fn any<T>(pattern: impl AsRef<T::Pattern>, val: T) -> bool
224where T: MatchOne,
225{
226 val.is_match_one(pattern.as_ref())
227}
228
229fn match_impl<I>(pattern: I, val: &I::Item) -> bool
230where I: IntoIterator,
231 I::Item: PartialOrd + From<u8> + Copy,
232{
233 let pat = &mut pattern.into_iter();
234 let dash = &I::Item::from(b'-');
235
236 let Some(mut first) = pat.next() else {
237 return false;
238 };
239
240 while let Some(ref cur) = pat.next() {
241 if first == *val {
242 return true;
243 }
244 if cur != dash {
245 first = *cur;
246 } else if let Some(to) = pat.next() {
247 if (first..=to).contains(val) {
248 return true;
249 }
250 if let Some(next) = pat.next() {
251 first = next;
252 } else {
253 return false;
254 }
255 } else {
256 first = *cur;
257 }
258 }
259
260 first == *val
261}
262
263#[cfg(test)]
264mod tests {
265 use super::*;
266
267 #[test]
268 fn basic_pattern() {
269 let datas = [
270 ("a", 'a'),
271 ("ab", 'a'),
272 ("ba", 'a'),
273 ("bac", 'a'),
274 ("bca", 'a'),
275 ("a-e", 'c'),
276 ("a-c", 'c'),
277 ("a-bc", 'c'),
278 ("a-bc", 'a'),
279 ("a-bc", 'b'),
280 ("你好", '你'),
281 ("你好", '好'),
282 ("a-", 'a'),
283 ("a-", '-'),
284 ("-a", '-'),
285 ("-", '-'),
286 ];
287
288 for (pat, val) in datas {
289 assert!(match_impl(pat.chars(), &val), "{pat:?}, {val:?}");
290 }
291 }
292
293 #[test]
294 fn basic_not_pattern() {
295 let datas = [
296 ("", 'a'),
297 ("a-b", '-'),
298 ("a-", 'c'),
299 ("a-", 'b'),
300 ];
301
302 for (pat, val) in datas {
303 assert!(! match_impl(pat.chars(), &val), "{pat:?}, {val:?}");
304 }
305 }
306
307 #[test]
308 fn first_pattern() {
309 let datas = [
310 ("a", "a"),
311 ("你", "你好"),
312 ("a-c", "a"),
313 ("a-c", "b"),
314 ];
315
316 for (pat, val) in datas {
317 assert!(any(pat, val));
318 }
319 }
320
321 #[test]
322 fn first_not_pattern_rest() {
323 let datas = [
324 ("a", "da"),
325 ("好", "你好"),
326 ("a-c", "da"),
327 ("a-c", "db"),
328 ("ab", "db"),
329 ];
330
331 for (pat, val) in datas {
332 assert!(! any(pat, val));
333 }
334 }
335
336 #[test]
337 fn first_not_pattern_empty() {
338 let datas = [
339 ("a", ""),
340 ("a-c", ""),
341 ];
342
343 for (pat, val) in datas {
344 assert!(! any(pat, val));
345 }
346 }
347
348 #[test]
349 fn dash_range() {
350 let datas = [
351 ("+--", "+"),
352 ("+--", ","),
353 ("+--", "-"),
354 ("+--a", "+"),
355 ("+--a", ","),
356 ("+--a", "-"),
357 ("+--a", "a"),
358 ("--0", "-"),
359 ("--0", "."),
360 ("--0", "/"),
361 ("--0", "0"),
362 ("--0a", "-"),
363 ("--0a", "."),
364 ("--0a", "/"),
365 ("--0a", "0"),
366 ("--0a", "a"),
367 ("a-c-e-g", "a"),
368 ("a-c-e-g", "b"),
369 ("a-c-e-g", "c"),
370 ("a-c-e-g", "e"),
371 ("a-c-e-g", "f"),
372 ("a-c-e-g", "g"),
373 ("a-c-e-g", "-"),
374 ];
375
376 for (pat, val) in datas {
377 assert!(any(pat, val), "{pat:?} cannot pattern {val:?}");
378 }
379 }
380
381 #[test]
382 fn dash_range_not_pat() {
383 let datas = [
384 ("+--a", "0"),
385 ("---a", "0"),
386 ("a-c-e-g", "d"),
387 ];
388
389 for (pat, val) in datas {
390 assert!(! any(pat, val));
391 }
392 }
393}