lazy_regex_proc_macros/
regex_code.rs1use {
2 proc_macro::TokenStream,
3 proc_macro2::TokenStream as TokenStream2,
4 quote::quote,
5 syn::LitStr,
6};
7
8pub(crate) struct RegexCode {
11 pub build: TokenStream2,
12 pub regex: RegexInstance,
13}
14
15pub(crate) enum RegexInstance {
16 Regex(regex::Regex),
17 Bytes(regex::bytes::Regex),
18}
19
20impl RegexCode {
21 pub fn from_token_stream(token_stream: TokenStream, is_bytes: bool) -> Result<Self, syn::Error> {
22 Self::from_lit_str(syn::parse::<syn::LitStr>(token_stream)?, is_bytes)
23 }
24 pub fn from_lit_str(lit_str: LitStr, mut is_bytes: bool) -> Result<Self, syn::Error> {
25 let pattern = lit_str.value();
26 let mut case_insensitive = false;
27 let mut multi_line = false;
28 let mut dot_matches_new_line = false;
29 let mut ignore_whitespace = false;
30 let mut swap_greed = false;
31 for (i, ch) in lit_str.suffix().chars().enumerate() {
32 match ch {
33 'i' => case_insensitive = true,
34 'm' => multi_line = true,
35 's' => dot_matches_new_line = true,
36 'x' => ignore_whitespace = true,
37 'U' => swap_greed = true,
38 'B' => is_bytes = true, _ => {
40 let lit = lit_str.token();
41 let pos = lit.to_string().len() - i;
42 return Err(syn::Error::new(
44 lit.subspan(pos - 1..pos).unwrap_or_else(|| lit.span()),
45 format!("unrecognized regex flag {:?}", ch),
46 ));
47 }
48 };
49 }
50
51 let regex = if is_bytes {
52 regex::bytes::Regex::new(&pattern).map(RegexInstance::Bytes)
53 } else {
54 regex::Regex::new(&pattern).map(RegexInstance::Regex)
55 };
56 let regex = regex.map_err(|e| syn::Error::new(lit_str.span(), e.to_string()))?;
57
58 let builder_token = if is_bytes {
59 quote!(BytesRegexBuilder)
60 } else {
61 quote!(RegexBuilder)
62 };
63 let build = quote! {
64 lazy_regex::Lazy::new(|| {
65 lazy_regex:: #builder_token ::new(#pattern)
67 .case_insensitive(#case_insensitive)
68 .multi_line(#multi_line)
69 .dot_matches_new_line(#dot_matches_new_line)
70 .ignore_whitespace(#ignore_whitespace)
71 .swap_greed(#swap_greed)
72 .build()
73 .unwrap()
74 })
75 };
76 Ok(Self { build, regex })
77 }
78}
79
80impl RegexCode {
81 pub fn statick(&self) -> TokenStream2 {
82 let build = &self.build;
83 let regex_token = match self.regex {
84 RegexInstance::Regex(..) => quote!(Regex),
85 RegexInstance::Bytes(..) => quote!(BytesRegex),
86 };
87 quote! {
88 static RE: lazy_regex::Lazy<lazy_regex:: #regex_token > = #build;
89 }
90 }
91
92 pub fn lazy_static(&self) -> TokenStream2 {
93 let statick = self.statick();
94 quote! {{
95 #statick;
96 &RE
97 }}
98 }
99
100 pub fn captures_len(&self) -> usize {
101 match &self.regex {
102 RegexInstance::Regex(regex) => regex.captures_len(),
103 RegexInstance::Bytes(regex) => regex.captures_len(),
104 }
105 }
106 pub fn named_groups(&self) -> Vec<(usize, &str)> {
107 match &self.regex {
108 RegexInstance::Regex(regex) => regex
109 .capture_names()
110 .enumerate()
111 .filter_map(|(i, n)| Some((i, n?)))
112 .collect(),
113 RegexInstance::Bytes(regex) => regex
114 .capture_names()
115 .enumerate()
116 .filter_map(|(i, n)| Some((i, n?)))
117 .collect(),
118 }
119 }
120}