1use std::borrow::Cow;
2use std::cell::RefCell;
3use std::collections::hash_map::HashMap;
4use std::collections::HashSet;
5use std::hash::BuildHasher;
6use std::rc::Rc;
7use std::sync::atomic::AtomicBool;
8use std::sync::Arc;
9
10use syn::{Expr, Lit, Meta, NestedMeta};
11
12use crate::{util::path_to_string, Error, Result};
13
14/// Create an instance from an item in an attribute declaration.
15///
16/// # Implementing `FromMeta`
17/// * 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.
18/// * Implement only the `from_*` methods that you intend to support. The default implementations will return useful errors.
19///
20/// # Provided Implementations
21/// ## bool
22///
23/// * Word with no value specified - becomes `true`.
24/// * As a boolean literal, e.g. `foo = true`.
25/// * As a string literal, e.g. `foo = "true"`.
26///
27/// ## char
28/// * As a char literal, e.g. `foo = '#'`.
29/// * As a string literal consisting of a single character, e.g. `foo = "#"`.
30///
31/// ## String
32/// * As a string literal, e.g. `foo = "hello"`.
33/// * As a raw string literal, e.g. `foo = r#"hello "world""#`.
34///
35/// ## Number
36/// * As a string literal, e.g. `foo = "-25"`.
37/// * As an unquoted positive value, e.g. `foo = 404`. Negative numbers must be in quotation marks.
38///
39/// ## ()
40/// * Word with no value specified, e.g. `foo`. This is best used with `Option`.
41/// See `darling::util::Flag` for a more strongly-typed alternative.
42///
43/// ## Option
44/// * Any format produces `Some`.
45///
46/// ## `Result<T, darling::Error>`
47/// * Allows for fallible parsing; will populate the target field with the result of the
48/// parse attempt.
49pub trait FromMeta: Sized {
50 fn from_nested_meta(item: &NestedMeta) -> Result<Self> {
51 (match *item {
52 NestedMeta::Lit(ref lit) => Self::from_value(lit),
53 NestedMeta::Meta(ref mi) => Self::from_meta(mi),
54 })
55 .map_err(|e| e.with_span(item))
56 }
57
58 /// Create an instance from a `syn::Meta` by dispatching to the format-appropriate
59 /// trait function. This generally should not be overridden by implementers.
60 ///
61 /// # Error Spans
62 /// If this method is overridden and can introduce errors that weren't passed up from
63 /// other `from_meta` calls, the override must call `with_span` on the error using the
64 /// `item` to make sure that the emitted diagnostic points to the correct location in
65 /// source code.
66 fn from_meta(item: &Meta) -> Result<Self> {
67 (match *item {
68 Meta::Path(_) => Self::from_word(),
69 Meta::List(ref value) => Self::from_list(
70 &value
71 .nested
72 .iter()
73 .cloned()
74 .collect::<Vec<syn::NestedMeta>>()[..],
75 ),
76 Meta::NameValue(ref value) => Self::from_value(&value.lit),
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 /// Create an instance from a char literal in a value position.
125 #[allow(unused_variables)]
126 fn from_char(value: char) -> Result<Self> {
127 Err(Error::unexpected_type("char"))
128 }
129
130 /// Create an instance from a string literal in a value position.
131 #[allow(unused_variables)]
132 fn from_string(value: &str) -> Result<Self> {
133 Err(Error::unexpected_type("string"))
134 }
135
136 /// Create an instance from a bool literal in a value position.
137 #[allow(unused_variables)]
138 fn from_bool(value: bool) -> Result<Self> {
139 Err(Error::unexpected_type("bool"))
140 }
141}
142
143// FromMeta impls for std and syn types.
144
145impl FromMeta for () {
146 fn from_word() -> Result<Self> {
147 Ok(())
148 }
149}
150
151impl FromMeta for bool {
152 fn from_word() -> Result<Self> {
153 Ok(true)
154 }
155
156 #[allow(clippy::wrong_self_convention)] // false positive
157 fn from_bool(value: bool) -> Result<Self> {
158 Ok(value)
159 }
160
161 fn from_string(value: &str) -> Result<Self> {
162 value.parse().map_err(|_| Error::unknown_value(value))
163 }
164}
165
166impl FromMeta for AtomicBool {
167 fn from_meta(mi: &Meta) -> Result<Self> {
168 FromMeta::from_meta(mi)
169 .map(AtomicBool::new)
170 .map_err(|e: Error| e.with_span(node:mi))
171 }
172}
173
174impl FromMeta for char {
175 #[allow(clippy::wrong_self_convention)] // false positive
176 fn from_char(value: char) -> Result<Self> {
177 Ok(value)
178 }
179
180 fn from_string(s: &str) -> Result<Self> {
181 let mut chars: Chars<'_> = s.chars();
182 let char1: Option = chars.next();
183 let char2: Option = chars.next();
184
185 if let (Some(char: char), None) = (char1, char2) {
186 Ok(char)
187 } else {
188 Err(Error::unexpected_type(ty:"string"))
189 }
190 }
191}
192
193impl FromMeta for String {
194 fn from_string(s: &str) -> Result<Self> {
195 Ok(s.to_string())
196 }
197}
198
199/// Generate an impl of `FromMeta` that will accept strings which parse to numbers or
200/// integer literals.
201macro_rules! from_meta_num {
202 ($ty:ident) => {
203 impl FromMeta for $ty {
204 fn from_string(s: &str) -> Result<Self> {
205 s.parse().map_err(|_| Error::unknown_value(s))
206 }
207
208 fn from_value(value: &Lit) -> Result<Self> {
209 (match *value {
210 Lit::Str(ref s) => Self::from_string(&s.value()),
211 Lit::Int(ref s) => Ok(s.base10_parse::<$ty>().unwrap()),
212 _ => Err(Error::unexpected_lit_type(value)),
213 })
214 .map_err(|e| e.with_span(value))
215 }
216 }
217 };
218}
219
220from_meta_num!(u8);
221from_meta_num!(u16);
222from_meta_num!(u32);
223from_meta_num!(u64);
224from_meta_num!(usize);
225from_meta_num!(i8);
226from_meta_num!(i16);
227from_meta_num!(i32);
228from_meta_num!(i64);
229from_meta_num!(isize);
230
231/// Generate an impl of `FromMeta` that will accept strings which parse to floats or
232/// float literals.
233macro_rules! from_meta_float {
234 ($ty:ident) => {
235 impl FromMeta for $ty {
236 fn from_string(s: &str) -> Result<Self> {
237 s.parse().map_err(|_| Error::unknown_value(s))
238 }
239
240 fn from_value(value: &Lit) -> Result<Self> {
241 (match *value {
242 Lit::Str(ref s) => Self::from_string(&s.value()),
243 Lit::Float(ref s) => Ok(s.base10_parse::<$ty>().unwrap()),
244 _ => Err(Error::unexpected_lit_type(value)),
245 })
246 .map_err(|e| e.with_span(value))
247 }
248 }
249 };
250}
251
252from_meta_float!(f32);
253from_meta_float!(f64);
254
255/// Parsing support for punctuated. This attempts to preserve span information
256/// when available, but also supports parsing strings with the call site as the
257/// emitted span.
258impl<T: syn::parse::Parse, P: syn::parse::Parse> FromMeta for syn::punctuated::Punctuated<T, P> {
259 fn from_value(value: &Lit) -> Result<Self> {
260 if let Lit::Str(ref ident: &LitStr) = *value {
261 ident
262 .parse_with(syn::punctuated::Punctuated::parse_terminated)
263 .map_err(|_| Error::unknown_lit_str_value(ident))
264 } else {
265 Err(Error::unexpected_lit_type(lit:value))
266 }
267 }
268}
269
270/// Adapter from `syn::parse::Parse` to `FromMeta`.
271///
272/// This cannot be a blanket impl, due to the `syn::Lit` family's need to handle non-string values.
273/// Therefore, we use a macro and a lot of impls.
274macro_rules! from_syn_parse {
275 ($ty:path) => {
276 impl FromMeta for $ty {
277 fn from_string(value: &str) -> Result<Self> {
278 syn::parse_str(value).map_err(|_| Error::unknown_value(value))
279 }
280
281 fn from_value(value: &::syn::Lit) -> Result<Self> {
282 if let ::syn::Lit::Str(ref v) = *value {
283 v.parse::<$ty>()
284 .map_err(|_| Error::unknown_lit_str_value(v))
285 } else {
286 Err(Error::unexpected_lit_type(value))
287 }
288 }
289 }
290 };
291}
292
293from_syn_parse!(syn::Ident);
294from_syn_parse!(syn::Expr);
295from_syn_parse!(syn::ExprArray);
296from_syn_parse!(syn::ExprPath);
297from_syn_parse!(syn::Path);
298from_syn_parse!(syn::Type);
299from_syn_parse!(syn::TypeArray);
300from_syn_parse!(syn::TypeBareFn);
301from_syn_parse!(syn::TypeGroup);
302from_syn_parse!(syn::TypeImplTrait);
303from_syn_parse!(syn::TypeInfer);
304from_syn_parse!(syn::TypeMacro);
305from_syn_parse!(syn::TypeNever);
306from_syn_parse!(syn::TypeParam);
307from_syn_parse!(syn::TypeParen);
308from_syn_parse!(syn::TypePath);
309from_syn_parse!(syn::TypePtr);
310from_syn_parse!(syn::TypeReference);
311from_syn_parse!(syn::TypeSlice);
312from_syn_parse!(syn::TypeTraitObject);
313from_syn_parse!(syn::TypeTuple);
314from_syn_parse!(syn::Visibility);
315from_syn_parse!(syn::WhereClause);
316
317macro_rules! from_numeric_array {
318 ($ty:ident) => {
319 /// Parsing an unsigned integer array, i.e. `example = "[1, 2, 3, 4]"`.
320 impl FromMeta for Vec<$ty> {
321 fn from_value(value: &Lit) -> Result<Self> {
322 let expr_array = syn::ExprArray::from_value(value)?;
323 // To meet rust <1.36 borrow checker rules on expr_array.elems
324 let v =
325 expr_array
326 .elems
327 .iter()
328 .map(|expr| match expr {
329 Expr::Lit(lit) => $ty::from_value(&lit.lit),
330 _ => Err(Error::custom("Expected array of unsigned integers")
331 .with_span(expr)),
332 })
333 .collect::<Result<Vec<$ty>>>();
334 v
335 }
336 }
337 };
338}
339
340from_numeric_array!(u8);
341from_numeric_array!(u16);
342from_numeric_array!(u32);
343from_numeric_array!(u64);
344from_numeric_array!(usize);
345
346impl FromMeta for syn::Lit {
347 fn from_value(value: &Lit) -> Result<Self> {
348 Ok(value.clone())
349 }
350}
351
352macro_rules! from_meta_lit {
353 ($impl_ty:path, $lit_variant:path) => {
354 impl FromMeta for $impl_ty {
355 fn from_value(value: &Lit) -> Result<Self> {
356 if let $lit_variant(ref value) = *value {
357 Ok(value.clone())
358 } else {
359 Err(Error::unexpected_lit_type(value))
360 }
361 }
362 }
363 };
364}
365
366from_meta_lit!(syn::LitInt, Lit::Int);
367from_meta_lit!(syn::LitFloat, Lit::Float);
368from_meta_lit!(syn::LitStr, Lit::Str);
369from_meta_lit!(syn::LitByte, Lit::Byte);
370from_meta_lit!(syn::LitByteStr, Lit::ByteStr);
371from_meta_lit!(syn::LitChar, Lit::Char);
372from_meta_lit!(syn::LitBool, Lit::Bool);
373from_meta_lit!(proc_macro2::Literal, Lit::Verbatim);
374
375impl FromMeta for syn::Meta {
376 fn from_meta(value: &syn::Meta) -> Result<Self> {
377 Ok(value.clone())
378 }
379}
380
381impl FromMeta for Vec<syn::WherePredicate> {
382 fn from_string(value: &str) -> Result<Self> {
383 syn::WhereClause::from_string(&format!("where {}", value))
384 .map(|c: WhereClause| c.predicates.into_iter().collect())
385 }
386
387 fn from_value(value: &Lit) -> Result<Self> {
388 if let syn::Lit::Str(s: &LitStr) = value {
389 syn::WhereClause::from_value(&syn::Lit::Str(syn::LitStr::new(
390 &format!("where {}", s.value()),
391 value.span(),
392 )))
393 .map(|c: WhereClause| c.predicates.into_iter().collect())
394 } else {
395 Err(Error::unexpected_lit_type(lit:value))
396 }
397 }
398}
399
400impl FromMeta for ident_case::RenameRule {
401 fn from_string(value: &str) -> Result<Self> {
402 value.parse().map_err(|_| Error::unknown_value(value))
403 }
404}
405
406impl<T: FromMeta> FromMeta for Option<T> {
407 fn from_none() -> Option<Self> {
408 Some(None)
409 }
410
411 fn from_meta(item: &Meta) -> Result<Self> {
412 FromMeta::from_meta(item).map(op:Some)
413 }
414}
415
416impl<T: FromMeta> FromMeta for Box<T> {
417 fn from_none() -> Option<Self> {
418 T::from_none().map(Box::new)
419 }
420
421 fn from_meta(item: &Meta) -> Result<Self> {
422 FromMeta::from_meta(item).map(op:Box::new)
423 }
424}
425
426impl<T: FromMeta> FromMeta for Result<T> {
427 fn from_none() -> Option<Self> {
428 T::from_none().map(Ok)
429 }
430
431 fn from_meta(item: &Meta) -> Result<Self> {
432 Ok(FromMeta::from_meta(item))
433 }
434}
435
436/// Parses the meta-item, and in case of error preserves a copy of the input for
437/// later analysis.
438impl<T: FromMeta> FromMeta for ::std::result::Result<T, Meta> {
439 fn from_meta(item: &Meta) -> Result<Self> {
440 T::from_meta(item)
441 .map(Ok)
442 .or_else(|_| Ok(Err(item.clone())))
443 }
444}
445
446impl<T: FromMeta> FromMeta for Rc<T> {
447 fn from_none() -> Option<Self> {
448 T::from_none().map(Rc::new)
449 }
450
451 fn from_meta(item: &Meta) -> Result<Self> {
452 FromMeta::from_meta(item).map(op:Rc::new)
453 }
454}
455
456impl<T: FromMeta> FromMeta for Arc<T> {
457 fn from_none() -> Option<Self> {
458 T::from_none().map(Arc::new)
459 }
460
461 fn from_meta(item: &Meta) -> Result<Self> {
462 FromMeta::from_meta(item).map(op:Arc::new)
463 }
464}
465
466impl<T: FromMeta> FromMeta for RefCell<T> {
467 fn from_none() -> Option<Self> {
468 T::from_none().map(RefCell::new)
469 }
470
471 fn from_meta(item: &Meta) -> Result<Self> {
472 FromMeta::from_meta(item).map(op:RefCell::new)
473 }
474}
475
476/// Trait to convert from a path into an owned key for a map.
477trait KeyFromPath: Sized {
478 fn from_path(path: &syn::Path) -> Result<Self>;
479 fn to_display(&self) -> Cow<'_, str>;
480}
481
482impl KeyFromPath for String {
483 fn from_path(path: &syn::Path) -> Result<Self> {
484 Ok(path_to_string(path))
485 }
486
487 fn to_display(&self) -> Cow<'_, str> {
488 Cow::Borrowed(self)
489 }
490}
491
492impl KeyFromPath for syn::Path {
493 fn from_path(path: &syn::Path) -> Result<Self> {
494 Ok(path.clone())
495 }
496
497 fn to_display(&self) -> Cow<'_, str> {
498 Cow::Owned(path_to_string(self))
499 }
500}
501
502impl KeyFromPath for syn::Ident {
503 fn from_path(path: &syn::Path) -> Result<Self> {
504 if path.segments.len() == 1
505 && path.leading_colon.is_none()
506 && path.segments[0].arguments.is_empty()
507 {
508 Ok(path.segments[0].ident.clone())
509 } else {
510 Err(Error::custom("Key must be an identifier").with_span(node:path))
511 }
512 }
513
514 fn to_display(&self) -> Cow<'_, str> {
515 Cow::Owned(self.to_string())
516 }
517}
518
519macro_rules! hash_map {
520 ($key:ty) => {
521 impl<V: FromMeta, S: BuildHasher + Default> FromMeta for HashMap<$key, V, S> {
522 fn from_list(nested: &[syn::NestedMeta]) -> Result<Self> {
523 // Convert the nested meta items into a sequence of (path, value result) result tuples.
524 // An outer Err means no (key, value) structured could be found, while an Err in the
525 // second position of the tuple means that value was rejected by FromMeta.
526 //
527 // We defer key conversion into $key so that we don't lose span information in the case
528 // of String keys; we'll need it for good duplicate key errors later.
529 let pairs = nested
530 .iter()
531 .map(|item| -> Result<(&syn::Path, Result<V>)> {
532 match *item {
533 syn::NestedMeta::Meta(ref inner) => {
534 let path = inner.path();
535 Ok((
536 path,
537 FromMeta::from_meta(inner).map_err(|e| e.at_path(&path)),
538 ))
539 }
540 syn::NestedMeta::Lit(_) => Err(Error::unsupported_format("literal")),
541 }
542 });
543
544 let mut errors = Error::accumulator();
545 // We need to track seen keys separately from the final map, since a seen key with an
546 // Err value won't go into the final map but should trigger a duplicate field error.
547 //
548 // This is a set of $key rather than Path to avoid the possibility that a key type
549 // parses two paths of different values to the same key value.
550 let mut seen_keys = HashSet::with_capacity(nested.len());
551
552 // The map to return in the Ok case. Its size will always be exactly nested.len(),
553 // since otherwise ≥1 field had a problem and the entire map is dropped immediately
554 // when the function returns `Err`.
555 let mut map = HashMap::with_capacity_and_hasher(nested.len(), Default::default());
556
557 for item in pairs {
558 if let Some((path, value)) = errors.handle(item) {
559 let key: $key = match KeyFromPath::from_path(path) {
560 Ok(k) => k,
561 Err(e) => {
562 errors.push(e);
563
564 // Surface value errors even under invalid keys
565 errors.handle(value);
566
567 continue;
568 }
569 };
570
571 let already_seen = seen_keys.contains(&key);
572
573 if already_seen {
574 errors.push(Error::duplicate_field(&key.to_display()).with_span(path));
575 }
576
577 match value {
578 Ok(_) if already_seen => {}
579 Ok(val) => {
580 map.insert(key.clone(), val);
581 }
582 Err(e) => {
583 errors.push(e);
584 }
585 }
586
587 seen_keys.insert(key);
588 }
589 }
590
591 errors.finish_with(map)
592 }
593 }
594 };
595}
596
597// This is done as a macro rather than a blanket impl to avoid breaking backwards compatibility
598// with 0.12.x, while still sharing the same impl.
599hash_map!(String);
600hash_map!(syn::Ident);
601hash_map!(syn::Path);
602
603/// Tests for `FromMeta` implementations. Wherever the word `ignore` appears in test input,
604/// it should not be considered by the parsing.
605#[cfg(test)]
606mod tests {
607 use proc_macro2::TokenStream;
608 use quote::quote;
609 use syn::parse_quote;
610
611 use crate::{Error, FromMeta, Result};
612
613 /// parse a string as a syn::Meta instance.
614 fn pm(tokens: TokenStream) -> ::std::result::Result<syn::Meta, String> {
615 let attribute: syn::Attribute = parse_quote!(#[#tokens]);
616 attribute.parse_meta().map_err(|_| "Unable to parse".into())
617 }
618
619 fn fm<T: FromMeta>(tokens: TokenStream) -> T {
620 FromMeta::from_meta(&pm(tokens).expect("Tests should pass well-formed input"))
621 .expect("Tests should pass valid input")
622 }
623
624 #[test]
625 fn unit_succeeds() {
626 fm::<()>(quote!(ignore));
627 }
628
629 #[test]
630 #[allow(clippy::bool_assert_comparison)]
631 fn bool_succeeds() {
632 // word format
633 assert_eq!(fm::<bool>(quote!(ignore)), true);
634
635 // bool literal
636 assert_eq!(fm::<bool>(quote!(ignore = true)), true);
637 assert_eq!(fm::<bool>(quote!(ignore = false)), false);
638
639 // string literals
640 assert_eq!(fm::<bool>(quote!(ignore = "true")), true);
641 assert_eq!(fm::<bool>(quote!(ignore = "false")), false);
642 }
643
644 #[test]
645 fn char_succeeds() {
646 // char literal
647 assert_eq!(fm::<char>(quote!(ignore = '😬')), '😬');
648
649 // string literal
650 assert_eq!(fm::<char>(quote!(ignore = "😬")), '😬');
651 }
652
653 #[test]
654 fn string_succeeds() {
655 // cooked form
656 assert_eq!(&fm::<String>(quote!(ignore = "world")), "world");
657
658 // raw form
659 assert_eq!(&fm::<String>(quote!(ignore = r#"world"#)), "world");
660 }
661
662 #[test]
663 #[allow(clippy::float_cmp)] // we want exact equality
664 fn number_succeeds() {
665 assert_eq!(fm::<u8>(quote!(ignore = "2")), 2u8);
666 assert_eq!(fm::<i16>(quote!(ignore = "-25")), -25i16);
667 assert_eq!(fm::<f64>(quote!(ignore = "1.4e10")), 1.4e10);
668 }
669
670 #[test]
671 fn int_without_quotes() {
672 assert_eq!(fm::<u8>(quote!(ignore = 2)), 2u8);
673 assert_eq!(fm::<u16>(quote!(ignore = 255)), 255u16);
674 assert_eq!(fm::<u32>(quote!(ignore = 5000)), 5000u32);
675
676 // Check that we aren't tripped up by incorrect suffixes
677 assert_eq!(fm::<u32>(quote!(ignore = 5000i32)), 5000u32);
678 }
679
680 #[test]
681 #[allow(clippy::float_cmp)] // we want exact equality
682 fn float_without_quotes() {
683 assert_eq!(fm::<f32>(quote!(ignore = 2.)), 2.0f32);
684 assert_eq!(fm::<f32>(quote!(ignore = 2.0)), 2.0f32);
685 assert_eq!(fm::<f64>(quote!(ignore = 1.4e10)), 1.4e10f64);
686 }
687
688 #[test]
689 fn meta_succeeds() {
690 use syn::Meta;
691
692 assert_eq!(
693 fm::<Meta>(quote!(hello(world, today))),
694 pm(quote!(hello(world, today))).unwrap()
695 );
696 }
697
698 #[test]
699 fn hash_map_succeeds() {
700 use std::collections::HashMap;
701
702 let comparison = {
703 let mut c = HashMap::new();
704 c.insert("hello".to_string(), true);
705 c.insert("world".to_string(), false);
706 c.insert("there".to_string(), true);
707 c
708 };
709
710 assert_eq!(
711 fm::<HashMap<String, bool>>(quote!(ignore(hello, world = false, there = "true"))),
712 comparison
713 );
714 }
715
716 /// Check that a `HashMap` cannot have duplicate keys, and that the generated error
717 /// is assigned a span to correctly target the diagnostic message.
718 #[test]
719 fn hash_map_duplicate() {
720 use std::collections::HashMap;
721
722 let err: Result<HashMap<String, bool>> =
723 FromMeta::from_meta(&pm(quote!(ignore(hello, hello = false))).unwrap());
724
725 let err = err.expect_err("Duplicate keys in HashMap should error");
726
727 assert!(err.has_span());
728 assert_eq!(err.to_string(), Error::duplicate_field("hello").to_string());
729 }
730
731 #[test]
732 fn hash_map_multiple_errors() {
733 use std::collections::HashMap;
734
735 let err = HashMap::<String, bool>::from_meta(
736 &pm(quote!(ignore(hello, hello = 3, hello = false))).unwrap(),
737 )
738 .expect_err("Duplicates and bad values should error");
739
740 assert_eq!(err.len(), 3);
741 let errors = err.into_iter().collect::<Vec<_>>();
742 assert!(errors[0].has_span());
743 assert!(errors[1].has_span());
744 assert!(errors[2].has_span());
745 }
746
747 #[test]
748 fn hash_map_ident_succeeds() {
749 use std::collections::HashMap;
750 use syn::parse_quote;
751
752 let comparison = {
753 let mut c = HashMap::<syn::Ident, bool>::new();
754 c.insert(parse_quote!(first), true);
755 c.insert(parse_quote!(second), false);
756 c
757 };
758
759 assert_eq!(
760 fm::<HashMap<syn::Ident, bool>>(quote!(ignore(first, second = false))),
761 comparison
762 );
763 }
764
765 #[test]
766 fn hash_map_ident_rejects_non_idents() {
767 use std::collections::HashMap;
768
769 let err: Result<HashMap<syn::Ident, bool>> =
770 FromMeta::from_meta(&pm(quote!(ignore(first, the::second))).unwrap());
771
772 err.unwrap_err();
773 }
774
775 #[test]
776 fn hash_map_path_succeeds() {
777 use std::collections::HashMap;
778 use syn::parse_quote;
779
780 let comparison = {
781 let mut c = HashMap::<syn::Path, bool>::new();
782 c.insert(parse_quote!(first), true);
783 c.insert(parse_quote!(the::second), false);
784 c
785 };
786
787 assert_eq!(
788 fm::<HashMap<syn::Path, bool>>(quote!(ignore(first, the::second = false))),
789 comparison
790 );
791 }
792
793 /// Tests that fallible parsing will always produce an outer `Ok` (from `fm`),
794 /// and will accurately preserve the inner contents.
795 #[test]
796 fn darling_result_succeeds() {
797 fm::<Result<()>>(quote!(ignore)).unwrap();
798 fm::<Result<()>>(quote!(ignore(world))).unwrap_err();
799 }
800
801 /// Test punctuated
802 #[test]
803 fn test_punctuated() {
804 fm::<syn::punctuated::Punctuated<syn::FnArg, syn::token::Comma>>(quote!(
805 ignore = "a: u8, b: Type"
806 ));
807 fm::<syn::punctuated::Punctuated<syn::Expr, syn::token::Comma>>(quote!(ignore = "a, b, c"));
808 }
809
810 #[test]
811 fn test_expr_array() {
812 fm::<syn::ExprArray>(quote!(ignore = "[0x1, 0x2]"));
813 fm::<syn::ExprArray>(quote!(ignore = "[\"Hello World\", \"Test Array\"]"));
814 }
815
816 #[test]
817 fn test_expr() {
818 fm::<syn::Expr>(quote!(ignore = "x + y"));
819 fm::<syn::Expr>(quote!(ignore = "an_object.method_call()"));
820 fm::<syn::Expr>(quote!(ignore = "{ a_statement(); in_a_block }"));
821 }
822
823 #[test]
824 fn test_expr_path() {
825 fm::<syn::ExprPath>(quote!(ignore = "std::mem::replace"));
826 fm::<syn::ExprPath>(quote!(ignore = "x"));
827 fm::<syn::ExprPath>(quote!(ignore = "example::<Test>"));
828 }
829
830 #[test]
831 fn test_number_array() {
832 assert_eq!(
833 fm::<Vec<u8>>(quote!(ignore = "[16, 0xff]")),
834 vec![0x10, 0xff]
835 );
836 assert_eq!(
837 fm::<Vec<u16>>(quote!(ignore = "[32, 0xffff]")),
838 vec![0x20, 0xffff]
839 );
840 assert_eq!(
841 fm::<Vec<u32>>(quote!(ignore = "[48, 0xffffffff]")),
842 vec![0x30, 0xffffffff]
843 );
844 assert_eq!(
845 fm::<Vec<u64>>(quote!(ignore = "[64, 0xffffffffffffffff]")),
846 vec![0x40, 0xffffffffffffffff]
847 );
848 assert_eq!(
849 fm::<Vec<usize>>(quote!(ignore = "[80, 0xffffffff]")),
850 vec![0x50, 0xffffffff]
851 );
852 }
853}
854