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
18pub 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 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 fn from_none() -> Option<Self> {
92 None
93 }
94
95 fn from_word() -> Result<Self> {
98 Err(Error::unsupported_format("word"))
99 }
100
101 #[allow(unused_variables)]
103 fn from_list(items: &[NestedMeta]) -> Result<Self> {
104 Err(Error::unsupported_format("list"))
105 }
106
107 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 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 #[allow(unused_variables)]
142 fn from_char(value: char) -> Result<Self> {
143 Err(Error::unexpected_type("char"))
144 }
145
146 #[allow(unused_variables)]
148 fn from_string(value: &str) -> Result<Self> {
149 Err(Error::unexpected_type("string"))
150 }
151
152 #[allow(unused_variables)]
154 fn from_bool(value: bool) -> Result<Self> {
155 Err(Error::unexpected_type("bool"))
156 }
157}
158
159impl 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)] 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)] 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
221macro_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
267macro_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
291impl<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
306impl 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), _ => 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
340impl 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), _ => 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 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), _ => Err(Error::unexpected_expr_type(expr)),
389 }
390 }
391}
392
393macro_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), _ => 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
431macro_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 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), _ => 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 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
631macro_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 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
658impl<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
668trait 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 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 let mut seen_keys = HashSet::with_capacity($nested.len());
758
759 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 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
803map!(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#[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 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 assert_eq!(fm::<bool>(quote!(ignore)), true);
846
847 assert_eq!(fm::<bool>(quote!(ignore = true)), true);
849 assert_eq!(fm::<bool>(quote!(ignore = false)), false);
850
851 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 assert_eq!(fm::<char>(quote!(ignore = '😬')), '😬');
860
861 assert_eq!(fm::<char>(quote!(ignore = "😬")), '😬');
863 }
864
865 #[test]
866 fn string_succeeds() {
867 assert_eq!(&fm::<String>(quote!(ignore = "world")), "world");
869
870 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)] 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 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)] 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 #[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 #[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 #[test]
1137 fn darling_result_succeeds() {
1138 fm::<Result<()>>(quote!(ignore)).unwrap();
1139 fm::<Result<()>>(quote!(ignore(world))).unwrap_err();
1140 }
1141
1142 #[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}