syn_utils/parsers/
mod.rs

1use syn::{token::Comma, RangeLimits};
2
3use crate::*;
4
5#[derive(Debug, Clone)]
6pub struct ClosedRangeList {
7  pub list: Vec<Range<i32>>,
8}
9
10impl Parse for ClosedRangeList {
11  fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
12    let mut ranges: Vec<Range<i32>> = Vec::new();
13
14    while !input.is_empty() {
15      let item: Expr = input.parse()?;
16
17      if let Expr::Range(range_expr) = &item {
18        let start = if let Some(start_expr) = &range_expr.start {
19          start_expr.as_int::<i32>()?
20        } else {
21          return Err(input.error("Expected a defined start for this range"));
22        };
23
24        if let Some(end_expr) = &range_expr.end {
25          let mut end = end_expr.as_int::<i32>()?;
26
27          if let RangeLimits::Closed(_) = &range_expr.limits {
28            end += 1;
29          }
30
31          ranges.push(start..end)
32        } else {
33          return Err(input.error("Expected a closed range"));
34        }
35      } else if let Expr::Lit(lit) = &item && let Lit::Int(lit_int) = &lit.lit {
36        let num = lit_int.base10_parse::<i32>()?;
37
38        ranges.push(num..num + 1);
39      } else {
40        return Err(error!(
41          item,
42          "Expected a range (e.g. `1..5`, `10..=15`) or a single number"
43        ));
44      }
45
46      if input.is_empty() {
47        break;
48      }
49
50      let _comma: Comma = input.parse()?;
51    }
52
53    ranges.sort_by_key(|range| range.start);
54
55    Ok(Self { list: ranges })
56  }
57}
58
59#[derive(Debug, Clone)]
60pub enum GenericRange {
61  Open(RangeFrom<i32>),
62  Closed(Range<i32>),
63}
64
65#[derive(Debug, Clone)]
66pub struct GenericRangeList {
67  pub list: Vec<GenericRange>,
68}
69
70impl Parse for GenericRangeList {
71  fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
72    let mut ranges: Vec<GenericRange> = Vec::new();
73
74    while !input.is_empty() {
75      let item: Expr = input.parse()?;
76
77      if let Expr::Range(range_expr) = &item {
78        let start = if let Some(start_expr) = &range_expr.start {
79          start_expr.as_int::<i32>()?
80        } else {
81          return Err(input.error("Expected a defined start for this range"));
82        };
83
84        if let Some(end_expr) = &range_expr.end {
85          let mut end = end_expr.as_int::<i32>()?;
86
87          if let RangeLimits::Closed(_) = &range_expr.limits {
88            end += 1;
89          }
90          ranges.push(GenericRange::Closed(start..end))
91        } else {
92          ranges.push(GenericRange::Open(start..))
93        }
94      } else if let Expr::Lit(lit) = &item && let Lit::Int(lit_int) = &lit.lit {
95        let num = lit_int.base10_parse::<i32>()?;
96
97        ranges.push(GenericRange::Closed(num..num + 1));
98      } else {
99        return Err(error!(
100          item,
101          "Expected a range (e.g. `1..5`, `10..=15`) or a single number"
102        ));
103      }
104
105      if input.is_empty() {
106        break;
107      }
108
109      let _comma: Comma = input.parse()?;
110    }
111
112    Ok(Self { list: ranges })
113  }
114}
115
116#[derive(Debug, Clone)]
117pub enum PathOrClosure {
118  Path(TokenStream2),
119  Closure(TokenStream2),
120}
121
122impl ToTokens for PathOrClosure {
123  fn to_tokens(&self, tokens: &mut TokenStream2) {
124    match self {
125      PathOrClosure::Path(path) => path.to_tokens(tokens),
126      PathOrClosure::Closure(expr_closure) => expr_closure.to_tokens(tokens),
127    }
128  }
129}
130
131#[derive(Debug, Clone)]
132pub enum CallOrClosure {
133  Call(TokenStream2),
134  Closure(TokenStream2),
135}
136
137impl ToTokens for CallOrClosure {
138  fn to_tokens(&self, tokens: &mut TokenStream2) {
139    match self {
140      CallOrClosure::Call(call) => call.to_tokens(tokens),
141      CallOrClosure::Closure(expr_closure) => expr_closure.to_tokens(tokens),
142    }
143  }
144}
145
146pub struct PunctuatedItems<T: Parse + ToTokens> {
147  pub list: Vec<T>,
148}
149
150pub type PathList = PunctuatedItems<Path>;
151pub type IdentList = PunctuatedItems<Ident>;
152
153impl<T: Parse + ToTokens> Parse for PunctuatedItems<T> {
154  fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
155    let mut inner = Vec::new();
156
157    while !input.is_empty() {
158      inner.push(input.parse()?);
159
160      if input.is_empty() {
161        break;
162      }
163      let _comma: Comma = input.parse()?;
164    }
165
166    Ok(Self { list: inner })
167  }
168}
169
170impl<T: Parse + ToTokens> ToTokens for PunctuatedItems<T> {
171  fn to_tokens(&self, tokens: &mut TokenStream2) {
172    let list = &self.list;
173
174    let output = quote! { #(#list),* };
175
176    tokens.extend(output)
177  }
178}
179
180pub struct StringList {
181  pub list: Vec<String>,
182}
183
184impl ToTokens for StringList {
185  fn to_tokens(&self, tokens: &mut TokenStream2) {
186    let list = &self.list;
187
188    let output = quote! { #(#list),* };
189
190    tokens.extend(output)
191  }
192}
193
194impl Parse for StringList {
195  fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
196    let mut list: Vec<String> = Vec::new();
197
198    while !input.is_empty() {
199      list.push(input.parse::<LitStr>()?.value());
200
201      if input.is_empty() {
202        break;
203      }
204      let _comma: Comma = input.parse()?;
205    }
206
207    Ok(Self { list })
208  }
209}
210
211pub struct NumList {
212  pub list: Vec<i32>,
213}
214
215impl ToTokens for NumList {
216  fn to_tokens(&self, tokens: &mut TokenStream2) {
217    let list = self
218      .list
219      .iter()
220      .map(|n| proc_macro2::Literal::i32_unsuffixed(*n));
221
222    let output = quote! { #(#list),* };
223
224    tokens.extend(output)
225  }
226}
227
228impl Parse for NumList {
229  fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
230    let mut list: Vec<i32> = Vec::new();
231
232    while !input.is_empty() {
233      list.push(input.parse::<LitInt>()?.base10_parse()?);
234
235      if input.is_empty() {
236        break;
237      }
238      let _comma: Comma = input.parse()?;
239    }
240
241    Ok(Self { list })
242  }
243}