regexm/
lib.rs

1#[macro_export]
2macro_rules! regexm {
3    (
4        match $str:tt {captures($pattern:expr) => |$caps:ident| $expr:expr, $($rest:tt)*}
5    ) => {
6        $crate::__regexm! {
7            tokens = [$($rest)*],
8            str = $str,
9            first_pattern = $pattern,
10            first_expr = {
11                let closure = |$caps: regex::Captures| $expr;
12                closure(regex::Regex::new($pattern).unwrap().captures($str).unwrap())
13            },
14            result = unknown,
15        }
16    };
17
18    (
19        match $str:tt {captures($pattern:expr) => |$caps:ident| $expr:block $($rest:tt)*}
20    ) => {
21        $crate::__regexm! {
22            tokens = [$($rest)*],
23            str = $str,
24            first_pattern = $pattern,
25            first_expr = {
26                let closure = |$caps: regex::Captures| $expr;
27                closure(regex::Regex::new($pattern).unwrap().captures($str).unwrap())
28            },
29            result = unknown,
30        }
31    };
32
33    (
34        match $str:tt {$pattern:expr => $expr:expr, $($rest:tt)*}
35    ) => {
36        $crate::__regexm! {
37            tokens = [$($rest)*],
38            str = $str,
39            first_pattern = $pattern,
40            first_expr = $expr,
41            result = unknown,
42        }
43    };
44
45    (
46        match $str:tt {$pattern:expr => $expr:block $($rest:tt)*}
47    ) => {
48        $crate::__regexm! {
49            tokens = [$($rest)*],
50            str = $str,
51            first_pattern = $pattern,
52            first_expr = $expr,
53            result = unknown,
54        }
55    };
56}
57
58#[macro_export]
59#[doc(hidden)]
60macro_rules! __regexm {
61    (
62        tokens = [captures($default:pat) => |$caps:ident| $default_expr:expr$(,)?],
63        str = $str:expr,
64        first_pattern = $first_pattern:expr,
65        first_expr = $first_expr:expr,
66        result = [$($result:tt)*],
67    ) => {
68        $($result)* else {
69            let closure = |$caps: regex::Captures| $default_expr;
70            closure(regex::Regex::new($default).unwrap().captures($str).unwrap())
71        };
72    };
73
74    (
75        tokens = [captures($default:pat) => |$caps:ident| $default_expr:block$(,)?],
76        str = $str:expr,
77        first_pattern = $first_pattern:expr,
78        first_expr = $first_expr:expr,
79        result = [$($result:tt)*],
80    ) => {
81        $($result)* else {
82            let closure = |$caps: regex::Captures| $default_expr;
83            closure(regex::Regex::new($default).unwrap().captures($str).unwrap())
84        };
85    };
86
87    (
88        tokens = [$default:pat => $default_expr:expr$(,)?],
89        str = $str:expr,
90        first_pattern = $first_pattern:expr,
91        first_expr = $first_expr:expr,
92        result = [$($result:tt)*],
93    ) => {
94        $($result)* else {$default_expr};
95    };
96
97    (
98        tokens = [$default:pat => $default_expr:block$(,)?],
99        str = $str:expr,
100        first_pattern = $first_pattern:expr,
101        first_expr = $first_expr:expr,
102        result = [$($result:tt)*],
103    ) => {
104        $($result)* else {$default_expr};
105    };
106
107    (
108        tokens = [],
109        str = $str:expr,
110        first_pattern = $first_pattern:expr,
111        first_expr = $first_expr:expr,
112        result = [$($result:tt)*],
113    ) => {
114        $($result)*
115    };
116
117    (
118        tokens = [captures($default:pat) => |$caps:ident| $default_expr:expr$(,)?],
119        str = $str:expr,
120        first_pattern = $first_pattern:expr,
121        first_expr = $first_expr:expr,
122        result = unknown,
123    ) => {
124        if regex::Regex::new($first_pattern).unwrap().is_match($str) {
125            $first_expr
126        } else {
127            let closure = |$caps: regex::Captures| $default_expr;
128            closure(regex::Regex::new($default).unwrap().captures($str).unwrap())
129        };
130    };
131
132    (
133        tokens = [captures($default:pat) => |$caps:ident| $default_expr:block$(,)?],
134        str = $str:expr,
135        first_pattern = $first_pattern:expr,
136        first_expr = $first_expr:expr,
137        result = unknown,
138    ) => {
139        if regex::Regex::new($first_pattern).unwrap().is_match($str) {
140            $first_expr
141        } else {
142            let closure = |$caps: regex::Captures| $default_expr;
143            closure(regex::Regex::new($default).unwrap().captures($str).unwrap())
144        };
145    };
146
147    (
148        tokens = [$default:pat => $default_expr:expr$(,)?],
149        str = $str:expr,
150        first_pattern = $first_pattern:expr,
151        first_expr = $first_expr:expr,
152        result = unknown,
153    ) => {
154        if regex::Regex::new($first_pattern).unwrap().is_match($str) {
155            $first_expr
156        } else {$default_expr};
157    };
158
159    (
160        tokens = [$default:pat => $default_expr:block$(,)?],
161        str = $str:expr,
162        first_pattern = $first_pattern:expr,
163        first_token = $first_tokens:expr,
164        result = unknown,
165    ) => {
166        if regex::Regex::new($first_pattern).unwrap().is_match($str) {
167            $first_tokens
168        } else {$default_expr};
169    };
170
171    (
172        tokens = [captures($pattern:expr) => |$caps:ident| $expr:expr, $($rest:tt)*],
173        str = $str:expr,
174        first_pattern = $first_pattern:expr,
175        first_expr = $first_expr:expr,
176        result = unknown,
177    ) => {
178        $crate::__regexm! {
179            tokens = [$($rest)*],
180            str = $str,
181            first_pattern = $first_pattern,
182            first_expr = $first_expr,
183            result = [if regex::Regex::new($first_pattern).unwrap().is_match($str) {
184                $first_expr
185            } else if regex::Regex::new($pattern).unwrap().is_match($str) {
186                let closure = |$caps: regex::Captures| $expr;
187                closure(regex::Regex::new($pattern).unwrap().captures($str).unwrap())
188            }],
189        }
190    };
191
192    (
193        tokens = [captures($pattern:expr) => |$caps:ident| $expr:block $($rest:tt)*],
194        str = $str:expr,
195        first_pattern = $first_pattern:expr,
196        first_expr = $first_expr:expr,
197        result = unknown,
198    ) => {
199        $crate::__regexm! {
200            tokens = [$($rest)*],
201            str = $str,
202            first_pattern = $first_pattern,
203            first_expr = $first_expr,
204            result = [if regex::Regex::new($first_pattern).unwrap().is_match($str) {
205                $first_expr
206            } else if regex::Regex::new($pattern).unwrap().is_match($str) {
207                let closure = |$caps: regex::Captures| $expr;
208                closure(regex::Regex::new($pattern).unwrap().captures($str).unwrap())
209            }],
210        }
211    };
212
213    (
214        tokens = [$pattern:expr => $expr:expr, $($rest:tt)*],
215        str = $str:expr,
216        first_pattern = $first_pattern:expr,
217        first_expr = $first_expr:expr,
218        result = unknown,
219    ) => {
220        $crate::__regexm! {
221            tokens = [$($rest)*],
222            str = $str,
223            first_pattern = $first_pattern,
224            first_expr = $first_expr,
225            result = [if regex::Regex::new($first_pattern).unwrap().is_match($str) {
226                $first_expr
227            } else if regex::Regex::new($pattern).unwrap().is_match($str) {
228                $expr
229            }],
230        }
231    };
232
233    (
234        tokens = [$pattern:expr => $expr:block $($rest:tt)*],
235        str = $str:expr,
236        first_pattern = $first_pattern:expr,
237        first_expr = $first_expr:expr,
238        result = unknown,
239    ) => {
240        $crate::__regexm! {
241            tokens = [$($rest)*],
242            str = $str,
243            first_pattern = $first_pattern,
244            first_expr = $first_expr,
245            result = [if regex::Regex::new($first_pattern).unwrap().is_match($str) {
246                $first_expr
247            } else if regex::Regex::new($pattern).unwrap().is_match($str) {
248                $expr
249            }],
250        }
251    };
252
253    (
254        tokens = [captures($pattern:expr) => |$caps:ident| $expr:expr, $($rest:tt)*],
255        str = $str:expr,
256        first_pattern = $first_pattern:expr,
257        first_expr = $first_expr:expr,
258        result = [$($result:tt)*],
259    ) => {
260        $crate::__regexm! {
261            tokens = [$($rest)*],
262            str = $str,
263            first_pattern = $first_pattern,
264            first_expr = $first_expr,
265            result = [$($result)* else if regex::Regex::new($pattern).unwrap().is_match($str) {
266                let closure = |$caps: regex::Captures| $expr;
267                closure(regex::Regex::new($pattern).unwrap().captures($str).unwrap())
268            }],
269        }
270    };
271
272    (
273        tokens = [captures($pattern:expr) => |$caps:ident| $expr:block $($rest:tt)*],
274        str = $str:expr,
275        first_pattern = $first_pattern:expr,
276        first_expr = $first_expr:expr,
277        result = [$($result:tt)*],
278    ) => {
279        $crate::__regexm! {
280            tokens = [$($rest)*],
281            str = $str,
282            first_pattern = $first_pattern,
283            first_expr = $first_expr,
284            result = [$($result)* else if regex::Regex::new($pattern).unwrap().is_match($str) {
285                let closure = |$caps: regex::Captures| $expr;
286                closure(regex::Regex::new($pattern).unwrap().captures($str).unwrap())
287            }],
288        }
289    };
290
291    (
292        tokens = [$pattern:expr => $expr:expr, $($rest:tt)*],
293        str = $str:expr,
294        first_pattern = $first_pattern:expr,
295        first_expr = $first_expr:expr,
296        result = [$($result:tt)*],
297    ) => {
298        $crate::__regexm! {
299            tokens = [$($rest)*],
300            str = $str,
301            first_pattern = $first_pattern,
302            first_expr = $first_expr,
303            result = [$($result)* else if regex::Regex::new($pattern).unwrap().is_match($str) {
304                $expr
305            }],
306        }
307    };
308
309    (
310        tokens = [$pattern:expr => $expr:block $($rest:tt)*],
311        str = $str:expr,
312        first_pattern = $first_pattern:expr,
313        first_expr = $first_expr:expr,
314        result = [$($result:tt)*],
315    ) => {
316        $crate::__regexm! {
317            tokens = [$($rest)*],
318            str = $str,
319            first_pattern = $first_pattern,
320            first_expr = $first_expr,
321            result = [$($result)* else if regex::Regex::new($pattern).unwrap().is_match($str) {
322                $expr
323            }],
324        }
325    };
326}
327
328#[cfg(test)]
329mod test {
330    #[test]
331    fn test_match_3_or_more_pattern() {
332        regexm!(match "2021-01-01" {
333            r"^\d{4}-\d{2}-\d{2}$" => assert!(true),
334            r"^\d{4}-\d{2}$" => assert!(false),
335            _ => assert!(false),
336        });
337
338        regexm!(match "foo" {
339            r"^\d{4}-\d{2}-\d{2}$" => assert!(false),
340            r"^\d{4}-\d{2}$" => assert!(false),
341            _ => assert!(true),
342        });
343
344        regexm!(match "2021-01-01" {
345            r"^\d{4}-\d{2}-\d{2}$" => assert!(true),
346            r"^\d{4}-\d{2}$" => assert!(false),
347            r"^\d{4}$" => assert!(false),
348            _ => assert!(false),
349        });
350    }
351
352    #[test]
353    fn test_match_3_or_more_pattern_block() {
354        regexm!(match "2021-01-01" {
355            r"^\d{4}-\d{2}-\d{2}$" => {
356                assert!(true);
357                assert!(true);
358            }
359            r"^\d{4}-\d{2}$" => {
360                assert!(false);
361                assert!(false);
362            }
363            _ => {
364                assert!(false);
365                assert!(false)
366            }
367        });
368
369        regexm!(match "foo" {
370            r"^\d{4}-\d{2}-\d{2}$" => {
371                assert!(false);
372                assert!(false);
373            }
374            r"^\d{4}-\d{2}$" => {
375                assert!(false);
376                assert!(false);
377            }
378            _ => {
379                assert!(true);
380                assert!(true)
381            }
382        });
383
384        regexm!(match "2021-01-01" {
385            r"^\d{4}-\d{2}-\d{2}$" => {
386                assert!(true);
387                assert!(true);
388            }
389            r"^\d{4}-\d{2}$" => {
390                assert!(false);
391                assert!(false);
392            }
393            r"^\d{4}$" => {
394                assert!(false);
395                assert!(false);
396            }
397            _ => {
398                assert!(false);
399                assert!(false)
400            }
401        });
402    }
403
404    #[test]
405    fn test_match_2_or_less_pattern() {
406        regexm!(match "2021-01-01" {
407            r"^\d{4}-\d{2}-\d{2}$" => assert!(true),
408            _ => assert!(false),
409        });
410
411        regexm!(match "foo" {
412            r"^\d{4}-\d{2}$" => assert!(false),
413            _ => assert!(true),
414        });
415    }
416
417    #[test]
418    fn test_match_2_or_less_pattern_block() {
419        regexm!(match "2021-01-01" {
420            r"^\d{4}-\d{2}-\d{2}$" => {
421                assert!(true);
422                assert!(true);
423            }
424            _ => {
425                assert!(false);
426                assert!(false)
427            }
428        });
429
430        regexm!(match "foo" {
431            r"^\d{4}-\d{2}$" => {
432                assert!(false);
433                assert!(false)
434            }
435            _ => {
436                assert!(true);
437                assert!(true);
438            }
439        });
440    }
441
442    #[test]
443    fn test_let_match_3_or_more_pattern() {
444        let foo = regexm!(match "2021-01-01" {
445            r"^\d{4}-\d{2}-\d{2}$" => "yyyy-mm-dd",
446            r"^\d{4}-\d{2}$" => "yyyy-mm",
447            _ => "default",
448        });
449        assert_eq!(foo, "yyyy-mm-dd");
450
451        let foo = regexm!(match "foo" {
452            r"^\d{4}-\d{2}-\d{2}$" => "yyyy-mm-dd",
453            r"^\d{4}-\d{2}$" => "yyyy-mm",
454            _ => "default",
455        });
456        assert_eq!(foo, "default");
457
458        let foo = regexm!(match "2021-01-01" {
459            r"^\d{4}-\d{2}-\d{2}$" => "yyyy-mm-dd",
460            r"^\d{4}-\d{2}$" => "yyyy-mm",
461            r"^\d{4}$" => "yyyy",
462            _ => "default",
463        });
464        assert_eq!(foo, "yyyy-mm-dd");
465    }
466
467    #[test]
468    fn test_let_match_3_or_more_pattern_block() {
469        let foo = regexm!(match "2021-01-01" {
470            r"^\d{4}-\d{2}-\d{2}$" => {
471                let bar = "yyyy-mm-dd";
472                bar
473            }
474            r"^\d{4}-\d{2}$" => {
475                let bar = "yyyy-mm";
476                bar
477            }
478            _ => "default",
479        });
480        assert_eq!(foo, "yyyy-mm-dd");
481
482        let foo = regexm!(match "foo" {
483            r"^\d{4}-\d{2}-\d{2}$" => {
484                let bar = "yyyy-mm-dd";
485                bar
486            }
487            r"^\d{4}-\d{2}$" => {
488                let bar = "yyyy-mm";
489                bar
490            }
491            _ => "default",
492        });
493        assert_eq!(foo, "default");
494
495        let foo = regexm!(match "2021-01-01" {
496            r"^\d{4}-\d{2}-\d{2}$" => {
497                let bar = "yyyy-mm-dd";
498                bar
499            }
500            r"^\d{4}-\d{2}$" => {
501                let bar = "yyyy-mm";
502                bar
503            }
504            r"^\d{4}$" => {
505                let bar = "yyyy";
506                bar
507            }
508            _ => "default",
509        });
510        assert_eq!(foo, "yyyy-mm-dd");
511    }
512
513    #[test]
514    fn test_let_match_2_or_less_pattern() {
515        let foo = regexm!(match "2021-01-01" {
516            r"^\d{4}-\d{2}-\d{2}$" => "yyyy-mm-dd",
517            _ => "default",
518        });
519        assert_eq!(foo, "yyyy-mm-dd");
520
521        let foo = regexm!(match "foo" {
522            r"^\d{4}-\d{2}$" => "yyyy-mm",
523            _ => "default",
524        });
525        assert_eq!(foo, "default");
526    }
527
528    #[test]
529    fn test_let_match_2_or_less_pattern_block() {
530        let foo = regexm!(match "2021-01-01" {
531            r"^\d{4}-\d{2}-\d{2}$" => {
532                let bar = "yyyy-mm-dd";
533                bar
534            }
535            _ => "default",
536        });
537        assert_eq!(foo, "yyyy-mm-dd");
538
539        let foo = regexm!(match "foo" {
540            r"^\d{4}-\d{2}$" => {
541                let bar = "yyyy-mm";
542                bar
543            }
544            _ => "default",
545        });
546        assert_eq!(foo, "default");
547    }
548
549    #[test]
550    fn test_capture_groups_match_3_or_more_pattern() {
551        regexm!(match "2021-01-01" {
552            captures(r"^(\d{4})-\d{2}-\d{2}$") =>
553                |caps| assert_eq!(caps.get(1).map_or("", |m| m.as_str()), "2021"),
554            r"^\d{4}-\d{2}$" => assert!(false),
555            _ => assert!(false),
556        });
557
558        regexm!(match "2021-01" {
559            r"^\d{4}-\d{2}-\d{2}$" => assert!(false),
560            captures(r"^(\d{4})-\d{2}$") =>
561                |caps| assert_eq!(caps.get(1).map_or("", |m| m.as_str()), "2021"),
562            r"^\d{4}$" => assert!(false),
563            _ => assert!(false),
564        });
565    }
566
567    #[test]
568    fn test_capture_groups_match_3_or_more_pattern_block() {
569        regexm!(match "2021-01-01" {
570            captures(r"^(\d{4})-\d{2}-\d{2}$") => |caps| {
571                let year = caps.get(1).map_or("", |m| m.as_str());
572                assert_eq!(year, "2021")
573            }
574            r"^\d{4}-\d{2}$" => {
575                assert!(false);
576                assert!(false);
577            }
578            _ => {
579                assert!(false);
580                assert!(false)
581            }
582        });
583
584        regexm!(match "2021-01" {
585            r"^\d{4}-\d{2}-\d{2}$" => {
586                assert!(false);
587                assert!(false);
588            }
589            captures(r"^(\d{4})-\d{2}$") => |caps| {
590                let month = caps.get(1).map_or("", |m| m.as_str());
591                assert_eq!(month, "2021")
592            }
593            r"^\d{4}$" => {
594                assert!(false);
595                assert!(false);
596            }
597            _ => {
598                assert!(false);
599                assert!(false)
600            }
601        });
602    }
603
604    #[test]
605    fn test_capture_groups_match_2_or_less_pattern() {
606        regexm!(match "2021-01-01" {
607            captures(r"^(\d{4})-\d{2}-\d{2}$") =>
608                |caps| assert_eq!(caps.get(1).map_or("", |m| m.as_str()), "2021"),
609            _ => assert!(false),
610        });
611
612        regexm!(match "foo" {
613            r"^\d{4}-\d{2}$" => assert!(false),
614            _ => assert!(true),
615        });
616    }
617
618    #[test]
619    fn test_capture_groups_match_2_or_less_pattern_block() {
620        regexm!(match "2021-01-01" {
621            captures(r"^(\d{4})-\d{2}-\d{2}$") => |caps| {
622                let year = caps.get(1).map_or("", |m| m.as_str());
623                assert_eq!(year, "2021");
624            }
625            _ => {
626                assert!(false);
627                assert!(false)
628            }
629        });
630    }
631}