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: Error| e.with_span(node: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: Chars<'_> = s.chars();
198 let char1: Option = chars.next();
199 let char2: Option = chars.next();
200
201 if let (Some(char: char), None) = (char1, char2) {
202 Ok(char)
203 } else {
204 Err(Error::unexpected_type(ty:"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: &LitStr) = *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(lit: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: &LitStr) = *value {
348 v.parse().map_err(|_| Error::unknown_lit_str_value(v))
349 } else {
350 Err(Error::unexpected_lit_type(lit:value))
351 }
352 }
353
354 fn from_expr(expr: &Expr) -> Result<Self> {
355 match expr {
356 Expr::Lit(lit: &ExprLit) => Self::from_value(&lit.lit),
357 Expr::Path(path: &ExprPath) => Ok(path.path.clone()),
358 Expr::Group(group: &ExprGroup) => 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: WhereClause| c.predicates.into_iter().collect())
583 }
584
585 fn from_value(value: &Lit) -> Result<Self> {
586 if let syn::Lit::Str(s: &LitStr) = value {
587 syn::WhereClause::from_value(&syn::Lit::Str(syn::LitStr::new(
588 &format!("where {}", s.value()),
589 value.span(),
590 )))
591 .map(|c: WhereClause| c.predicates.into_iter().collect())
592 } else {
593 Err(Error::unexpected_lit_type(lit: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(op: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(node: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}
1237