parse_display/
helpers_std.rs

1use core::mem;
2use std::borrow::Cow;
3use std::collections::HashMap;
4
5use regex::Regex;
6use regex_syntax::ast::{Ast, Flags, GroupKind};
7
8use crate::FromStrFormat;
9
10pub use regex;
11
12#[track_caller]
13pub fn to_regex<T, E>(format: &dyn FromStrFormat<T, Err = E>) -> Option<String> {
14    format.regex()
15}
16
17#[track_caller]
18pub fn to_ast<T, E>(format: &dyn FromStrFormat<T, Err = E>) -> Option<(String, Ast)> {
19    let s = format.regex()?;
20    let ast = ast_from_str(&s);
21    Some((s, ast))
22}
23
24#[track_caller]
25fn ast_from_str(s: &str) -> Ast {
26    let Ok(mut ast) = regex_syntax::ast::parse::Parser::new().parse(s) else {
27        panic!("invalid regex: {s}")
28    };
29    let e = replace_ast(&mut ast, &mut |ast| {
30        if let Ast::Group(g) = ast {
31            match &g.kind {
32                GroupKind::CaptureIndex(_) => {
33                    g.kind = GroupKind::NonCapturing(Flags {
34                        span: g.span,
35                        items: vec![],
36                    });
37                }
38                GroupKind::CaptureName { name, .. } => {
39                    return Err(format!(
40                        "named capture group is not supported: `{}`",
41                        name.name
42                    ));
43                }
44                GroupKind::NonCapturing(_) => {}
45            }
46        }
47        Ok(true)
48    });
49    if let Err(e) = e {
50        panic!("{e}");
51    }
52    ast
53}
54
55pub struct Parser {
56    pub re: Regex,
57    pub ss: Vec<Option<String>>,
58}
59impl Parser {
60    #[track_caller]
61    pub fn new(s: &str, with: &mut [(&str, Option<(String, Ast)>)]) -> Self {
62        let mut asts: HashMap<&str, &Ast> = HashMap::new();
63        let mut ss = Vec::new();
64        for (capture_name, item) in with {
65            if let Some((item_s, item_ast)) = item {
66                asts.insert(capture_name, item_ast);
67                ss.push(Some(mem::take(item_s)));
68            } else {
69                ss.push(None);
70            }
71        }
72        let re = if asts.is_empty() {
73            Cow::Borrowed(s)
74        } else {
75            let mut ast = regex_syntax::ast::parse::Parser::new().parse(s).unwrap();
76            let e = replace_ast(&mut ast, &mut |ast| {
77                if let Ast::Group(g) = ast {
78                    if let GroupKind::CaptureName { name, .. } = &g.kind {
79                        if let Some(ast) = asts.get(name.name.as_str()) {
80                            g.ast = Box::new((*ast).clone());
81                            return Ok(false);
82                        }
83                    }
84                }
85                Ok(true)
86            });
87            if let Err(e) = e {
88                panic!("{e}");
89            }
90            Cow::Owned(ast.to_string())
91        };
92        Self {
93            re: Regex::new(&re).unwrap(),
94            ss,
95        }
96    }
97}
98
99#[track_caller]
100pub fn build_regex(s: &str, with: &[(&str, Option<Ast>)]) -> Regex {
101    let with: HashMap<&str, &Ast> = with
102        .iter()
103        .filter_map(|(name, ast)| Some((*name, ast.as_ref()?)))
104        .collect();
105    let re = if with.is_empty() {
106        Cow::Borrowed(s)
107    } else {
108        let mut ast = regex_syntax::ast::parse::Parser::new().parse(s).unwrap();
109        let e = replace_ast(&mut ast, &mut |ast| {
110            if let Ast::Group(g) = ast {
111                if let GroupKind::CaptureName { name, .. } = &g.kind {
112                    if let Some(ast) = with.get(name.name.as_str()) {
113                        g.ast = Box::new((*ast).clone());
114                        return Ok(false);
115                    }
116                }
117            }
118            Ok(true)
119        });
120        if let Err(e) = e {
121            panic!("{e}");
122        }
123        Cow::Owned(ast.to_string())
124    };
125    Regex::new(&re).unwrap()
126}
127
128fn replace_asts(
129    asts: &mut Vec<Ast>,
130    f: &mut impl FnMut(&mut Ast) -> ReplaceAstResult<bool>,
131) -> ReplaceAstResult {
132    for ast in asts {
133        replace_ast(ast, f)?;
134    }
135    Ok(())
136}
137
138fn replace_ast(
139    ast: &mut Ast,
140    f: &mut impl FnMut(&mut Ast) -> ReplaceAstResult<bool>,
141) -> ReplaceAstResult {
142    if !f(ast)? {
143        return Ok(());
144    }
145    match ast {
146        Ast::Empty(..)
147        | Ast::Flags(..)
148        | Ast::Literal(..)
149        | Ast::Dot(..)
150        | Ast::Assertion(..)
151        | Ast::ClassUnicode(..)
152        | Ast::ClassPerl(..)
153        | Ast::ClassBracketed(..) => Ok(()),
154        Ast::Repetition(rep) => replace_ast(&mut rep.ast, f),
155        Ast::Group(g) => replace_ast(&mut g.ast, f),
156        Ast::Alternation(alt) => replace_asts(&mut alt.asts, f),
157        Ast::Concat(c) => replace_asts(&mut c.asts, f),
158    }
159}
160
161type ReplaceAstResult<T = ()> = Result<T, String>;