attribute_derive/
parsing.rs

1//! This module defines the traits defining how parsing works.
2//!
3//! `attribute-derive` reuses the same traits for nested values and the root
4//! attribute, this is why all traits in this module are named `Attribute*`.
5#![doc = include_str!("../docs/traits.html")]
6
7use std::ops::Range;
8
9use manyhow::{span_range, SpanRanged};
10use proc_macro2::{Ident, Span};
11use quote::ToTokens;
12use syn::ext::IdentExt;
13use syn::parse::discouraged::Speculative;
14#[cfg(doc)]
15use syn::parse::ParseBuffer;
16use syn::parse::{Parse, ParseStream};
17use syn::token::Paren;
18use syn::{parenthesized, Result, Token};
19
20use crate::from_partial::FromPartial;
21use crate::FromAttr;
22
23/// Values that can be parsed named, e.g. `<name>(<value>)`, `<name> = <value>`,
24/// `<name>` (as flag).
25///
26/// This is the default parsing mode used for fields in derived [`FromAttr`]
27/// implementations.
28pub trait AttributeNamed: AttributeBase {
29    /// What open delimiter to use when providing error messages.
30    ///
31    /// For `<name> = <value>`, this is `" = "`, for `<function>(<like>)`, it is
32    /// `"("`.
33    ///
34    /// As named attributes can allow both `<name> = <value>` and
35    /// `name(<value>)`, this might not be the only way this attribute can be
36    /// used.
37    const PREFERRED_OPEN_DELIMITER: &'static str = " = ";
38    /// What close delimiter to use when providing error messages.
39    ///
40    /// For `<name> = <value>`, this is `""`, for `<function>(<like>)`, it is
41    /// `")"`.
42    ///
43    /// As named attributes can allow both `<name> = <value>` and
44    /// `<name>(<value>)`, this might not be the only way this attribute can be
45    /// used.
46    const PREFERRED_CLOSE_DELIMITER: &'static str = "";
47
48    /// Parses an attribute containing `Self` called `name`.
49    ///
50    /// While this function can be implemented freely, the provided
51    /// implementations support `<name> = <value>`, `<function>(<like>)` and
52    /// `<flag>`.
53    ///
54    /// **Note:** This needs to stop parsing at the end of the value, before a
55    /// possible following `,` and further arguments.
56    fn parse_named(
57        name: &'static str,
58        input: ParseStream,
59    ) -> Result<Option<Named<SpannedValue<Self::Partial>>>>;
60}
61
62/// Values that can be parsed positionally, i.e., without a name, e.g.
63/// `"literal"`, `a + b`, `true`.
64///
65/// When deriving [`FromAttr`] this behavior is enabled via putting
66/// `#[attr(positional)]` on the field.
67///
68/// The trait is implemented for each [`AttributeValue`] that implements the
69/// marker trait [`PositionalValue`].
70pub trait AttributePositional: AttributeBase {
71    /// Parses `Self`, positionally.
72    ///
73    /// **Note:** This needs to stop parsing at the end of the value, before a
74    /// possible following `,` and further arguments.
75    fn parse_positional(input: ParseStream) -> Result<Option<SpannedValue<Self::Partial>>>;
76}
77
78/// Any values that can be parsed in an attribute input.
79///
80/// This is probably the trait you want to implement when you created a custom
81/// type for field inside [`#[derive(FromAttr)]`](FromAttr), as it will provide
82/// implementations for [`FromAttr`], [`AttributeNamed`] and, if you implement
83/// the marker trait [`PositionalValue`], [`AttributePositional`] as well.
84///
85/// For named attributes by default it will support both `<name> = <value>` and
86/// `<function>(<like>)`, though this can be tweaked in the implementation.
87pub trait AttributeValue: AttributeBase {
88    /// Printed when not encountering a `(` or `=` respectively while trying to
89    /// parse a [`AttributeNamed`].
90    const EXPECTED: &'static str = "expected `=` or `(`";
91    /// What open delimiter to use when providing error messages.
92    ///
93    /// For `<name> = <value>`, this is `" = "`, for `<function>(<like>)`, it is
94    /// `"("`.
95    ///
96    /// As named attributes can allow both `<name> = <value>` and
97    /// `name(<value>)`, this might not be the only way this attribute can be
98    /// used.
99    const PREFERRED_OPEN_DELIMITER: &'static str = " = ";
100    /// What close delimiter to use when providing error messages.
101    ///
102    /// For `<name> = <value>`, this is `""`, for `<function>(<like>)`, it is
103    /// `")"`.
104    ///
105    /// As named attributes can allow both `<name> = <value>` and
106    /// `<name>(<value>)`, this might not be the only way this attribute can be
107    /// used.
108    const PREFERRED_CLOSE_DELIMITER: &'static str = "";
109    /// Parses the attribute value when parentheses (`(`) were peeked.
110    ///
111    /// Note: this is the input with the parentheses, and potentially following
112    /// arguments.
113    ///
114    /// ```text
115    /// attribute(value), ...
116    ///          ^^^^^^^^^^^^
117    /// ```
118    ///
119    /// In the default implementation this calls through to
120    /// [`parse_value`](Self::parse_value) after removing the parentheses.
121    fn parse_value_meta(input: ParseStream) -> Result<SpannedValue<Self::Partial>> {
122        let content;
123        let paren = parenthesized!(content in input);
124        Self::parse_value(&content)
125            .map(SpannedValue::value)
126            .map(SpannedValue::with(paren.span.join()))
127    }
128
129    /// Parses the attribute value when an equals (`=`) was peeked.
130    ///
131    /// Note: this is the input with the equals, and potentially following
132    /// arguments.
133    ///
134    /// ```text
135    /// attribute = value, ...
136    ///           ^^^^^^^^^^^^
137    /// ```
138    ///
139    /// In the default implementation this calls through to
140    /// [`parse_value`](Self::parse_value) after removing the `=`.
141    fn parse_value_eq(input: ParseStream) -> Result<SpannedValue<Self::Partial>> {
142        <Token![=]>::parse(input)?;
143        Self::parse_value(input)
144    }
145
146    /// Parses the plain attribute value without leading `=` or enclosing
147    /// parenthesis.
148    ///
149    /// **Note:** this input includes potentially a trailing `,` and following
150    /// arguments.
151    ///
152    /// ```text
153    /// attribute = value, ...
154    ///             ^^^^^^^^^^
155    /// ```
156    ///
157    /// For simple syntax this is the only function needed to implement, as the
158    /// default implementations for [`parse_value_meta`](Self::parse_value_meta)
159    /// and [`parse_value_eq`](Self::parse_value_eq).
160    fn parse_value(input: ParseStream) -> Result<SpannedValue<Self::Partial>>;
161}
162
163impl<T: AttributeValue> FromAttr for T {
164    fn parse_partial(input: ParseStream) -> Result<Self::Partial> {
165        Self::parse_value(input).map(SpannedValue::value)
166    }
167}
168
169impl<T: AttributeValue> AttributeNamed for T {
170    const PREFERRED_CLOSE_DELIMITER: &'static str = Self::PREFERRED_CLOSE_DELIMITER;
171    const PREFERRED_OPEN_DELIMITER: &'static str = Self::PREFERRED_OPEN_DELIMITER;
172
173    fn parse_named(
174        name: &'static str,
175        input: ParseStream,
176    ) -> Result<Option<Named<SpannedValue<Self::Partial>>>> {
177        let Some(name) = parse_name(input, name) else {
178            return Ok(None);
179        };
180        let value = if input.peek(Token![=]) {
181            Self::parse_value_eq(input)?
182        } else if input.peek(Paren) {
183            Self::parse_value_meta(input)?
184        } else {
185            return Err(input.error(Self::EXPECTED));
186        };
187        Ok(Some(Named { name, value }))
188    }
189}
190
191/// Marker trait that enables the blanket implementation of
192/// [`AttributePositional`] for [`AttributeValue`].
193pub trait PositionalValue {}
194
195impl<T: AttributeValue + PositionalValue> AttributePositional for T {
196    fn parse_positional(input: ParseStream) -> Result<Option<SpannedValue<Self::Partial>>> {
197        Self::parse_value(input).map(Some)
198    }
199}
200
201/// Trait implementing parsing for `<function>(<like>)` attributes.
202///
203/// This is the trait defining the parsing of both top level attributes deriving
204/// [`FromAttr`] and sub attributes.
205/// ```
206/// # quote::quote!(
207/// #[attribute(sub_attribute("hello", "world"))]
208/// # );  
209/// ```
210pub trait AttributeMeta: AttributeBase {
211    /// Parses the content of the parenthesis:
212    ///
213    /// ```text
214    /// attribute(value)       
215    ///           ^^^^^
216    /// ```
217    fn parse_inner(input: ParseStream) -> Result<Self::Partial>;
218}
219
220impl<T: AttributeMeta> AttributeValue for T {
221    const EXPECTED: &'static str = "expected `(`";
222    const PREFERRED_CLOSE_DELIMITER: &'static str = ")";
223    const PREFERRED_OPEN_DELIMITER: &'static str = "(";
224
225    fn parse_value_eq(input: ParseStream) -> Result<SpannedValue<Self::Partial>> {
226        Err(input.error(Self::EXPECTED))
227    }
228
229    fn parse_value(input: ParseStream) -> Result<SpannedValue<Self::Partial>> {
230        Self::parse_inner(input).map(SpannedValue::call_site)
231    }
232}
233
234/// Trait implemented for attributes that can be parsed optionally as a
235/// positional argument, and the requirement for `Option<T>` to implement
236/// [`AttributePositional`].
237pub trait AttributePeekable {
238    /// Used to decide whether to parse optional positional values.
239    ///
240    /// While most implementations should not mutate `input`, it might be good
241    /// to call this on a [`fork`](ParseBuffer::fork) of the original
242    /// [`ParseStream`] to ensure no mutation is persisted.
243    ///
244    /// # Implementation notes
245    /// This should not try to parse `input`, if you cannot decide if `input`
246    /// matches using [`ParseBuffer::peek`] ([peek2](ParseBuffer::peek2),
247    /// [peek3](ParseBuffer::peek3)), consider not implementing
248    /// [`AttributePeekable`].
249    ///
250    /// `attribute-derive` will always [`fork`](ParseBuffer::fork) before
251    /// calling this function to allow `peek` to modify `input` without
252    /// effecting further parsing.
253    fn peek(input: ParseStream) -> bool;
254}
255
256#[derive(Debug)]
257/// Helper struct to hold a value and the ident of its property.
258pub struct Named<T> {
259    /// The value.
260    pub value: T,
261    /// The argument name.
262    pub name: Ident,
263}
264
265impl<T> Named<T> {
266    #[doc(hidden)]
267    pub fn error_span(&self) -> Span {
268        self.name.span()
269    }
270}
271
272impl<T> Named<SpannedValue<T>> {
273    /// The value.
274    pub fn value(self) -> T {
275        self.value.value
276    }
277}
278
279/// Parses the name, if it matches returns `Some(name)` and removes the `name`
280/// from input, if `None` it does not modify the input.
281pub fn parse_name(input: ParseStream, name: &str) -> Option<Ident> {
282    let fork = &input.fork();
283    let ident: Ident = Ident::parse_any(fork).ok()?;
284    if ident == name {
285        input.advance_to(fork);
286        Some(ident)
287    } else {
288        None
289    }
290}
291
292/// Utility crate holding `Self::Partial` used in most attribute traits, i.e.,
293/// [`FromAttr`], [`AttributeValue`], [`AttributePositional`], ...
294pub trait AttributeBase: FromPartial<Self::Partial> {
295    /// Partial type for this attribute. In most cases this can be `Self`,
296    /// unless the attribute can be parsed in multiple on-its-own-incomplete
297    /// parts or needs special handling on the conversion.
298    type Partial;
299}
300
301/// Helper struct to hold a value and the corresponding range.
302#[derive(Debug)]
303pub struct SpannedValue<T> {
304    /// The value.
305    pub value: T,
306    /// The value's span.
307    pub span: Range<Span>,
308}
309
310impl<T: Default> Default for SpannedValue<T> {
311    fn default() -> Self {
312        Self::call_site(Default::default())
313    }
314}
315
316impl<T> SpannedValue<T> {
317    /// The value.
318    pub fn value(self) -> T {
319        self.value
320    }
321
322    /// The value's span.
323    pub fn span(&self) -> Range<Span> {
324        self.span.clone()
325    }
326
327    /// Map the value to a new type, keeping the span.
328    pub fn map_value<I>(self, map: impl FnOnce(T) -> I) -> SpannedValue<I> {
329        SpannedValue {
330            span: self.span(),
331            value: map(self.value()),
332        }
333    }
334
335    pub(crate) fn with(span: impl SpanRanged) -> impl Fn(T) -> Self {
336        move |value| Self::new(value, span.span_range())
337    }
338
339    /// Creates a new `SpannedValue` from a `value` implementing [`ToTokens`].
340    pub fn from_to_tokens(value: T) -> Self
341    where
342        T: ToTokens,
343    {
344        Self {
345            span: span_range!(value),
346            value,
347        }
348    }
349
350    /// Creates a new `SpannedValue` from a `value` and a [`span`](SpanRanged).
351    pub fn new(value: T, span: impl SpanRanged) -> SpannedValue<T> {
352        Self {
353            value,
354            span: span.span_range(),
355        }
356    }
357
358    /// Creates a new `SpannedValue` with the span [`Span::call_site()`].
359    pub fn call_site(value: T) -> SpannedValue<T> {
360        Self::new(value, Span::call_site())
361    }
362
363    #[doc(hidden)]
364    pub fn error_span(&self) -> Span {
365        self.span()
366            .span_joined()
367            .unwrap_or_else(|| self.span().start)
368    }
369}
370
371impl<T> SpanRanged for SpannedValue<T> {
372    fn span_range(&self) -> Range<Span> {
373        self.span.clone()
374    }
375}