|
1 | 1 | use crate::fields::Field; |
2 | 2 | use crate::nom_packages::apply_nom_namespaces; |
3 | 3 | use itertools::Itertools; |
| 4 | +use proc_macro::Span; |
4 | 5 | use proc_macro2::TokenStream; |
5 | 6 | use quote::{quote, ToTokens}; |
6 | 7 | use syn::parse::{Parse, ParseStream}; |
7 | 8 | use syn::{parse, Error, Expr, LitStr, Token}; |
8 | 9 |
|
9 | 10 | pub enum ParseSettings { |
10 | | - Split(Expr), |
| 11 | + Split { |
| 12 | + prefix: Option<Expr>, |
| 13 | + split: Expr, |
| 14 | + suffix: Option<Expr>, |
| 15 | + }, |
11 | 16 | Match(LitStr), |
12 | 17 | } |
13 | 18 |
|
14 | 19 | impl ParseSettings { |
15 | 20 | pub fn from(attrs: TokenStream) -> syn::Result<Self> { |
16 | | - let arguments = parse::<Arguments>(attrs.into())?; |
17 | | - |
18 | | - match arguments { |
19 | | - Arguments::Split { expr, .. } => Ok(ParseSettings::Split(expr)), |
20 | | - Arguments::Match { lit, .. } => Ok(ParseSettings::Match(lit)), |
21 | | - Arguments::LiteralMatch { lit } => Ok(ParseSettings::Match(lit)), |
22 | | - } |
| 21 | + parse::<ParseSettings>(attrs.into()) |
23 | 22 | } |
24 | 23 |
|
25 | 24 | pub fn generate_parse_expressions(&self, fields: &[Field]) -> Vec<TokenStream> { |
26 | 25 | match self { |
27 | | - ParseSettings::Split(expr) => { |
28 | | - let mut expr = expr.clone(); |
29 | | - apply_nom_namespaces(&mut expr); |
30 | | - let expr = quote! { let (input, _) = #expr.parse(input)?; }; |
31 | | - Itertools::intersperse( |
| 26 | + ParseSettings::Split{ prefix, split, suffix} => { |
| 27 | + let mut split = split.clone(); |
| 28 | + apply_nom_namespaces(&mut split); |
| 29 | + |
| 30 | + let mut expressions: Vec<_> = Itertools::intersperse( |
32 | 31 | fields |
33 | 32 | .iter() |
34 | 33 | .map(|field| field.generate_expression().into_token_stream()), |
35 | | - expr, |
| 34 | + quote! { let (input, _) = split.parse(input)?; }, |
36 | 35 | ) |
37 | | - .collect() |
| 36 | + .collect(); |
| 37 | + |
| 38 | + if let Some(prefix) = prefix { |
| 39 | + let mut prefix = prefix.clone(); |
| 40 | + apply_nom_namespaces(&mut prefix); |
| 41 | + expressions.insert(0, quote! { let (input, _) = #prefix.parse(input)?; }); |
| 42 | + } |
| 43 | + |
| 44 | + if let Some(suffix) = suffix { |
| 45 | + let mut suffix = suffix.clone(); |
| 46 | + apply_nom_namespaces(&mut suffix); |
| 47 | + expressions.push(quote! { let (input, _) = #suffix.parse(input)?; }); |
| 48 | + } |
| 49 | + |
| 50 | + expressions.insert(0, quote! { let mut split = #split; }); |
| 51 | + expressions |
38 | 52 | } |
39 | 53 | ParseSettings::Match(literal) => { |
40 | 54 | let value = literal.value(); |
@@ -68,50 +82,57 @@ impl ParseSettings { |
68 | 82 | mod keywords { |
69 | 83 | use syn::custom_keyword; |
70 | 84 |
|
| 85 | + custom_keyword!(prefix); |
71 | 86 | custom_keyword!(split); |
| 87 | + custom_keyword!(suffix); |
72 | 88 | } |
73 | 89 |
|
74 | | -#[allow(dead_code)] |
75 | | -enum Arguments { |
76 | | - Split { |
77 | | - token: keywords::split, |
78 | | - eq_token: Token![=], |
79 | | - expr: Expr, |
80 | | - }, |
81 | | - Match { |
82 | | - token: Token![match], |
83 | | - eq_token: Token![=], |
84 | | - lit: LitStr, |
85 | | - }, |
86 | | - LiteralMatch { |
87 | | - lit: LitStr, |
88 | | - }, |
89 | | -} |
90 | | - |
91 | | -impl Parse for Arguments { |
| 90 | +impl Parse for ParseSettings { |
92 | 91 | fn parse(input: ParseStream) -> syn::Result<Self> { |
93 | 92 | let lookahead = input.lookahead1(); |
94 | | - if lookahead.peek(keywords::split) { |
95 | | - let token = input.parse::<keywords::split>()?; |
96 | | - let eq_token = input.parse::<Token![=]>()?; |
97 | | - let expr = input.parse::<Expr>()?; |
98 | | - Ok(Arguments::Split { |
99 | | - token, |
100 | | - eq_token, |
101 | | - expr, |
102 | | - }) |
103 | | - } else if lookahead.peek(Token![match]) { |
104 | | - let token = input.parse::<Token![match]>()?; |
105 | | - let eq_token = input.parse::<Token![=]>()?; |
| 93 | + if lookahead.peek(LitStr) { |
106 | 94 | let lit = input.parse::<LitStr>()?; |
107 | | - Ok(Arguments::Match { |
108 | | - token, |
109 | | - eq_token, |
110 | | - lit, |
| 95 | + return Ok(ParseSettings::Match(lit)); |
| 96 | + } |
| 97 | + |
| 98 | + let mut prefix: Option<Expr> = None; |
| 99 | + let mut split: Option<Expr> = None; |
| 100 | + let mut suffix: Option<Expr> = None; |
| 101 | + let mut first = true; |
| 102 | + |
| 103 | + while !input.is_empty() { |
| 104 | + if first { |
| 105 | + first = false; |
| 106 | + } else { |
| 107 | + input.parse::<Token![;]>()?; |
| 108 | + } |
| 109 | + |
| 110 | + let lookahead = input.lookahead1(); |
| 111 | + if lookahead.peek(keywords::prefix) { |
| 112 | + input.parse::<keywords::prefix>()?; |
| 113 | + input.parse::<Token![=]>()?; |
| 114 | + prefix = Some(input.parse()?); |
| 115 | + } else if lookahead.peek(keywords::split) { |
| 116 | + input.parse::<keywords::split>()?; |
| 117 | + input.parse::<Token![=]>()?; |
| 118 | + split = Some(input.parse()?); |
| 119 | + } else if lookahead.peek(keywords::suffix) { |
| 120 | + input.parse::<keywords::suffix>()?; |
| 121 | + input.parse::<Token![=]>()?; |
| 122 | + suffix = Some(input.parse()?); |
| 123 | + } else { |
| 124 | + return Err(lookahead.error()); |
| 125 | + } |
| 126 | + } |
| 127 | + |
| 128 | + if let Some(split) = split { |
| 129 | + Ok(ParseSettings::Split { |
| 130 | + prefix, |
| 131 | + split, |
| 132 | + suffix, |
111 | 133 | }) |
112 | 134 | } else { |
113 | | - let lit = input.parse::<LitStr>()?; |
114 | | - Ok(Arguments::LiteralMatch { lit }) |
| 135 | + Err(Error::new(Span::call_site().into(), "Missing `split` keyword")) |
115 | 136 | } |
116 | 137 | } |
117 | 138 | } |
0 commit comments