macroific_attr_parse/
value_syntax.rs

1use syn::parse::{Parse, ParseBuffer, ParseStream};
2use syn::{parenthesized, Token};
3
4/// Syntax used for providing a value
5#[derive(Copy, Clone, Eq, PartialEq, Debug)]
6pub enum ValueSyntax {
7    /// `= contents`
8    Eq,
9
10    /// `(contents)`
11    Paren,
12}
13
14impl ValueSyntax {
15    /// Returns `true` if the syntax is [`Eq`](ValueSyntax::Eq).
16    ///
17    /// # Example
18    ///
19    /// ```
20    /// # use macroific_attr_parse::ValueSyntax;
21    /// #
22    /// assert!(ValueSyntax::Eq.is_eq());
23    /// assert!(!ValueSyntax::Paren.is_eq());
24    /// ```
25    #[inline]
26    #[must_use]
27    pub const fn is_eq(self) -> bool {
28        matches!(self, Self::Eq)
29    }
30
31    /// Returns `true` if the syntax is [`Paren`](ValueSyntax::Paren).
32    ///
33    /// # Example
34    ///
35    /// ```
36    /// # use macroific_attr_parse::ValueSyntax;
37    /// #
38    /// assert!(ValueSyntax::Paren.is_paren());
39    /// assert!(!ValueSyntax::Eq.is_paren());
40    /// ```
41    #[inline]
42    #[must_use]
43    pub const fn is_paren(self) -> bool {
44        matches!(self, Self::Paren)
45    }
46
47    /// Peek the stream **without moving the cursor** and attempt to construct self based on the
48    /// next token.
49    ///
50    /// # Example
51    ///
52    /// ```
53    /// # use macroific_attr_parse::ValueSyntax;
54    /// # use syn::parse::{ParseStream, Parse};
55    /// # use proc_macro2::TokenStream;
56    /// # use quote::quote;
57    /// #
58    /// struct Foo {
59    ///   peek: Option<ValueSyntax>,
60    ///   rest: TokenStream,
61    /// }
62    ///
63    /// impl Parse for Foo {
64    ///   fn parse(input: ParseStream) -> syn::Result<Self> {
65    ///     let peek = ValueSyntax::from_stream(input);
66    ///     let rest = input.parse()?;
67    ///
68    ///     Ok(Self { peek, rest })
69    ///   }
70    /// }
71    ///
72    /// let v: Foo = syn::parse2(quote!(= 123)).unwrap();
73    /// assert_eq!(v.peek, Some(ValueSyntax::Eq));
74    /// assert_eq!(v.rest.to_string(), "= 123");
75    ///
76    /// let v: Foo = syn::parse2(quote!((456))).unwrap();
77    /// assert_eq!(v.peek, Some(ValueSyntax::Paren));
78    /// assert_eq!(v.rest.to_string(), "(456)");
79    ///
80    /// let v: Foo = syn::parse2(quote!(none)).unwrap();
81    /// assert_eq!(v.peek, None);
82    /// assert_eq!(v.rest.to_string(), "none");
83    /// ```
84    pub fn from_stream(parse: ParseStream) -> Option<Self> {
85        if parse.peek(Token![=]) {
86            Some(Self::Eq)
87        } else if parse.peek(syn::token::Paren) {
88            Some(Self::Paren)
89        } else {
90            None
91        }
92    }
93
94    /// Parse whatever tokens need to be parsed based on the resolved syntax.
95    /// Returns a `ParseBuffer` you should continue parsing if the syntax is
96    /// [`Paren`](ValueSyntax::Paren).
97    ///
98    /// # Example
99    ///
100    /// ```
101    /// # use macroific_attr_parse::ValueSyntax;
102    /// # use syn::parse::{ParseStream, Parse};
103    /// # use quote::quote;
104    /// #
105    /// /// `=` implementation
106    /// struct Wrapper1(syn::LitStr);
107    /// impl Parse for Wrapper1 {
108    ///   fn parse(input: ParseStream) -> syn::Result<Self> {
109    ///       let inner = ValueSyntax::Eq.parse_token(input)?;
110    ///       assert!(inner.is_none());
111    ///       Ok(Self(input.parse()?))
112    ///   }
113    /// }
114    ///
115    /// /// `(value)` implementation
116    /// struct Wrapper2(syn::LitStr);
117    /// impl Parse for Wrapper2 {
118    ///   fn parse(input: ParseStream) -> syn::Result<Self> {
119    ///       let inner = ValueSyntax::Paren.parse_token(input)?.expect("expected inner buffer");
120    ///       Ok(Self(inner.parse()?))
121    ///   }
122    /// }
123    ///
124    /// let v: Wrapper1 = syn::parse2(quote!(= "foo")).unwrap();
125    /// assert_eq!(v.0.value(), "foo");
126    ///
127    /// let v: Wrapper2 = syn::parse2(quote!(("bar"))).unwrap();
128    /// assert_eq!(v.0.value(), "bar");
129    /// ```
130    pub fn parse_token(self, input: ParseStream) -> syn::Result<Option<ParseBuffer>> {
131        match self {
132            Self::Eq => {
133                input.parse::<Token![=]>()?;
134                Ok(None)
135            }
136            Self::Paren => {
137                let content;
138                parenthesized!(content in input);
139                Ok(Some(content))
140            }
141        }
142    }
143
144    /// Parse whatever tokens need to be parsed based on the resolved syntax and
145    /// then parse the referenced value as `P`.
146    ///
147    /// # Example
148    ///
149    /// ```
150    /// # use macroific_attr_parse::ValueSyntax;
151    /// # use syn::parse::{ParseStream, Parse};
152    /// # use quote::quote;
153    /// #
154    /// /// `=` implementation
155    /// struct Wrapper1(syn::LitStr);
156    /// impl Parse for Wrapper1 {
157    ///   fn parse(input: ParseStream) -> syn::Result<Self> {
158    ///       Ok(Self(ValueSyntax::Eq.parse(input)?))
159    ///   }
160    /// }
161    ///
162    /// /// `(value)` implementation
163    /// struct Wrapper2(syn::LitStr);
164    /// impl Parse for Wrapper2 {
165    ///   fn parse(input: ParseStream) -> syn::Result<Self> {
166    ///       Ok(Self(ValueSyntax::Paren.parse(input)?))
167    ///   }
168    /// }
169    ///
170    /// let v: Wrapper1 = syn::parse2(quote!(= "foo")).unwrap();
171    /// assert_eq!(v.0.value(), "foo");
172    ///
173    /// let v: Wrapper2 = syn::parse2(quote!(("bar"))).unwrap();
174    /// assert_eq!(v.0.value(), "bar");
175    /// ```
176    pub fn parse<P: Parse>(self, input: ParseStream) -> syn::Result<P> {
177        if let Some(inner) = self.parse_token(input)? {
178            inner.parse()
179        } else {
180            input.parse()
181        }
182    }
183}