parse_more/lib.rs
1//! # parse-more
2//! Parse-more is an extension of the [syn::parse::Parse] trait from the [syn](https://docs.rs/syn/) crate, allowing to parse input from procedural macros directly, without having to create a custom structure and implementing the [syn::parse::Parse] trait on it.
3//!
4//! It provides classic [syn](https://docs.rs/syn/) macros and functions variant, using the [ParseMore] trait instead.
5//!
6//! # Example
7//!
8//! ```
9//! # extern crate proc_macro;
10//! use quote::quote;
11//! use parse_more::{parse_more_macro_input, Concat, Braced};
12//! use proc_macro::TokenStream;
13//! use syn::{Expr, Ident, Token, punctuated::Punctuated};
14//!
15//! # const IGNORE_TOKENS: &str = stringify! {
16//! #[proc_macro]
17//! # };
18//! pub fn complex_args(input: TokenStream) -> TokenStream {
19//! let content = parse_more_macro_input!(
20//! input as Punctuated<Concat<(Ident, Token![=>], Braced<(Ident, Expr)>)>, Token![,]>
21//! );
22//! content
23//! .into_iter()
24//! .map(|concat| {
25//! // Second item is discarded (it's the => arrow)
26//! let (ident, _, Braced((other_ident, literal))) = concat.value();
27//! quote! {
28//! println!("{}: {} versus other type {}", #literal, (-1i8) as #ident, (-1i8) as #other_ident);
29//! }
30//! })
31//! .collect::<proc_macro2::TokenStream>()
32//! .into()
33//! }
34//! ```
35//! And then :
36//! ```
37//! # macro_rules! complex_args { ($($tt:tt)*) => {} }
38//! complex_args! {
39//! u8 => {
40//! (i8, "u8 integer")
41//! },
42//! u16 => {
43//! (i16, "u16 integer")
44//! },
45//! Foo => {
46//! (Bar, 8 + 42)
47//! }
48//! }
49//! ```
50
51extern crate proc_macro;
52
53pub mod syn_types;
54
55use syn::{braced, bracketed, parenthesized, parse::Parse, punctuated::Punctuated};
56
57/// Parsing interface implemented by all types from the [syn] crate which already implement [syn::parse::Parse], and some others usefull ones.
58/// Use the [parse_more_auto_impl] macros to easily implement [ParseMore] on a type which already implement [syn::parse::Parse].
59pub trait ParseMore: Sized {
60 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self>;
61}
62
63/// This wrapper implements [syn::parse::Parse] when T implements [ParseMore]. It permits to use this wrapper and a type implementing [ParseMore] in a parsing function from [syn].
64///
65/// You should probably use [parse_more], [parse_more_macro_input], or any other function from this crate instead of using this type directly.
66///
67/// # Example
68///
69/// ```
70/// # extern crate proc_macro;
71/// use quote::quote;
72/// use parse_more::ParseMoreWrapper;
73/// use proc_macro::TokenStream;
74/// use syn::{Ident, Token};
75///
76/// # const IGNORE_TOKENS: &str = stringify! {
77/// #[proc_macro]
78/// # };
79/// pub fn flip_identifiers(input: TokenStream) -> TokenStream {
80/// let (ident_a, arrow, ident_b) = syn::parse::<ParseMoreWrapper<(Ident, Token![=>], Ident)>>(input).unwrap().0;
81/// quote! {
82/// #ident_b <= #ident_a
83/// }.into()
84/// }
85/// ```
86pub struct ParseMoreWrapper<T>(pub T);
87
88/// Call the [ParseMore::parse] method.
89impl<T: ParseMore> Parse for ParseMoreWrapper<T> {
90 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
91 Ok(ParseMoreWrapper(ParseMore::parse(input)?))
92 }
93}
94
95/// This macro auto-implements the [ParseMore] traits on its arguments, which *MUST* implement the [syn::parse::Parse] trait.
96/// It allows using custom types inside of any parse-more macros/functions.
97///
98/// FIXME: create a derive macro to handle this.
99///
100/// # Example
101///
102/// ```
103/// use parse_more::parse_more_auto_impl;
104/// use syn::{Ident, Token};
105///
106/// struct MyParsedStruct(Ident, Ident);
107///
108/// impl syn::parse::Parse for MyParsedStruct {
109/// fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
110/// let a = input.parse()?;
111/// input.parse::<Token![=>]>()?;
112/// let b = input.parse()?;
113/// Ok(Self(a, b))
114/// }
115/// }
116///
117/// parse_more_auto_impl! {
118/// MyParsedStruct
119/// }
120/// ```
121#[macro_export]
122macro_rules! parse_more_auto_impl {
123 ($($ty:ty),*) => {
124 $(impl $crate::ParseMore for $ty {
125 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
126 <Self as syn::parse::Parse>::parse(input)
127 }
128 })*
129 }
130}
131
132/// Implement [ParseMore] for a given tuple.
133macro_rules! tuple_impls {
134 ($first:ident) => {
135 impl<$first:ParseMore> ParseMore for ($first, ) {
136 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
137 let content;
138 parenthesized!(content in input);
139 let first = content.parse::<ParseMoreWrapper<$first>>()?.0;
140 content.parse::<syn::Token![,]>()?;
141 Ok((first,))
142 }
143 }
144 };
145 ($first:ident $($generics:ident)*) => {
146 impl<$first:ParseMore, $($generics: ParseMore),*> ParseMore for ($first, $($generics,)*) {
147 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
148 let content;
149 parenthesized!(content in input);
150 let first = content.parse::<ParseMoreWrapper<$first>>()?.0;
151 let res = Ok((first,
152 $({
153 content.parse::<syn::Token![,]>()?;
154 content.parse::<ParseMoreWrapper<$generics>>()?.0
155 },)*
156 ));
157
158 // If there is a remaining comma, parse it
159 if content.peek(syn::Token![,]) {
160 content.parse::<syn::Token![,]>().unwrap();
161 }
162 res
163 }
164 }
165 };
166}
167
168/// Inner code of the [for_each_tuple] macro.
169macro_rules! for_each_tuple_ {
170 ($mac:ident =>) => {};
171 ($mac:ident => $first:ident, $($generics:ident,)*) => {
172 $mac! {
173 $first $($generics)*
174 }
175 for_each_tuple_ ! {
176 $mac => $($generics,)*
177 }
178 };
179}
180
181/// Call a macro over every tuple size between 1 and 20.
182macro_rules! for_each_tuple {
183 ($mac:ident) => {
184 for_each_tuple_! {
185 $mac => A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T,
186 }
187 };
188}
189
190// Generate [ParseMore] impls for tuples.
191for_each_tuple! {
192 tuple_impls
193}
194
195/// Implement [ParseMore] for the array type.
196/// ```
197/// # extern crate proc_macro;
198/// use quote::quote;
199/// use parse_more::parse_more_macro_input;
200/// use proc_macro::TokenStream;
201/// use syn::{Ident, Token};
202///
203/// # const IGNORE_TOKENS: &str = stringify! {
204/// #[proc_macro]
205/// # };
206/// pub fn identifiers_sum(input: TokenStream) -> TokenStream {
207/// let idents = parse_more_macro_input!(input as [Ident; 3]);
208/// let [a, b, c] = idents;
209/// quote! {
210/// #a + #b + #c
211/// }.into()
212/// }
213/// ```
214impl<T: ParseMore, const N: usize> ParseMore for [T; N] {
215 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
216 let content;
217 bracketed!(content in input);
218
219 let mut res = vec![];
220 for i in 0..N {
221 if i != 0 {
222 content.parse::<syn::Token![,]>()?;
223 }
224 res.push(content.parse::<ParseMoreWrapper<T>>()?.0)
225 }
226
227 // If there is a remaining comma, parse it
228 if content.peek(syn::Token![,]) {
229 content.parse::<syn::Token![,]>().unwrap();
230 }
231 Ok(unsafe { res.try_into().unwrap_unchecked() })
232 }
233}
234
235/// Implement [ParseMore] for the [Vec] type, with a behaviour similar to [syn::punctuated::Punctuated<T, syn::parse::Nothing>]
236/// ```
237/// # extern crate proc_macro;
238/// use quote::quote;
239/// use parse_more::parse_more_macro_input;
240/// use proc_macro::TokenStream;
241/// use syn::Ident;
242///
243/// # const IGNORE_TOKENS: &str = stringify! {
244/// #[proc_macro]
245/// # };
246/// pub fn identifiers_sum(input: TokenStream) -> TokenStream {
247/// let idents = parse_more_macro_input!(input as Vec<Ident>);
248/// idents.into_iter().map(|ident| quote! {
249/// #ident
250/// }).collect::<proc_macro2::TokenStream>().into()
251/// }
252/// ```
253impl<T: ParseMore> ParseMore for Vec<T> {
254 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
255 let mut res = vec![];
256 while let Ok(parsed) = input.parse::<ParseMoreWrapper<T>>() {
257 res.push(parsed.0);
258 }
259 Ok(res)
260 }
261}
262
263/// Implement [ParseMore] for the [syn::punctuated::Punctuated] type.
264/// ```
265/// # extern crate proc_macro;
266/// use quote::quote;
267/// use parse_more::parse_more_macro_input;
268/// use proc_macro::TokenStream;
269/// use syn::{Ident, Token, punctuated::Punctuated};
270///
271/// # const IGNORE_TOKENS: &str = stringify! {
272/// #[proc_macro]
273/// # };
274/// pub fn identifiers_sum(input: TokenStream) -> TokenStream {
275/// let idents = parse_more_macro_input!(input as Punctuated<Ident, Token![,]>);
276/// idents.into_iter().map(|ident| quote! {
277/// #ident
278/// }).collect::<proc_macro2::TokenStream>().into()
279/// }
280/// ```
281impl<T: ParseMore, P: ParseMore> ParseMore for Punctuated<T, P> {
282 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
283 let mut res = Punctuated::new();
284 let wrapped =
285 Punctuated::<ParseMoreWrapper<T>, ParseMoreWrapper<P>>::parse_terminated(input)?;
286
287 // Unwrap parsed values
288 wrapped.into_pairs().for_each(|pair| {
289 let pair = pair.into_tuple();
290 res.push_value(pair.0 .0);
291 if let Some(punct) = pair.1 {
292 res.push_punct(punct.0);
293 }
294 });
295
296 Ok(res)
297 }
298}
299
300/// Parse successive items.
301#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
302pub struct Concat<T>(pub T);
303
304/// Imlement [ParseMore] for the [Concat] type.
305macro_rules! concat_impls {
306 ($($generics:ident)*) => {
307 impl<$($generics: ParseMore),*> ParseMore for Concat<($($generics,)*)> {
308 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
309 Ok(Self((
310 $(input.parse::<ParseMoreWrapper<$generics>>()?.0,)*
311 )))
312 }
313 }
314 };
315}
316
317impl<T> Concat<T> {
318 /// Get the parsed value, as a tuple containing all items.
319 pub fn value(self) -> T {
320 self.0
321 }
322}
323
324for_each_tuple! { concat_impls }
325
326/// Parse an item surrounded by braces.
327#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
328pub struct Braced<T>(pub T);
329
330/// Imlement [ParseMore] for the [Braced] type.
331impl<T: ParseMore> ParseMore for Braced<T> {
332 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
333 let content;
334 braced!(content in input);
335 Ok(Self(content.parse::<ParseMoreWrapper<T>>()?.0))
336 }
337}
338
339impl<T> Braced<T> {
340 /// Get the parsed value.
341 pub fn value(self) -> T {
342 self.0
343 }
344}
345
346/// Parse an item surrounded by parenthesis.
347#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
348pub struct Parenthesized<T>(pub T);
349
350/// Imlement [ParseMore] for the [Parenthesized] type.
351impl<T: ParseMore> ParseMore for Parenthesized<T> {
352 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
353 let content;
354 parenthesized!(content in input);
355 Ok(Self(content.parse::<ParseMoreWrapper<T>>()?.0))
356 }
357}
358
359impl<T> Parenthesized<T> {
360 /// Get the parsed value.
361 pub fn value(self) -> T {
362 self.0
363 }
364}
365
366/// Parse an item surrounded by brackets.
367#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
368pub struct Bracketed<T>(pub T);
369
370/// Imlement [ParseMore] for the [Bracketed] type.
371impl<T: ParseMore> ParseMore for Bracketed<T> {
372 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
373 let content;
374 bracketed!(content in input);
375 Ok(Self(content.parse::<ParseMoreWrapper<T>>()?.0))
376 }
377}
378
379impl<T> Bracketed<T> {
380 /// Get the parsed value.
381 pub fn value(self) -> T {
382 self.0
383 }
384}
385
386/// Same as the [syn::parse_quote] macro, but using the [ParseMore] trait.
387#[macro_export]
388macro_rules! parse_more_quote {
389 ($($tt:tt)*) => {{
390 // Add an type hint for the compiler
391 let __tmp: $crate::ParseMoreWrapper<_> = syn::parse_quote!($($tt:tt)*);
392 __tmp.0
393 }};
394}
395
396/// Same as the [syn::parse_macro_input] macro, but using the [ParseMore] trait.
397#[macro_export]
398macro_rules! parse_more_macro_input {
399 ($tokenstream:ident as $ty:ty) => {
400 match $crate::parse_more::<$ty>($tokenstream) {
401 Ok(data) => data,
402 Err(err) => {
403 return proc_macro::TokenStream::from(err.to_compile_error());
404 }
405 }
406 };
407 ($tokenstream:ident) => {
408 $crate::parse_more_macro_input!($tokenstream as _)
409 };
410}
411
412/// Same as the [syn::parse()] function, but using the [ParseMore] trait.
413pub fn parse_more<T: ParseMore>(tokens: proc_macro::TokenStream) -> syn::Result<T> {
414 Ok(syn::parse::<ParseMoreWrapper<T>>(tokens)?.0)
415}
416
417/// Same as the [syn::parse2] function, but using the [ParseMore] trait.
418pub fn parse2_more<T: ParseMore>(tokens: proc_macro2::TokenStream) -> syn::Result<T> {
419 Ok(syn::parse2::<ParseMoreWrapper<T>>(tokens)?.0)
420}
421
422/// Same as the [syn::parse_str] function, but using the [ParseMore] trait.
423pub fn parse_more_str<T: ParseMore>(s: &str) -> syn::Result<T> {
424 Ok(syn::parse_str::<ParseMoreWrapper<T>>(s)?.0)
425}