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>;