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}