synthez_core/parse/
ext.rs

1//! Extensions for [`syn::parse`] types.
2//!
3//! [`syn::parse`]: mod@syn::parse
4
5use std::{any::TypeId, iter};
6
7use proc_macro2::Span;
8use sealed::sealed;
9use syn::{
10    parse::Parse,
11    punctuated::Punctuated,
12    token::{self, Token},
13};
14
15/// Extension of a [`syn::parse::ParseBuffer`] providing common function widely
16/// used by this crate for parsing.
17#[sealed]
18pub trait ParseBuffer {
19    /// Tries to parse `T` as the next [`Token`].
20    ///
21    /// Doesn't move [`ParseBuffer`]'s cursor if there is no `T`.
22    ///
23    /// # Errors
24    ///
25    /// If `T` fails to be parsed.
26    fn try_parse<T: Default + Parse + Token>(&self) -> syn::Result<Option<T>>;
27
28    /// Checks whether the next [`Token`] is `T`.
29    ///
30    /// Doesn't move [`ParseBuffer`]'s cursor.
31    #[must_use]
32    fn is_next<T: Default + Token>(&self) -> bool;
33
34    /// Parses the next [`Token`] as [`syn::Ident`] _allowing_ Rust keywords,
35    /// while default [`Parse`] implementation for [`syn::Ident`] disallows
36    /// them.
37    ///
38    /// Always moves [`ParseBuffer`]'s cursor.
39    ///
40    /// # Errors
41    ///
42    /// If [`syn::Ident`] fails to be parsed.
43    ///
44    /// [`syn::Ident`]: struct@syn::Ident
45    fn parse_any_ident(&self) -> syn::Result<syn::Ident>;
46
47    /// Parses the next [`Token`] as [`syn::Ident`] _allowing_ Rust keywords,
48    /// while default [`Parse`] implementation for [`syn::Ident`] disallows
49    /// them. Drops the parsed [`Token`] in-place.
50    ///
51    /// Always moves [`ParseBuffer`]'s cursor.
52    ///
53    /// # Errors
54    ///
55    /// If [`syn::Ident`] fails to be parsed.
56    ///
57    /// [`syn::Ident`]: struct@syn::Ident
58    fn skip_any_ident(&self) -> syn::Result<()> {
59        self.parse_any_ident().map(drop)
60    }
61
62    /// Parses the wrapped (in a wrapper `W`) [`Token`]s as `T` [`Punctuated`]
63    /// with a `P` separator.
64    ///
65    /// Always moves [`ParseBuffer`]'s cursor.
66    ///
67    /// # Errors
68    ///
69    /// If parsing [`Punctuated`] `T` wrapped into `W` fails.
70    fn parse_wrapped_and_punctuated<T, W, P>(
71        &self,
72    ) -> syn::Result<Punctuated<T, P>>
73    where
74        T: Parse,
75        W: Default + Token + AcceptedWrapper + 'static,
76        P: Default + Parse + Token;
77
78    /// Checks whether the next [`Token`] is a wrapper `W` and if yes, then
79    /// parses the wrapped [`Token`]s as `T` [`Punctuated`] with a `P`
80    /// separator. Otherwise, parses just `T`.
81    ///
82    /// Always moves [`ParseBuffer`]'s cursor.
83    ///
84    /// # Errors
85    ///
86    /// If either parsing [`Punctuated`] `T` wrapped into `W`, or parsing just
87    /// `T`, fails.
88    fn parse_maybe_wrapped_and_punctuated<T, W, P>(
89        &self,
90    ) -> syn::Result<Punctuated<T, P>>
91    where
92        T: Parse,
93        W: Default + Token + AcceptedWrapper + 'static,
94        P: Default + Parse + Token;
95
96    /// Checks whether the next [`Token`] is a wrapper `W` and if yes, then
97    /// parses the wrapped [`Token`]s as `T` [`Punctuated`] with a `P`
98    /// separator. Otherwise, parses just `T` following the [`token::Eq`].
99    ///
100    /// Always moves [`ParseBuffer`]'s cursor.
101    ///
102    /// # Errors
103    ///
104    /// If either parsing [`Punctuated`] `T` wrapped into `W`, or parsing just
105    /// `T` following the [`token::Eq`], fails.
106    ///
107    /// [`token::Eq`]: struct@token::Eq
108    fn parse_eq_or_wrapped_and_punctuated<T, W, P>(
109        &self,
110    ) -> syn::Result<Punctuated<T, P>>
111    where
112        T: Parse,
113        W: Default + Token + AcceptedWrapper + 'static,
114        P: Default + Parse + Token;
115}
116
117#[sealed]
118impl<'buf> ParseBuffer for syn::parse::ParseBuffer<'buf> {
119    fn try_parse<T: Default + Parse + Token>(&self) -> syn::Result<Option<T>> {
120        self.is_next::<T>().then(|| self.parse()).transpose()
121    }
122
123    fn is_next<T: Default + Token>(&self) -> bool {
124        self.lookahead1().peek(|_| T::default())
125    }
126
127    fn parse_any_ident(&self) -> syn::Result<syn::Ident> {
128        <syn::Ident as syn::ext::IdentExt>::parse_any(self)
129    }
130
131    fn parse_wrapped_and_punctuated<T, W, P>(
132        &self,
133    ) -> syn::Result<Punctuated<T, P>>
134    where
135        T: Parse,
136        W: Default + Token + AcceptedWrapper + 'static,
137        P: Default + Parse + Token,
138    {
139        let inner;
140        if TypeId::of::<W>() == TypeId::of::<token::Bracket>() {
141            _ = syn::bracketed!(inner in self);
142        } else if TypeId::of::<W>() == TypeId::of::<token::Brace>() {
143            _ = syn::braced!(inner in self);
144        } else if TypeId::of::<W>() == TypeId::of::<token::Paren>() {
145            _ = syn::parenthesized!(inner in self);
146        } else {
147            return Err(syn::Error::new(
148                Span::call_site(),
149                "`ParseBufferExt::parse_wrapped_and_punctuated` supports only \
150                 brackets, braces and parentheses as wrappers.",
151            ));
152        }
153        Punctuated::parse_terminated(&inner)
154    }
155
156    fn parse_maybe_wrapped_and_punctuated<T, W, P>(
157        &self,
158    ) -> syn::Result<Punctuated<T, P>>
159    where
160        T: Parse,
161        W: Default + Token + AcceptedWrapper + 'static,
162        P: Default + Parse + Token,
163    {
164        Ok(if self.is_next::<W>() {
165            self.parse_wrapped_and_punctuated::<T, W, P>()?
166        } else {
167            iter::once(self.parse::<T>()?).collect()
168        })
169    }
170
171    fn parse_eq_or_wrapped_and_punctuated<T, W, P>(
172        &self,
173    ) -> syn::Result<Punctuated<T, P>>
174    where
175        T: Parse,
176        W: Default + Token + AcceptedWrapper + 'static,
177        P: Default + Parse + Token,
178    {
179        Ok(if self.is_next::<W>() {
180            self.parse_wrapped_and_punctuated::<T, W, P>()?
181        } else {
182            _ = self.parse::<token::Eq>()?;
183            iter::once(self.parse::<T>()?).collect()
184        })
185    }
186}
187
188/// Trait marking [`Token`] types accepted by
189/// [`parse_wrapped_and_punctuated()`][0] method (and similar) as a wrapper.
190///
191/// [0]: ParseBuffer::parse_wrapped_and_punctuated
192#[sealed]
193pub trait AcceptedWrapper {}
194
195#[sealed]
196impl AcceptedWrapper for token::Bracket {}
197
198#[sealed]
199impl AcceptedWrapper for token::Brace {}
200
201#[sealed]
202impl AcceptedWrapper for token::Paren {}