darling_core/
from_meta.rs

1use std::borrow::Cow;
2use std::cell::RefCell;
3use std::collections::btree_map::BTreeMap;
4use std::collections::hash_map::HashMap;
5use std::collections::HashSet;
6use std::hash::BuildHasher;
7use std::num;
8use std::rc::Rc;
9use std::sync::atomic::AtomicBool;
10use std::sync::Arc;
11
12use syn::{Expr, Lit, Meta};
13
14use crate::ast::NestedMeta;
15use crate::util::path_to_string;
16use crate::{Error, Result};
17
18/// Create an instance from an item in an attribute declaration.
19///
20/// # Implementing `FromMeta`
21/// * Do not take a dependency on the `ident` of the passed-in meta item. The ident will be set by the field name of the containing struct.
22/// * Implement only the `from_*` methods that you intend to support. The default implementations will return useful errors.
23///
24/// # Provided Implementations
25/// ## bool
26///
27/// * Word with no value specified - becomes `true`.
28/// * As a boolean literal, e.g. `foo = true`.
29/// * As a string literal, e.g. `foo = "true"`.
30///
31/// ## char
32/// * As a char literal, e.g. `foo = '#'`.
33/// * As a string literal consisting of a single character, e.g. `foo = "#"`.
34///
35/// ## String
36/// * As a string literal, e.g. `foo = "hello"`.
37/// * As a raw string literal, e.g. `foo = r#"hello "world""#`.
38///
39/// ## Number
40/// * As a string literal, e.g. `foo = "-25"`.
41/// * As an unquoted positive value, e.g. `foo = 404`. Negative numbers must be in quotation marks.
42///
43/// ## ()
44/// * Word with no value specified, e.g. `foo`. This is best used with `Option`.
45///   See `darling::util::Flag` for a more strongly-typed alternative.
46///
47/// ## Option
48/// * Any format produces `Some`.
49///
50/// ## `Result<T, darling::Error>`
51/// * Allows for fallible parsing; will populate the target field with the result of the
52///   parse attempt.
53pub trait FromMeta: Sized {
54    fn from_nested_meta(item: &NestedMeta) -> Result<Self> {
55        (match *item {
56            NestedMeta::Lit(ref lit) => Self::from_value(lit),
57            NestedMeta::Meta(ref mi) => Self::from_meta(mi),
58        })
59        .map_err(|e| e.with_span(item))
60    }
61
62    /// Create an instance from a `syn::Meta` by dispatching to the format-appropriate
63    /// trait function. This generally should not be overridden by implementers.
64    ///
65    /// # Error Spans
66    /// If this method is overridden and can introduce errors that weren't passed up from
67    /// other `from_meta` calls, the override must call `with_span` on the error using the
68    /// `item` to make sure that the emitted diagnostic points to the correct location in
69    /// source code.
70    fn from_meta(item: &Meta) -> Result<Self> {
71        (match *item {
72            Meta::Path(_) => Self::from_word(),
73            Meta::List(ref value) => {
74                Self::from_list(&NestedMeta::parse_meta_list(value.tokens.clone())?[..])
75            }
76            Meta::NameValue(ref value) => Self::from_expr(&value.value),
77        })
78        .map_err(|e| e.with_span(item))
79    }
80
81    /// When a field is omitted from a parent meta-item, `from_none` is used to attempt
82    /// recovery before a missing field error is generated.
83    ///
84    /// **Most types should not override this method.** `darling` already allows field-level
85    /// missing-field recovery using `#[darling(default)]` and `#[darling(default = "...")]`,
86    /// and users who add a `String` field to their `FromMeta`-deriving struct would be surprised
87    /// if they get back `""` instead of a missing field error when that field is omitted.
88    ///
89    /// The primary use-case for this is `Option<T>` fields gracefully handlling absence without
90    /// needing `#[darling(default)]`.
91    fn from_none() -> Option<Self> {
92        None
93    }
94
95    /// Create an instance from the presence of the word in the attribute with no
96    /// additional options specified.
97    fn from_word() -> Result<Self> {
98        Err(Error::unsupported_format("word"))
99    }
100
101    /// Create an instance from a list of nested meta items.
102    #[allow(unused_variables)]
103    fn from_list(items: &[NestedMeta]) -> Result<Self> {
104        Err(Error::unsupported_format("list"))
105    }
106
107    /// Create an instance from a literal value of either `foo = "bar"` or `foo("bar")`.
108    /// This dispatches to the appropriate method based on the type of literal encountered,
109    /// and generally should not be overridden by implementers.
110    ///
111    /// # Error Spans
112    /// If this method is overridden, the override must make sure to add `value`'s span
113    /// information to the returned error by calling `with_span(value)` on the `Error` instance.
114    fn from_value(value: &Lit) -> Result<Self> {
115        (match *value {
116            Lit::Bool(ref b) => Self::from_bool(b.value),
117            Lit::Str(ref s) => Self::from_string(&s.value()),
118            Lit::Char(ref ch) => Self::from_char(ch.value()),
119            _ => Err(Error::unexpected_lit_type(value)),
120        })
121        .map_err(|e| e.with_span(value))
122    }
123
124    fn from_expr(expr: &Expr) -> Result<Self> {
125        match *expr {
126            Expr::Lit(ref lit) => Self::from_value(&lit.lit),
127            Expr::Group(ref group) => {
128                // syn may generate this invisible group delimiter when the input to the darling
129                // proc macro (specifically, the attributes) are generated by a
130                // macro_rules! (e.g. propagating a macro_rules!'s expr)
131                // Since we want to basically ignore these invisible group delimiters,
132                // we just propagate the call to the inner expression.
133                Self::from_expr(&group.expr)
134            }
135            _ => Err(Error::unexpected_expr_type(expr)),
136        }
137        .map_err(|e| e.with_span(expr))
138    }
139
140    /// Create an instance from a char literal in a value position.
141    #[allow(unused_variables)]
142    fn from_char(value: char) -> Result<Self> {
143        Err(Error::unexpected_type("char"))
144    }
145
146    /// Create an instance from a string literal in a value position.
147    #[allow(unused_variables)]
148    fn from_string(value: &str) -> Result<Self> {
149        Err(Error::unexpected_type("string"))
150    }
151
152    /// Create an instance from a bool literal in a value position.
153    #[allow(unused_variables)]
154    fn from_bool(value: bool) -> Result<Self> {
155        Err(Error::unexpected_type("bool"))
156    }
157}
158
159// FromMeta impls for std and syn types.
160
161impl FromMeta for () {
162    fn from_word() -> Result<Self> {
163        Ok(())
164    }
165}
166
167impl FromMeta for bool {
168    fn from_word() -> Result<Self> {
169        Ok(true)
170    }
171
172    #[allow(clippy::wrong_self_convention)] // false positive
173    fn from_bool(value: bool) -> Result<Self> {
174        Ok(value)
175    }
176
177    fn from_string(value: &str) -> Result<Self> {
178        value.parse().map_err(|_| Error::unknown_value(value))
179    }
180}
181
182impl FromMeta for AtomicBool {
183    fn from_meta(mi: &Meta) -> Result<Self> {
184        FromMeta::from_meta(mi)
185            .map(AtomicBool::new)
186            .map_err(|e| e.with_span(mi))
187    }
188}
189
190impl FromMeta for char {
191    #[allow(clippy::wrong_self_convention)] // false positive
192    fn from_char(value: char) -> Result<Self> {
193        Ok(value)
194    }
195
196    fn from_string(s: &str) -> Result<Self> {
197        let mut chars = s.chars();
198        let char1 = chars.next();
199        let char2 = chars.next();
200
201        if let (Some(char), None) = (char1, char2) {
202            Ok(char)
203        } else {
204            Err(Error::unexpected_type("string"))
205        }
206    }
207}
208
209impl FromMeta for String {
210    fn from_string(s: &str) -> Result<Self> {
211        Ok(s.to_string())
212    }
213}
214
215impl FromMeta for std::path::PathBuf {
216    fn from_string(s: &str) -> Result<Self> {
217        Ok(s.into())
218    }
219}
220
221/// Generate an impl of `FromMeta` that will accept strings which parse to numbers or
222/// integer literals.
223macro_rules! from_meta_num {
224    ($ty:path) => {
225        impl FromMeta for $ty {
226            fn from_string(s: &str) -> Result<Self> {
227                s.parse().map_err(|_| Error::unknown_value(s))
228            }
229
230            fn from_value(value: &Lit) -> Result<Self> {
231                (match *value {
232                    Lit::Str(ref s) => Self::from_string(&s.value()),
233                    Lit::Int(ref s) => s.base10_parse::<$ty>().map_err(Error::from),
234                    _ => Err(Error::unexpected_lit_type(value)),
235                })
236                .map_err(|e| e.with_span(value))
237            }
238        }
239    };
240}
241
242from_meta_num!(u8);
243from_meta_num!(u16);
244from_meta_num!(u32);
245from_meta_num!(u64);
246from_meta_num!(u128);
247from_meta_num!(usize);
248from_meta_num!(i8);
249from_meta_num!(i16);
250from_meta_num!(i32);
251from_meta_num!(i64);
252from_meta_num!(i128);
253from_meta_num!(isize);
254from_meta_num!(num::NonZeroU8);
255from_meta_num!(num::NonZeroU16);
256from_meta_num!(num::NonZeroU32);
257from_meta_num!(num::NonZeroU64);
258from_meta_num!(num::NonZeroU128);
259from_meta_num!(num::NonZeroUsize);
260from_meta_num!(num::NonZeroI8);
261from_meta_num!(num::NonZeroI16);
262from_meta_num!(num::NonZeroI32);
263from_meta_num!(num::NonZeroI64);
264from_meta_num!(num::NonZeroI128);
265from_meta_num!(num::NonZeroIsize);
266
267/// Generate an impl of `FromMeta` that will accept strings which parse to floats or
268/// float literals.
269macro_rules! from_meta_float {
270    ($ty:ident) => {
271        impl FromMeta for $ty {
272            fn from_string(s: &str) -> Result<Self> {
273                s.parse().map_err(|_| Error::unknown_value(s))
274            }
275
276            fn from_value(value: &Lit) -> Result<Self> {
277                (match *value {
278                    Lit::Str(ref s) => Self::from_string(&s.value()),
279                    Lit::Float(ref s) => s.base10_parse::<$ty>().map_err(Error::from),
280                    _ => Err(Error::unexpected_lit_type(value)),
281                })
282                .map_err(|e| e.with_span(value))
283            }
284        }
285    };
286}
287
288from_meta_float!(f32);
289from_meta_float!(f64);
290
291/// Parsing support for punctuated. This attempts to preserve span information
292/// when available, but also supports parsing strings with the call site as the
293/// emitted span.
294impl<T: syn::parse::Parse, P: syn::parse::Parse> FromMeta for syn::punctuated::Punctuated<T, P> {
295    fn from_value(value: &Lit) -> Result<Self> {
296        if let Lit::Str(ref ident) = *value {
297            ident
298                .parse_with(syn::punctuated::Punctuated::parse_terminated)
299                .map_err(|_| Error::unknown_lit_str_value(ident))
300        } else {
301            Err(Error::unexpected_lit_type(value))
302        }
303    }
304}
305
306/// Support for arbitrary expressions as values in a meta item.
307///
308/// For backwards-compatibility to versions of `darling` based on `syn` 1,
309/// string literals will be "unwrapped" and their contents will be parsed
310/// as an expression.
311///
312/// See [`util::parse_expr`](crate::util::parse_expr) for functions to provide
313/// alternate parsing modes for this type.
314impl FromMeta for syn::Expr {
315    fn from_expr(expr: &Expr) -> Result<Self> {
316        match expr {
317            Expr::Lit(syn::ExprLit {
318                lit: lit @ syn::Lit::Str(_),
319                ..
320            }) => Self::from_value(lit),
321            Expr::Group(group) => Self::from_expr(&group.expr), // see FromMeta::from_expr
322            _ => Ok(expr.clone()),
323        }
324    }
325
326    fn from_string(value: &str) -> Result<Self> {
327        syn::parse_str(value).map_err(|_| Error::unknown_value(value))
328    }
329
330    fn from_value(value: &::syn::Lit) -> Result<Self> {
331        if let ::syn::Lit::Str(ref v) = *value {
332            v.parse::<syn::Expr>()
333                .map_err(|_| Error::unknown_lit_str_value(v))
334        } else {
335            Err(Error::unexpected_lit_type(value))
336        }
337    }
338}
339
340/// Parser for paths that supports both quote-wrapped and bare values.
341impl FromMeta for syn::Path {
342    fn from_string(value: &str) -> Result<Self> {
343        syn::parse_str(value).map_err(|_| Error::unknown_value(value))
344    }
345
346    fn from_value(value: &::syn::Lit) -> Result<Self> {
347        if let ::syn::Lit::Str(ref v) = *value {
348            v.parse().map_err(|_| Error::unknown_lit_str_value(v))
349        } else {
350            Err(Error::unexpected_lit_type(value))
351        }
352    }
353
354    fn from_expr(expr: &Expr) -> Result<Self> {
355        match expr {
356            Expr::Lit(lit) => Self::from_value(&lit.lit),
357            Expr::Path(path) => Ok(path.path.clone()),
358            Expr::Group(group) => Self::from_expr(&group.expr), // see FromMeta::from_expr
359            _ => Err(Error::unexpected_expr_type(expr)),
360        }
361    }
362}
363
364impl FromMeta for syn::Ident {
365    fn from_string(value: &str) -> Result<Self> {
366        syn::parse_str(value).map_err(|_| Error::unknown_value(value))
367    }
368
369    fn from_value(value: &syn::Lit) -> Result<Self> {
370        if let syn::Lit::Str(ref v) = *value {
371            v.parse().map_err(|_| Error::unknown_lit_str_value(v))
372        } else {
373            Err(Error::unexpected_lit_type(value))
374        }
375    }
376
377    fn from_expr(expr: &Expr) -> Result<Self> {
378        match expr {
379            Expr::Lit(lit) => Self::from_value(&lit.lit),
380            // All idents are paths, but not all paths are idents -
381            // the get_ident() method does additional validation to
382            // make sure the path is actually an ident.
383            Expr::Path(path) => match path.path.get_ident() {
384                Some(ident) => Ok(ident.clone()),
385                None => Err(Error::unexpected_expr_type(expr)),
386            },
387            Expr::Group(group) => Self::from_expr(&group.expr), // see FromMeta::from_expr
388            _ => Err(Error::unexpected_expr_type(expr)),
389        }
390    }
391}
392
393/// Adapter for various expression types.
394///
395/// Prior to syn 2.0, darling supported arbitrary expressions as long as they
396/// were wrapped in quotation marks. This was helpful for people writing
397/// libraries that needed expressions, but it now creates an ambiguity when
398/// parsing a meta item.
399///
400/// To address this, the macro supports both formats; if it cannot parse the
401/// item as an expression of the right type and the passed-in expression is
402/// a string literal, it will fall back to parsing the string contents.
403macro_rules! from_syn_expr_type {
404    ($ty:path, $variant:ident) => {
405        impl FromMeta for $ty {
406            fn from_expr(expr: &syn::Expr) -> Result<Self> {
407                match expr {
408                    syn::Expr::$variant(body) => Ok(body.clone()),
409                    syn::Expr::Lit(expr_lit) => Self::from_value(&expr_lit.lit),
410                    syn::Expr::Group(group) => Self::from_expr(&group.expr), // see FromMeta::from_expr
411                    _ => Err(Error::unexpected_expr_type(expr)),
412                }
413            }
414
415            fn from_value(value: &::syn::Lit) -> Result<Self> {
416                if let syn::Lit::Str(body) = &value {
417                    body.parse::<$ty>()
418                        .map_err(|_| Error::unknown_lit_str_value(body))
419                } else {
420                    Err(Error::unexpected_lit_type(value))
421                }
422            }
423        }
424    };
425}
426
427from_syn_expr_type!(syn::ExprArray, Array);
428from_syn_expr_type!(syn::ExprPath, Path);
429from_syn_expr_type!(syn::ExprRange, Range);
430
431/// Adapter from `syn::parse::Parse` to `FromMeta` for items that cannot
432/// be expressed in a [`syn::MetaNameValue`].
433///
434/// This cannot be a blanket impl, due to the `syn::Lit` family's need to handle non-string values.
435/// Therefore, we use a macro and a lot of impls.
436macro_rules! from_syn_parse {
437    ($ty:path) => {
438        impl FromMeta for $ty {
439            fn from_string(value: &str) -> Result<Self> {
440                syn::parse_str(value).map_err(|_| Error::unknown_value(value))
441            }
442
443            fn from_value(value: &::syn::Lit) -> Result<Self> {
444                if let ::syn::Lit::Str(ref v) = *value {
445                    v.parse::<$ty>()
446                        .map_err(|_| Error::unknown_lit_str_value(v))
447                } else {
448                    Err(Error::unexpected_lit_type(value))
449                }
450            }
451        }
452    };
453}
454
455from_syn_parse!(syn::Type);
456from_syn_parse!(syn::TypeArray);
457from_syn_parse!(syn::TypeBareFn);
458from_syn_parse!(syn::TypeGroup);
459from_syn_parse!(syn::TypeImplTrait);
460from_syn_parse!(syn::TypeInfer);
461from_syn_parse!(syn::TypeMacro);
462from_syn_parse!(syn::TypeNever);
463from_syn_parse!(syn::TypeParam);
464from_syn_parse!(syn::TypeParen);
465from_syn_parse!(syn::TypePath);
466from_syn_parse!(syn::TypePtr);
467from_syn_parse!(syn::TypeReference);
468from_syn_parse!(syn::TypeSlice);
469from_syn_parse!(syn::TypeTraitObject);
470from_syn_parse!(syn::TypeTuple);
471from_syn_parse!(syn::Visibility);
472from_syn_parse!(syn::WhereClause);
473
474macro_rules! from_numeric_array {
475    ($ty:ident) => {
476        /// Parsing an unsigned integer array, i.e. `example = "[1, 2, 3, 4]"`.
477        impl FromMeta for Vec<$ty> {
478            fn from_expr(expr: &syn::Expr) -> Result<Self> {
479                match expr {
480                    syn::Expr::Array(expr_array) => expr_array
481                        .elems
482                        .iter()
483                        .map(|expr| {
484                            let unexpected = || {
485                                Error::custom("Expected array of unsigned integers").with_span(expr)
486                            };
487                            match expr {
488                                Expr::Lit(lit) => $ty::from_value(&lit.lit),
489                                Expr::Group(group) => match &*group.expr {
490                                    Expr::Lit(lit) => $ty::from_value(&lit.lit),
491                                    _ => Err(unexpected()),
492                                },
493                                _ => Err(unexpected()),
494                            }
495                        })
496                        .collect::<Result<Vec<$ty>>>(),
497                    syn::Expr::Lit(expr_lit) => Self::from_value(&expr_lit.lit),
498                    syn::Expr::Group(group) => Self::from_expr(&group.expr), // see FromMeta::from_expr
499                    _ => Err(Error::unexpected_expr_type(expr)),
500                }
501            }
502
503            fn from_value(value: &Lit) -> Result<Self> {
504                let expr_array = syn::ExprArray::from_value(value)?;
505                Self::from_expr(&syn::Expr::Array(expr_array))
506            }
507        }
508    };
509}
510
511from_numeric_array!(u8);
512from_numeric_array!(u16);
513from_numeric_array!(u32);
514from_numeric_array!(u64);
515from_numeric_array!(usize);
516
517impl FromMeta for syn::Lit {
518    fn from_value(value: &Lit) -> Result<Self> {
519        Ok(value.clone())
520    }
521}
522
523macro_rules! from_meta_lit {
524    ($impl_ty:path, $lit_variant:path) => {
525        impl FromMeta for $impl_ty {
526            fn from_value(value: &Lit) -> Result<Self> {
527                if let $lit_variant(ref value) = *value {
528                    Ok(value.clone())
529                } else {
530                    Err(Error::unexpected_lit_type(value))
531                }
532            }
533        }
534
535        impl FromMeta for Vec<$impl_ty> {
536            fn from_list(items: &[NestedMeta]) -> Result<Self> {
537                items
538                    .iter()
539                    .map(<$impl_ty as FromMeta>::from_nested_meta)
540                    .collect()
541            }
542
543            fn from_value(value: &syn::Lit) -> Result<Self> {
544                let expr_array = syn::ExprArray::from_value(value)?;
545                Self::from_expr(&syn::Expr::Array(expr_array))
546            }
547
548            fn from_expr(expr: &syn::Expr) -> Result<Self> {
549                match expr {
550                    syn::Expr::Array(expr_array) => expr_array
551                        .elems
552                        .iter()
553                        .map(<$impl_ty as FromMeta>::from_expr)
554                        .collect::<Result<Vec<_>>>(),
555                    syn::Expr::Lit(expr_lit) => Self::from_value(&expr_lit.lit),
556                    syn::Expr::Group(g) => Self::from_expr(&g.expr),
557                    _ => Err(Error::unexpected_expr_type(expr)),
558                }
559            }
560        }
561    };
562}
563
564from_meta_lit!(syn::LitInt, Lit::Int);
565from_meta_lit!(syn::LitFloat, Lit::Float);
566from_meta_lit!(syn::LitStr, Lit::Str);
567from_meta_lit!(syn::LitByte, Lit::Byte);
568from_meta_lit!(syn::LitByteStr, Lit::ByteStr);
569from_meta_lit!(syn::LitChar, Lit::Char);
570from_meta_lit!(syn::LitBool, Lit::Bool);
571from_meta_lit!(proc_macro2::Literal, Lit::Verbatim);
572
573impl FromMeta for syn::Meta {
574    fn from_meta(value: &syn::Meta) -> Result<Self> {
575        Ok(value.clone())
576    }
577}
578
579impl FromMeta for Vec<syn::WherePredicate> {
580    fn from_string(value: &str) -> Result<Self> {
581        syn::WhereClause::from_string(&format!("where {}", value))
582            .map(|c| c.predicates.into_iter().collect())
583    }
584
585    fn from_value(value: &Lit) -> Result<Self> {
586        if let syn::Lit::Str(s) = value {
587            syn::WhereClause::from_value(&syn::Lit::Str(syn::LitStr::new(
588                &format!("where {}", s.value()),
589                value.span(),
590            )))
591            .map(|c| c.predicates.into_iter().collect())
592        } else {
593            Err(Error::unexpected_lit_type(value))
594        }
595    }
596}
597
598impl FromMeta for ident_case::RenameRule {
599    fn from_string(value: &str) -> Result<Self> {
600        value.parse().map_err(|_| Error::unknown_value(value))
601    }
602}
603
604impl<T: FromMeta> FromMeta for Option<T> {
605    fn from_none() -> Option<Self> {
606        Some(None)
607    }
608
609    fn from_meta(item: &Meta) -> Result<Self> {
610        FromMeta::from_meta(item).map(Some)
611    }
612}
613
614impl<T: FromMeta> FromMeta for Result<T> {
615    fn from_none() -> Option<Self> {
616        T::from_none().map(Ok)
617    }
618
619    // `#[darling(flatten)]` forwards directly to this method, so it's
620    // necessary to declare it to avoid getting an unsupported format
621    // error if it's invoked directly.
622    fn from_list(items: &[NestedMeta]) -> Result<Self> {
623        Ok(FromMeta::from_list(items))
624    }
625
626    fn from_meta(item: &Meta) -> Result<Self> {
627        Ok(FromMeta::from_meta(item))
628    }
629}
630
631/// Create an impl that forwards to an inner type `T` for parsing.
632macro_rules! smart_pointer_t {
633    ($ty:path, $map_fn:path) => {
634        impl<T: FromMeta> FromMeta for $ty {
635            fn from_none() -> Option<Self> {
636                T::from_none().map($map_fn)
637            }
638
639            // `#[darling(flatten)]` forwards directly to this method, so it's
640            // necessary to declare it to avoid getting an unsupported format
641            // error if it's invoked directly.
642            fn from_list(items: &[NestedMeta]) -> Result<Self> {
643                FromMeta::from_list(items).map($map_fn)
644            }
645
646            fn from_meta(item: &Meta) -> Result<Self> {
647                FromMeta::from_meta(item).map($map_fn)
648            }
649        }
650    };
651}
652
653smart_pointer_t!(Box<T>, Box::new);
654smart_pointer_t!(Rc<T>, Rc::new);
655smart_pointer_t!(Arc<T>, Arc::new);
656smart_pointer_t!(RefCell<T>, RefCell::new);
657
658/// Parses the meta-item, and in case of error preserves a copy of the input for
659/// later analysis.
660impl<T: FromMeta> FromMeta for ::std::result::Result<T, Meta> {
661    fn from_meta(item: &Meta) -> Result<Self> {
662        T::from_meta(item)
663            .map(Ok)
664            .or_else(|_| Ok(Err(item.clone())))
665    }
666}
667
668/// Trait to convert from a path into an owned key for a map.
669trait KeyFromPath: Sized {
670    fn from_path(path: &syn::Path) -> Result<Self>;
671    fn to_display(&self) -> Cow<'_, str>;
672}
673
674impl KeyFromPath for String {
675    fn from_path(path: &syn::Path) -> Result<Self> {
676        Ok(path_to_string(path))
677    }
678
679    fn to_display(&self) -> Cow<'_, str> {
680        Cow::Borrowed(self)
681    }
682}
683
684impl KeyFromPath for syn::Path {
685    fn from_path(path: &syn::Path) -> Result<Self> {
686        Ok(path.clone())
687    }
688
689    fn to_display(&self) -> Cow<'_, str> {
690        Cow::Owned(path_to_string(self))
691    }
692}
693
694impl KeyFromPath for syn::Ident {
695    fn from_path(path: &syn::Path) -> Result<Self> {
696        if path.segments.len() == 1
697            && path.leading_colon.is_none()
698            && path.segments[0].arguments.is_empty()
699        {
700            Ok(path.segments[0].ident.clone())
701        } else {
702            Err(Error::custom("Key must be an identifier").with_span(path))
703        }
704    }
705
706    fn to_display(&self) -> Cow<'_, str> {
707        Cow::Owned(self.to_string())
708    }
709}
710
711macro_rules! map {
712    (hash_map, $key:ty, $nested:ident) => {
713        impl<V: FromMeta, S: BuildHasher + Default> FromMeta for HashMap<$key, V, S> {
714            map!(
715                HashMap::with_capacity_and_hasher($nested.len(), Default::default()),
716                $key,
717                $nested
718            );
719        }
720    };
721
722    (btree_map, $key:ty, $nested:ident) => {
723        impl<V: FromMeta> FromMeta for BTreeMap<$key, V> {
724            map!(BTreeMap::new(), $key, $nested);
725        }
726    };
727
728    ($new:expr, $key:ty, $nested:ident) => {
729        fn from_list($nested: &[NestedMeta]) -> Result<Self> {
730            // Convert the nested meta items into a sequence of (path, value result) result tuples.
731            // An outer Err means no (key, value) structured could be found, while an Err in the
732            // second position of the tuple means that value was rejected by FromMeta.
733            //
734            // We defer key conversion into $key so that we don't lose span information in the case
735            // of String keys; we'll need it for good duplicate key errors later.
736            let pairs = $nested
737                .iter()
738                .map(|item| -> Result<(&syn::Path, Result<V>)> {
739                    match *item {
740                        NestedMeta::Meta(ref inner) => {
741                            let path = inner.path();
742                            Ok((
743                                path,
744                                FromMeta::from_meta(inner).map_err(|e| e.at_path(&path)),
745                            ))
746                        }
747                        NestedMeta::Lit(_) => Err(Error::unsupported_format("expression")),
748                    }
749                });
750
751            let mut errors = Error::accumulator();
752            // We need to track seen keys separately from the final map, since a seen key with an
753            // Err value won't go into the final map but should trigger a duplicate field error.
754            //
755            // This is a set of $key rather than Path to avoid the possibility that a key type
756            // parses two paths of different values to the same key value.
757            let mut seen_keys = HashSet::with_capacity($nested.len());
758
759            // The map to return in the Ok case. Its size will always be exactly nested.len(),
760            // since otherwise ≥1 field had a problem and the entire map is dropped immediately
761            // when the function returns `Err`.
762            let mut map = $new;
763
764            for item in pairs {
765                if let Some((path, value)) = errors.handle(item) {
766                    let key: $key = match KeyFromPath::from_path(path) {
767                        Ok(k) => k,
768                        Err(e) => {
769                            errors.push(e);
770
771                            // Surface value errors even under invalid keys
772                            errors.handle(value);
773
774                            continue;
775                        }
776                    };
777
778                    let already_seen = seen_keys.contains(&key);
779
780                    if already_seen {
781                        errors.push(Error::duplicate_field(&key.to_display()).with_span(path));
782                    }
783
784                    match value {
785                        Ok(_) if already_seen => {}
786                        Ok(val) => {
787                            map.insert(key.clone(), val);
788                        }
789                        Err(e) => {
790                            errors.push(e);
791                        }
792                    }
793
794                    seen_keys.insert(key);
795                }
796            }
797
798            errors.finish_with(map)
799        }
800    };
801}
802
803// This is done as a macro rather than a blanket impl to avoid breaking backwards compatibility
804// with 0.12.x, while still sharing the same impl.
805map!(hash_map, String, nested);
806map!(hash_map, syn::Ident, nested);
807map!(hash_map, syn::Path, nested);
808
809map!(btree_map, String, nested);
810map!(btree_map, syn::Ident, nested);
811
812/// Tests for `FromMeta` implementations. Wherever the word `ignore` appears in test input,
813/// it should not be considered by the parsing.
814#[cfg(test)]
815mod tests {
816    use std::num::{NonZeroU32, NonZeroU64};
817
818    use proc_macro2::TokenStream;
819    use quote::quote;
820    use syn::parse_quote;
821
822    use crate::{Error, FromMeta, Result};
823
824    /// parse a string as a syn::Meta instance.
825    fn pm(tokens: TokenStream) -> ::std::result::Result<syn::Meta, String> {
826        let attribute: syn::Attribute = parse_quote!(#[#tokens]);
827        Ok(attribute.meta)
828    }
829
830    #[track_caller]
831    fn fm<T: FromMeta>(tokens: TokenStream) -> T {
832        FromMeta::from_meta(&pm(tokens).expect("Tests should pass well-formed input"))
833            .expect("Tests should pass valid input")
834    }
835
836    #[test]
837    fn unit_succeeds() {
838        fm::<()>(quote!(ignore));
839    }
840
841    #[test]
842    #[allow(clippy::bool_assert_comparison)]
843    fn bool_succeeds() {
844        // word format
845        assert_eq!(fm::<bool>(quote!(ignore)), true);
846
847        // bool literal
848        assert_eq!(fm::<bool>(quote!(ignore = true)), true);
849        assert_eq!(fm::<bool>(quote!(ignore = false)), false);
850
851        // string literals
852        assert_eq!(fm::<bool>(quote!(ignore = "true")), true);
853        assert_eq!(fm::<bool>(quote!(ignore = "false")), false);
854    }
855
856    #[test]
857    fn char_succeeds() {
858        // char literal
859        assert_eq!(fm::<char>(quote!(ignore = '😬')), '😬');
860
861        // string literal
862        assert_eq!(fm::<char>(quote!(ignore = "😬")), '😬');
863    }
864
865    #[test]
866    fn string_succeeds() {
867        // cooked form
868        assert_eq!(&fm::<String>(quote!(ignore = "world")), "world");
869
870        // raw form
871        assert_eq!(&fm::<String>(quote!(ignore = r#"world"#)), "world");
872    }
873
874    #[test]
875    fn pathbuf_succeeds() {
876        assert_eq!(
877            fm::<std::path::PathBuf>(quote!(ignore = r#"C:\"#)),
878            std::path::PathBuf::from(r#"C:\"#)
879        );
880    }
881
882    #[test]
883    #[allow(clippy::float_cmp)] // we want exact equality
884    fn number_succeeds() {
885        assert_eq!(fm::<u8>(quote!(ignore = "2")), 2u8);
886        assert_eq!(fm::<i16>(quote!(ignore = "-25")), -25i16);
887        assert_eq!(fm::<f64>(quote!(ignore = "1.4e10")), 1.4e10);
888    }
889
890    #[should_panic(expected = "UnknownValue(\"0\")")]
891    #[test]
892    fn nonzero_number_fails() {
893        fm::<NonZeroU64>(quote!(ignore = "0"));
894    }
895
896    #[test]
897    fn nonzero_number_succeeds() {
898        assert_eq!(
899            fm::<NonZeroU32>(quote!(ignore = "2")),
900            NonZeroU32::new(2).unwrap()
901        );
902    }
903
904    #[test]
905    fn int_without_quotes() {
906        assert_eq!(fm::<u8>(quote!(ignore = 2)), 2u8);
907        assert_eq!(fm::<u16>(quote!(ignore = 255)), 255u16);
908        assert_eq!(fm::<u32>(quote!(ignore = 5000)), 5000u32);
909
910        // Check that we aren't tripped up by incorrect suffixes
911        assert_eq!(fm::<u32>(quote!(ignore = 5000i32)), 5000u32);
912    }
913
914    #[test]
915    fn negative_int_without_quotes() {
916        assert_eq!(fm::<i8>(quote!(ignore = -2)), -2i8);
917        assert_eq!(fm::<i32>(quote!(ignore = -255)), -255i32);
918    }
919
920    #[test]
921    #[allow(clippy::float_cmp)] // we want exact equality
922    fn float_without_quotes() {
923        assert_eq!(fm::<f32>(quote!(ignore = 2.)), 2.0f32);
924        assert_eq!(fm::<f32>(quote!(ignore = 2.0)), 2.0f32);
925        assert_eq!(fm::<f64>(quote!(ignore = 1.4e10)), 1.4e10f64);
926    }
927
928    #[test]
929    fn too_large_int_produces_error() {
930        assert!(fm::<Result<u8>>(quote!(ignore = 2000)).is_err());
931    }
932
933    #[test]
934    fn meta_succeeds() {
935        use syn::Meta;
936
937        assert_eq!(
938            fm::<Meta>(quote!(hello(world, today))),
939            pm(quote!(hello(world, today))).unwrap()
940        );
941    }
942
943    #[test]
944    fn hash_map_succeeds() {
945        use std::collections::HashMap;
946
947        let comparison = {
948            let mut c = HashMap::new();
949            c.insert("hello".to_string(), true);
950            c.insert("world".to_string(), false);
951            c.insert("there".to_string(), true);
952            c
953        };
954
955        assert_eq!(
956            fm::<HashMap<String, bool>>(quote!(ignore(hello, world = false, there = "true"))),
957            comparison
958        );
959    }
960
961    /// Check that a `HashMap` cannot have duplicate keys, and that the generated error
962    /// is assigned a span to correctly target the diagnostic message.
963    #[test]
964    fn hash_map_duplicate() {
965        use std::collections::HashMap;
966
967        let err: Result<HashMap<String, bool>> =
968            FromMeta::from_meta(&pm(quote!(ignore(hello, hello = false))).unwrap());
969
970        let err = err.expect_err("Duplicate keys in HashMap should error");
971
972        assert!(err.has_span());
973        assert_eq!(err.to_string(), Error::duplicate_field("hello").to_string());
974    }
975
976    #[test]
977    fn hash_map_multiple_errors() {
978        use std::collections::HashMap;
979
980        let err = HashMap::<String, bool>::from_meta(
981            &pm(quote!(ignore(hello, hello = 3, hello = false))).unwrap(),
982        )
983        .expect_err("Duplicates and bad values should error");
984
985        assert_eq!(err.len(), 3);
986        let errors = err.into_iter().collect::<Vec<_>>();
987        assert!(errors[0].has_span());
988        assert!(errors[1].has_span());
989        assert!(errors[2].has_span());
990    }
991
992    #[test]
993    fn hash_map_ident_succeeds() {
994        use std::collections::HashMap;
995        use syn::parse_quote;
996
997        let comparison = {
998            let mut c = HashMap::<syn::Ident, bool>::new();
999            c.insert(parse_quote!(first), true);
1000            c.insert(parse_quote!(second), false);
1001            c
1002        };
1003
1004        assert_eq!(
1005            fm::<HashMap<syn::Ident, bool>>(quote!(ignore(first, second = false))),
1006            comparison
1007        );
1008    }
1009
1010    #[test]
1011    fn hash_map_ident_rejects_non_idents() {
1012        use std::collections::HashMap;
1013
1014        let err: Result<HashMap<syn::Ident, bool>> =
1015            FromMeta::from_meta(&pm(quote!(ignore(first, the::second))).unwrap());
1016
1017        err.unwrap_err();
1018    }
1019
1020    #[test]
1021    fn hash_map_path_succeeds() {
1022        use std::collections::HashMap;
1023        use syn::parse_quote;
1024
1025        let comparison = {
1026            let mut c = HashMap::<syn::Path, bool>::new();
1027            c.insert(parse_quote!(first), true);
1028            c.insert(parse_quote!(the::second), false);
1029            c
1030        };
1031
1032        assert_eq!(
1033            fm::<HashMap<syn::Path, bool>>(quote!(ignore(first, the::second = false))),
1034            comparison
1035        );
1036    }
1037
1038    #[test]
1039    fn btree_map_succeeds() {
1040        use std::collections::BTreeMap;
1041
1042        let comparison = {
1043            let mut c = BTreeMap::new();
1044            c.insert("hello".to_string(), true);
1045            c.insert("world".to_string(), false);
1046            c.insert("there".to_string(), true);
1047            c
1048        };
1049
1050        assert_eq!(
1051            fm::<BTreeMap<String, bool>>(quote!(ignore(hello, world = false, there = "true"))),
1052            comparison
1053        );
1054    }
1055
1056    /// Check that a `HashMap` cannot have duplicate keys, and that the generated error
1057    /// is assigned a span to correctly target the diagnostic message.
1058    #[test]
1059    fn btree_map_duplicate() {
1060        use std::collections::BTreeMap;
1061
1062        let err: Result<BTreeMap<String, bool>> =
1063            FromMeta::from_meta(&pm(quote!(ignore(hello, hello = false))).unwrap());
1064
1065        let err = err.expect_err("Duplicate keys in BTreeMap should error");
1066
1067        assert!(err.has_span());
1068        assert_eq!(err.to_string(), Error::duplicate_field("hello").to_string());
1069    }
1070
1071    #[test]
1072    fn btree_map_multiple_errors() {
1073        use std::collections::BTreeMap;
1074
1075        let err = BTreeMap::<String, bool>::from_meta(
1076            &pm(quote!(ignore(hello, hello = 3, hello = false))).unwrap(),
1077        )
1078        .expect_err("Duplicates and bad values should error");
1079
1080        assert_eq!(err.len(), 3);
1081        let errors = err.into_iter().collect::<Vec<_>>();
1082        assert!(errors[0].has_span());
1083        assert!(errors[1].has_span());
1084        assert!(errors[2].has_span());
1085    }
1086
1087    #[test]
1088    fn btree_map_ident_succeeds() {
1089        use std::collections::BTreeMap;
1090        use syn::parse_quote;
1091
1092        let comparison = {
1093            let mut c = BTreeMap::<syn::Ident, bool>::new();
1094            c.insert(parse_quote!(first), true);
1095            c.insert(parse_quote!(second), false);
1096            c
1097        };
1098
1099        assert_eq!(
1100            fm::<BTreeMap<syn::Ident, bool>>(quote!(ignore(first, second = false))),
1101            comparison
1102        );
1103    }
1104
1105    #[test]
1106    fn btree_map_ident_rejects_non_idents() {
1107        use std::collections::BTreeMap;
1108
1109        let err: Result<BTreeMap<syn::Ident, bool>> =
1110            FromMeta::from_meta(&pm(quote!(ignore(first, the::second))).unwrap());
1111
1112        err.unwrap_err();
1113    }
1114
1115    #[test]
1116    fn btree_map_expr_values_succeed() {
1117        use std::collections::BTreeMap;
1118        use syn::parse_quote;
1119
1120        let comparison: BTreeMap<String, syn::Expr> = vec![
1121            ("hello", parse_quote!(2 + 2)),
1122            ("world", parse_quote!(x.foo())),
1123        ]
1124        .into_iter()
1125        .map(|(k, v)| (k.to_string(), v))
1126        .collect();
1127
1128        assert_eq!(
1129            fm::<BTreeMap<String, syn::Expr>>(quote!(ignore(hello = 2 + 2, world = x.foo()))),
1130            comparison
1131        );
1132    }
1133
1134    /// Tests that fallible parsing will always produce an outer `Ok` (from `fm`),
1135    /// and will accurately preserve the inner contents.
1136    #[test]
1137    fn darling_result_succeeds() {
1138        fm::<Result<()>>(quote!(ignore)).unwrap();
1139        fm::<Result<()>>(quote!(ignore(world))).unwrap_err();
1140    }
1141
1142    /// Test punctuated
1143    #[test]
1144    fn test_punctuated() {
1145        fm::<syn::punctuated::Punctuated<syn::FnArg, syn::token::Comma>>(quote!(
1146            ignore = "a: u8, b: Type"
1147        ));
1148        fm::<syn::punctuated::Punctuated<syn::Expr, syn::token::Comma>>(quote!(ignore = "a, b, c"));
1149    }
1150
1151    #[test]
1152    fn test_expr_array() {
1153        fm::<syn::ExprArray>(quote!(ignore = "[0x1, 0x2]"));
1154        fm::<syn::ExprArray>(quote!(ignore = "[\"Hello World\", \"Test Array\"]"));
1155    }
1156
1157    #[test]
1158    fn test_expr() {
1159        fm::<syn::Expr>(quote!(ignore = "x + y"));
1160        fm::<syn::Expr>(quote!(ignore = "an_object.method_call()"));
1161        fm::<syn::Expr>(quote!(ignore = "{ a_statement(); in_a_block }"));
1162    }
1163
1164    #[test]
1165    fn test_expr_without_quotes() {
1166        fm::<syn::Expr>(quote!(ignore = x + y));
1167        fm::<syn::Expr>(quote!(ignore = an_object.method_call()));
1168        fm::<syn::Expr>(quote!(
1169            ignore = {
1170                a_statement();
1171                in_a_block
1172            }
1173        ));
1174    }
1175
1176    #[test]
1177    fn test_expr_path() {
1178        fm::<syn::ExprPath>(quote!(ignore = "std::mem::replace"));
1179        fm::<syn::ExprPath>(quote!(ignore = "x"));
1180        fm::<syn::ExprPath>(quote!(ignore = "example::<Test>"));
1181    }
1182
1183    #[test]
1184    fn test_expr_path_without_quotes() {
1185        fm::<syn::ExprPath>(quote!(ignore = std::mem::replace));
1186        fm::<syn::ExprPath>(quote!(ignore = x));
1187        fm::<syn::ExprPath>(quote!(ignore = example::<Test>));
1188    }
1189
1190    #[test]
1191    fn test_path_without_quotes() {
1192        fm::<syn::Path>(quote!(ignore = std::mem::replace));
1193        fm::<syn::Path>(quote!(ignore = x));
1194        fm::<syn::Path>(quote!(ignore = example::<Test>));
1195    }
1196
1197    #[test]
1198    fn test_number_array() {
1199        assert_eq!(fm::<Vec<u8>>(quote!(ignore = [16, 0xff])), vec![0x10, 0xff]);
1200        assert_eq!(
1201            fm::<Vec<u16>>(quote!(ignore = "[32, 0xffff]")),
1202            vec![0x20, 0xffff]
1203        );
1204        assert_eq!(
1205            fm::<Vec<u32>>(quote!(ignore = "[48, 0xffffffff]")),
1206            vec![0x30, 0xffffffff]
1207        );
1208        assert_eq!(
1209            fm::<Vec<u64>>(quote!(ignore = "[64, 0xffffffffffffffff]")),
1210            vec![0x40, 0xffffffffffffffff]
1211        );
1212        assert_eq!(
1213            fm::<Vec<usize>>(quote!(ignore = "[80, 0xffffffff]")),
1214            vec![0x50, 0xffffffff]
1215        );
1216    }
1217
1218    #[test]
1219    fn test_lit_array() {
1220        fm::<Vec<syn::LitStr>>(quote!(ignore = "[\"Hello World\", \"Test Array\"]"));
1221        fm::<Vec<syn::LitStr>>(quote!(ignore = ["Hello World", "Test Array"]));
1222        fm::<Vec<syn::LitChar>>(quote!(ignore = "['a', 'b', 'c']"));
1223        fm::<Vec<syn::LitBool>>(quote!(ignore = "[true]"));
1224        fm::<Vec<syn::LitStr>>(quote!(ignore = "[]"));
1225        fm::<Vec<syn::LitStr>>(quote!(ignore = []));
1226        fm::<Vec<syn::LitBool>>(quote!(ignore = [true, false]));
1227    }
1228
1229    #[test]
1230    fn expr_range_without_quotes() {
1231        fm::<syn::ExprRange>(quote!(ignore = 0..5));
1232        fm::<syn::ExprRange>(quote!(ignore = 0..=5));
1233        fm::<syn::ExprRange>(quote!(ignore = ..5));
1234        fm::<syn::ExprRange>(quote!(ignore = ..(x + y)));
1235    }
1236}