1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
//! Extensions for [`syn::parse`] types.
//!
//! [`syn::parse`]: mod@syn::parse

use std::{any::TypeId, iter};

use sealed::sealed;
use syn::{
    parse::Parse,
    punctuated::Punctuated,
    token::{self, Token},
};

/// Extension of a [`syn::parse::ParseBuffer`] providing common function widely
/// used by this crate for parsing.
#[sealed]
pub trait ParseBuffer {
    /// Tries to parse `T` as the next [`Token`].
    ///
    /// Doesn't move [`ParseBuffer`]'s cursor if there is no `T`.
    ///
    /// # Errors
    ///
    /// If `T` fails to be parsed.
    fn try_parse<T: Default + Parse + Token>(&self) -> syn::Result<Option<T>>;

    /// Checks whether the next [`Token`] is `T`.
    ///
    /// Doesn't move [`ParseBuffer`]'s cursor.
    #[must_use]
    fn is_next<T: Default + Token>(&self) -> bool;

    /// Parses the next [`Token`] as [`syn::Ident`] _allowing_ Rust keywords,
    /// while default [`Parse`] implementation for [`syn::Ident`] disallows
    /// them.
    ///
    /// Always moves [`ParseBuffer`]'s cursor.
    ///
    /// # Errors
    ///
    /// If [`syn::Ident`] fails to be parsed.
    ///
    /// [`syn::Ident`]: struct@syn::Ident
    fn parse_any_ident(&self) -> syn::Result<syn::Ident>;

    /// Parses the next [`Token`] as [`syn::Ident`] _allowing_ Rust keywords,
    /// while default [`Parse`] implementation for [`syn::Ident`] disallows
    /// them. Drops the parsed [`Token`] in-place.
    ///
    /// Always moves [`ParseBuffer`]'s cursor.
    ///
    /// # Errors
    ///
    /// If [`syn::Ident`] fails to be parsed.
    ///
    /// [`syn::Ident`]: struct@syn::Ident
    #[inline]
    fn skip_any_ident(&self) -> syn::Result<()> {
        self.parse_any_ident().map(drop)
    }

    /// Parses the wrapped (in a wrapper `W`) [`Token`]s as `T` [`Punctuated`]
    /// with a `P` separator.
    ///
    /// Always moves [`ParseBuffer`]'s cursor.
    ///
    /// # Errors
    ///
    /// If parsing [`Punctuated`] `T` wrapped into `W` fails.
    fn parse_wrapped_and_punctuated<T, W, P>(
        &self,
    ) -> syn::Result<Punctuated<T, P>>
    where
        T: Parse,
        W: Default + Token + 'static,
        P: Default + Parse + Token;

    /// Checks whether the next [`Token`] is a wrapper `W` and if yes, then
    /// parses the wrapped [`Token`]s as `T` [`Punctuated`] with a `P`
    /// separator. Otherwise, parses just `T`.
    ///
    /// Always moves [`ParseBuffer`]'s cursor.
    ///
    /// # Errors
    ///
    /// If either parsing [`Punctuated`] `T` wrapped into `W`, or parsing just
    /// `T`, fails.
    fn parse_maybe_wrapped_and_punctuated<T, W, P>(
        &self,
    ) -> syn::Result<Punctuated<T, P>>
    where
        T: Parse,
        W: Default + Token + 'static,
        P: Default + Parse + Token;

    /// Checks whether the next [`Token`] is a wrapper `W` and if yes, then
    /// parses the wrapped [`Token`]s as `T` [`Punctuated`] with a `P`
    /// separator. Otherwise, parses just `T` following the [`token::Eq`].
    ///
    /// Always moves [`ParseBuffer`]'s cursor.
    ///
    /// # Errors
    ///
    /// If either parsing [`Punctuated`] `T` wrapped into `W`, or parsing just
    /// `T` following the [`token::Eq`], fails.
    ///
    /// [`token::Eq`]: struct@token::Eq
    fn parse_eq_or_wrapped_and_punctuated<T, W, P>(
        &self,
    ) -> syn::Result<Punctuated<T, P>>
    where
        T: Parse,
        W: Default + Token + 'static,
        P: Default + Parse + Token;
}

#[sealed]
impl<'buf> ParseBuffer for syn::parse::ParseBuffer<'buf> {
    #[inline]
    fn try_parse<T: Default + Parse + Token>(&self) -> syn::Result<Option<T>> {
        self.is_next::<T>().then(|| self.parse()).transpose()
    }

    #[inline]
    fn is_next<T: Default + Token>(&self) -> bool {
        self.lookahead1().peek(|_| T::default())
    }

    #[inline]
    fn parse_any_ident(&self) -> syn::Result<syn::Ident> {
        <syn::Ident as syn::ext::IdentExt>::parse_any(self)
    }

    fn parse_wrapped_and_punctuated<T, W, P>(
        &self,
    ) -> syn::Result<Punctuated<T, P>>
    where
        T: Parse,
        W: Default + Token + 'static,
        P: Default + Parse + Token,
    {
        let inner;
        if TypeId::of::<W>() == TypeId::of::<token::Bracket>() {
            let _ = syn::bracketed!(inner in self);
        } else if TypeId::of::<W>() == TypeId::of::<token::Brace>() {
            let _ = syn::braced!(inner in self);
        } else if TypeId::of::<W>() == TypeId::of::<token::Paren>() {
            let _ = syn::parenthesized!(inner in self);
        } else {
            unimplemented!(
                "ParseBufferExt::parse_wrapped_and_punctuated supports only \
                 brackets, braces and parentheses as wrappers.",
            );
        }
        Punctuated::parse_terminated(&inner)
    }

    fn parse_maybe_wrapped_and_punctuated<T, W, P>(
        &self,
    ) -> syn::Result<Punctuated<T, P>>
    where
        T: Parse,
        W: Default + Token + 'static,
        P: Default + Parse + Token,
    {
        Ok(if self.is_next::<W>() {
            self.parse_wrapped_and_punctuated::<T, W, P>()?
        } else {
            iter::once(self.parse::<T>()?).collect()
        })
    }

    fn parse_eq_or_wrapped_and_punctuated<T, W, P>(
        &self,
    ) -> syn::Result<Punctuated<T, P>>
    where
        T: Parse,
        W: Default + Token + 'static,
        P: Default + Parse + Token,
    {
        Ok(if self.is_next::<W>() {
            self.parse_wrapped_and_punctuated::<T, W, P>()?
        } else {
            let _ = self.parse::<token::Eq>()?;
            iter::once(self.parse::<T>()?).collect()
        })
    }
}