real_world_patterns/
real_world_patterns.rs1use re_parser::ast::{GroupKind, Regex};
11use re_parser::parse;
12
13fn count_captures(node: &Regex) -> usize {
15 match node {
16 Regex::Group(inner, kind) => {
17 let self_count = matches!(kind, GroupKind::Capturing | GroupKind::Named(_)) as usize;
18 self_count + count_captures(inner)
19 }
20 Regex::Quantifier(inner, _, _) => count_captures(inner),
21 Regex::Concat(nodes) | Regex::Alternation(nodes) => {
22 nodes.iter().map(count_captures).sum()
23 }
24 _ => 0,
25 }
26}
27
28fn is_fully_anchored(node: &Regex) -> bool {
30 let Regex::Concat(nodes) = node else {
31 return false;
32 };
33 let first = nodes.first().map(|n| matches!(n, Regex::Anchor(re_parser::ast::Anchor::Start)));
34 let last = nodes.last().map(|n| matches!(n, Regex::Anchor(re_parser::ast::Anchor::End)));
35 first.unwrap_or(false) && last.unwrap_or(false)
36}
37
38fn named_groups(node: &Regex, out: &mut Vec<String>) {
40 match node {
41 Regex::Group(inner, GroupKind::Named(name)) => {
42 out.push(name.clone());
43 named_groups(inner, out);
44 }
45 Regex::Group(inner, _) => named_groups(inner, out),
46 Regex::Quantifier(inner, _, _) => named_groups(inner, out),
47 Regex::Concat(nodes) | Regex::Alternation(nodes) => {
48 for n in nodes {
49 named_groups(n, out);
50 }
51 }
52 _ => {}
53 }
54}
55
56fn report(label: &str, pattern: &str) {
57 println!("── {label} ──");
58 println!(" pattern : {pattern}");
59
60 match parse(pattern) {
61 Err(e) => println!(" ERROR : {e}"),
62 Ok(ast) => {
63 let captures = count_captures(&ast);
64 let anchored = is_fully_anchored(&ast);
65 let mut names = Vec::new();
66 named_groups(&ast, &mut names);
67
68 println!(" captures : {captures}");
69 println!(" anchored : {anchored}");
70 if !names.is_empty() {
71 println!(" named : {}", names.join(", "));
72 }
73 }
74 }
75 println!();
76}
77
78fn main() {
79 report(
80 "IPv4 address",
81 r"^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$",
82 );
83
84 report(
85 "ISO 8601 date",
86 r"^(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})$",
87 );
88
89 report(
90 "Email (simplified)",
91 r"^[\w.+-]+@[\w-]+\.[\w.]+$",
92 );
93
94 report(
95 "Semantic version",
96 r"^(?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)(?:-(?P<pre>[a-zA-Z0-9.]+))?$",
97 );
98
99 report(
100 "HTTP method",
101 r"^(?:GET|POST|PUT|PATCH|DELETE|HEAD|OPTIONS)$",
102 );
103
104 report(
105 "Hex colour",
106 r"^#(?:[0-9a-fA-F]{3}){1,2}$",
107 );
108
109 report(
110 "URL (basic)",
111 r"^(?P<scheme>https?)://(?P<host>[^/]+)(?P<path>/[^\s]*)?$",
112 );
113
114 report("Invalid escape", r"^\zip$");
116}