1use std::borrow::Cow;
2use std::cell::RefCell;
3use std::collections::hash_map::HashMap;
4use std::collections::HashSet;
5use std::hash::BuildHasher;
6use std::num;
7use std::rc::Rc;
8use std::sync::atomic::AtomicBool;
9use std::sync::Arc;
10
11use syn::{Expr, Lit, Meta};
12
13use crate::ast::NestedMeta;
14use crate::util::path_to_string;
15use crate::{Error, Result};
16
17pub trait FromMeta: Sized {
53 fn from_nested_meta(item: &NestedMeta) -> Result<Self> {
54 (match *item {
55 NestedMeta::Lit(ref lit) => Self::from_value(lit),
56 NestedMeta::Meta(ref mi) => Self::from_meta(mi),
57 })
58 .map_err(|e| e.with_span(item))
59 }
60
61 fn from_meta(item: &Meta) -> Result<Self> {
70 (match *item {
71 Meta::Path(_) => Self::from_word(),
72 Meta::List(ref value) => {
73 Self::from_list(&NestedMeta::parse_meta_list(value.tokens.clone())?[..])
74 }
75 Meta::NameValue(ref value) => Self::from_expr(&value.value),
76 })
77 .map_err(|e| e.with_span(item))
78 }
79
80 fn from_none() -> Option<Self> {
91 None
92 }
93
94 fn from_word() -> Result<Self> {
97 Err(Error::unsupported_format("word"))
98 }
99
100 #[allow(unused_variables)]
102 fn from_list(items: &[NestedMeta]) -> Result<Self> {
103 Err(Error::unsupported_format("list"))
104 }
105
106 fn from_value(value: &Lit) -> Result<Self> {
114 (match *value {
115 Lit::Bool(ref b) => Self::from_bool(b.value),
116 Lit::Str(ref s) => Self::from_string(&s.value()),
117 Lit::Char(ref ch) => Self::from_char(ch.value()),
118 _ => Err(Error::unexpected_lit_type(value)),
119 })
120 .map_err(|e| e.with_span(value))
121 }
122
123 fn from_expr(expr: &Expr) -> Result<Self> {
124 match *expr {
125 Expr::Lit(ref lit) => Self::from_value(&lit.lit),
126 Expr::Group(ref group) => {
127 Self::from_expr(&group.expr)
133 }
134 _ => Err(Error::unexpected_expr_type(expr)),
135 }
136 .map_err(|e| e.with_span(expr))
137 }
138
139 #[allow(unused_variables)]
141 fn from_char(value: char) -> Result<Self> {
142 Err(Error::unexpected_type("char"))
143 }
144
145 #[allow(unused_variables)]
147 fn from_string(value: &str) -> Result<Self> {
148 Err(Error::unexpected_type("string"))
149 }
150
151 #[allow(unused_variables)]
153 fn from_bool(value: bool) -> Result<Self> {
154 Err(Error::unexpected_type("bool"))
155 }
156}
157
158impl FromMeta for () {
161 fn from_word() -> Result<Self> {
162 Ok(())
163 }
164}
165
166impl FromMeta for bool {
167 fn from_word() -> Result<Self> {
168 Ok(true)
169 }
170
171 #[allow(clippy::wrong_self_convention)] fn from_bool(value: bool) -> Result<Self> {
173 Ok(value)
174 }
175
176 fn from_string(value: &str) -> Result<Self> {
177 value.parse().map_err(|_| Error::unknown_value(value))
178 }
179}
180
181impl FromMeta for AtomicBool {
182 fn from_meta(mi: &Meta) -> Result<Self> {
183 FromMeta::from_meta(mi)
184 .map(AtomicBool::new)
185 .map_err(|e| e.with_span(mi))
186 }
187}
188
189impl FromMeta for char {
190 #[allow(clippy::wrong_self_convention)] fn from_char(value: char) -> Result<Self> {
192 Ok(value)
193 }
194
195 fn from_string(s: &str) -> Result<Self> {
196 let mut chars = s.chars();
197 let char1 = chars.next();
198 let char2 = chars.next();
199
200 if let (Some(char), None) = (char1, char2) {
201 Ok(char)
202 } else {
203 Err(Error::unexpected_type("string"))
204 }
205 }
206}
207
208impl FromMeta for String {
209 fn from_string(s: &str) -> Result<Self> {
210 Ok(s.to_string())
211 }
212}
213
214impl FromMeta for std::path::PathBuf {
215 fn from_string(s: &str) -> Result<Self> {
216 Ok(s.into())
217 }
218}
219
220macro_rules! from_meta_num {
223 ($ty:path) => {
224 impl FromMeta for $ty {
225 fn from_string(s: &str) -> Result<Self> {
226 s.parse().map_err(|_| Error::unknown_value(s))
227 }
228
229 fn from_value(value: &Lit) -> Result<Self> {
230 (match *value {
231 Lit::Str(ref s) => Self::from_string(&s.value()),
232 Lit::Int(ref s) => s.base10_parse::<$ty>().map_err(Error::from),
233 _ => Err(Error::unexpected_lit_type(value)),
234 })
235 .map_err(|e| e.with_span(value))
236 }
237 }
238 };
239}
240
241from_meta_num!(u8);
242from_meta_num!(u16);
243from_meta_num!(u32);
244from_meta_num!(u64);
245from_meta_num!(u128);
246from_meta_num!(usize);
247from_meta_num!(i8);
248from_meta_num!(i16);
249from_meta_num!(i32);
250from_meta_num!(i64);
251from_meta_num!(i128);
252from_meta_num!(isize);
253from_meta_num!(num::NonZeroU8);
254from_meta_num!(num::NonZeroU16);
255from_meta_num!(num::NonZeroU32);
256from_meta_num!(num::NonZeroU64);
257from_meta_num!(num::NonZeroU128);
258from_meta_num!(num::NonZeroUsize);
259from_meta_num!(num::NonZeroI8);
260from_meta_num!(num::NonZeroI16);
261from_meta_num!(num::NonZeroI32);
262from_meta_num!(num::NonZeroI64);
263from_meta_num!(num::NonZeroI128);
264from_meta_num!(num::NonZeroIsize);
265
266macro_rules! from_meta_float {
269 ($ty:ident) => {
270 impl FromMeta for $ty {
271 fn from_string(s: &str) -> Result<Self> {
272 s.parse().map_err(|_| Error::unknown_value(s))
273 }
274
275 fn from_value(value: &Lit) -> Result<Self> {
276 (match *value {
277 Lit::Str(ref s) => Self::from_string(&s.value()),
278 Lit::Float(ref s) => s.base10_parse::<$ty>().map_err(Error::from),
279 _ => Err(Error::unexpected_lit_type(value)),
280 })
281 .map_err(|e| e.with_span(value))
282 }
283 }
284 };
285}
286
287from_meta_float!(f32);
288from_meta_float!(f64);
289
290impl<T: syn::parse::Parse, P: syn::parse::Parse> FromMeta for syn::punctuated::Punctuated<T, P> {
294 fn from_value(value: &Lit) -> Result<Self> {
295 if let Lit::Str(ref ident) = *value {
296 ident
297 .parse_with(syn::punctuated::Punctuated::parse_terminated)
298 .map_err(|_| Error::unknown_lit_str_value(ident))
299 } else {
300 Err(Error::unexpected_lit_type(value))
301 }
302 }
303}
304
305impl FromMeta for syn::Expr {
314 fn from_expr(expr: &Expr) -> Result<Self> {
315 match expr {
316 Expr::Lit(syn::ExprLit {
317 lit: lit @ syn::Lit::Str(_),
318 ..
319 }) => Self::from_value(lit),
320 Expr::Group(group) => Self::from_expr(&group.expr), _ => Ok(expr.clone()),
322 }
323 }
324
325 fn from_string(value: &str) -> Result<Self> {
326 syn::parse_str(value).map_err(|_| Error::unknown_value(value))
327 }
328
329 fn from_value(value: &::syn::Lit) -> Result<Self> {
330 if let ::syn::Lit::Str(ref v) = *value {
331 v.parse::<syn::Expr>()
332 .map_err(|_| Error::unknown_lit_str_value(v))
333 } else {
334 Err(Error::unexpected_lit_type(value))
335 }
336 }
337}
338
339impl FromMeta for syn::Path {
341 fn from_string(value: &str) -> Result<Self> {
342 syn::parse_str(value).map_err(|_| Error::unknown_value(value))
343 }
344
345 fn from_value(value: &::syn::Lit) -> Result<Self> {
346 if let ::syn::Lit::Str(ref v) = *value {
347 v.parse().map_err(|_| Error::unknown_lit_str_value(v))
348 } else {
349 Err(Error::unexpected_lit_type(value))
350 }
351 }
352
353 fn from_expr(expr: &Expr) -> Result<Self> {
354 match expr {
355 Expr::Lit(lit) => Self::from_value(&lit.lit),
356 Expr::Path(path) => Ok(path.path.clone()),
357 Expr::Group(group) => Self::from_expr(&group.expr), _ => Err(Error::unexpected_expr_type(expr)),
359 }
360 }
361}
362
363impl FromMeta for syn::Ident {
364 fn from_string(value: &str) -> Result<Self> {
365 syn::parse_str(value).map_err(|_| Error::unknown_value(value))
366 }
367
368 fn from_value(value: &syn::Lit) -> Result<Self> {
369 if let syn::Lit::Str(ref v) = *value {
370 v.parse().map_err(|_| Error::unknown_lit_str_value(v))
371 } else {
372 Err(Error::unexpected_lit_type(value))
373 }
374 }
375
376 fn from_expr(expr: &Expr) -> Result<Self> {
377 match expr {
378 Expr::Lit(lit) => Self::from_value(&lit.lit),
379 Expr::Path(path) => match path.path.get_ident() {
383 Some(ident) => Ok(ident.clone()),
384 None => Err(Error::unexpected_expr_type(expr)),
385 },
386 Expr::Group(group) => Self::from_expr(&group.expr), _ => Err(Error::unexpected_expr_type(expr)),
388 }
389 }
390}
391
392macro_rules! from_syn_expr_type {
403 ($ty:path, $variant:ident) => {
404 impl FromMeta for $ty {
405 fn from_expr(expr: &syn::Expr) -> Result<Self> {
406 match expr {
407 syn::Expr::$variant(body) => Ok(body.clone()),
408 syn::Expr::Lit(expr_lit) => Self::from_value(&expr_lit.lit),
409 syn::Expr::Group(group) => Self::from_expr(&group.expr), _ => Err(Error::unexpected_expr_type(expr)),
411 }
412 }
413
414 fn from_value(value: &::syn::Lit) -> Result<Self> {
415 if let syn::Lit::Str(body) = &value {
416 body.parse::<$ty>()
417 .map_err(|_| Error::unknown_lit_str_value(body))
418 } else {
419 Err(Error::unexpected_lit_type(value))
420 }
421 }
422 }
423 };
424}
425
426from_syn_expr_type!(syn::ExprArray, Array);
427from_syn_expr_type!(syn::ExprPath, Path);
428
429macro_rules! from_syn_parse {
435 ($ty:path) => {
436 impl FromMeta for $ty {
437 fn from_string(value: &str) -> Result<Self> {
438 syn::parse_str(value).map_err(|_| Error::unknown_value(value))
439 }
440
441 fn from_value(value: &::syn::Lit) -> Result<Self> {
442 if let ::syn::Lit::Str(ref v) = *value {
443 v.parse::<$ty>()
444 .map_err(|_| Error::unknown_lit_str_value(v))
445 } else {
446 Err(Error::unexpected_lit_type(value))
447 }
448 }
449 }
450 };
451}
452
453from_syn_parse!(syn::Type);
454from_syn_parse!(syn::TypeArray);
455from_syn_parse!(syn::TypeBareFn);
456from_syn_parse!(syn::TypeGroup);
457from_syn_parse!(syn::TypeImplTrait);
458from_syn_parse!(syn::TypeInfer);
459from_syn_parse!(syn::TypeMacro);
460from_syn_parse!(syn::TypeNever);
461from_syn_parse!(syn::TypeParam);
462from_syn_parse!(syn::TypeParen);
463from_syn_parse!(syn::TypePath);
464from_syn_parse!(syn::TypePtr);
465from_syn_parse!(syn::TypeReference);
466from_syn_parse!(syn::TypeSlice);
467from_syn_parse!(syn::TypeTraitObject);
468from_syn_parse!(syn::TypeTuple);
469from_syn_parse!(syn::Visibility);
470from_syn_parse!(syn::WhereClause);
471
472macro_rules! from_numeric_array {
473 ($ty:ident) => {
474 impl FromMeta for Vec<$ty> {
476 fn from_expr(expr: &syn::Expr) -> Result<Self> {
477 match expr {
478 syn::Expr::Array(expr_array) => expr_array
479 .elems
480 .iter()
481 .map(|expr| {
482 let unexpected = || {
483 Error::custom("Expected array of unsigned integers").with_span(expr)
484 };
485 match expr {
486 Expr::Lit(lit) => $ty::from_value(&lit.lit),
487 Expr::Group(group) => match &*group.expr {
488 Expr::Lit(lit) => $ty::from_value(&lit.lit),
489 _ => Err(unexpected()),
490 },
491 _ => Err(unexpected()),
492 }
493 })
494 .collect::<Result<Vec<$ty>>>(),
495 syn::Expr::Lit(expr_lit) => Self::from_value(&expr_lit.lit),
496 syn::Expr::Group(group) => Self::from_expr(&group.expr), _ => Err(Error::unexpected_expr_type(expr)),
498 }
499 }
500
501 fn from_value(value: &Lit) -> Result<Self> {
502 let expr_array = syn::ExprArray::from_value(value)?;
503 Self::from_expr(&syn::Expr::Array(expr_array))
504 }
505 }
506 };
507}
508
509from_numeric_array!(u8);
510from_numeric_array!(u16);
511from_numeric_array!(u32);
512from_numeric_array!(u64);
513from_numeric_array!(usize);
514
515impl FromMeta for syn::Lit {
516 fn from_value(value: &Lit) -> Result<Self> {
517 Ok(value.clone())
518 }
519}
520
521macro_rules! from_meta_lit {
522 ($impl_ty:path, $lit_variant:path) => {
523 impl FromMeta for $impl_ty {
524 fn from_value(value: &Lit) -> Result<Self> {
525 if let $lit_variant(ref value) = *value {
526 Ok(value.clone())
527 } else {
528 Err(Error::unexpected_lit_type(value))
529 }
530 }
531 }
532
533 impl FromMeta for Vec<$impl_ty> {
534 fn from_list(items: &[NestedMeta]) -> Result<Self> {
535 items
536 .iter()
537 .map(<$impl_ty as FromMeta>::from_nested_meta)
538 .collect()
539 }
540
541 fn from_value(value: &syn::Lit) -> Result<Self> {
542 let expr_array = syn::ExprArray::from_value(value)?;
543 Self::from_expr(&syn::Expr::Array(expr_array))
544 }
545
546 fn from_expr(expr: &syn::Expr) -> Result<Self> {
547 match expr {
548 syn::Expr::Array(expr_array) => expr_array
549 .elems
550 .iter()
551 .map(<$impl_ty as FromMeta>::from_expr)
552 .collect::<Result<Vec<_>>>(),
553 syn::Expr::Lit(expr_lit) => Self::from_value(&expr_lit.lit),
554 syn::Expr::Group(g) => Self::from_expr(&g.expr),
555 _ => Err(Error::unexpected_expr_type(expr)),
556 }
557 }
558 }
559 };
560}
561
562from_meta_lit!(syn::LitInt, Lit::Int);
563from_meta_lit!(syn::LitFloat, Lit::Float);
564from_meta_lit!(syn::LitStr, Lit::Str);
565from_meta_lit!(syn::LitByte, Lit::Byte);
566from_meta_lit!(syn::LitByteStr, Lit::ByteStr);
567from_meta_lit!(syn::LitChar, Lit::Char);
568from_meta_lit!(syn::LitBool, Lit::Bool);
569from_meta_lit!(proc_macro2::Literal, Lit::Verbatim);
570
571impl FromMeta for syn::Meta {
572 fn from_meta(value: &syn::Meta) -> Result<Self> {
573 Ok(value.clone())
574 }
575}
576
577impl FromMeta for Vec<syn::WherePredicate> {
578 fn from_string(value: &str) -> Result<Self> {
579 syn::WhereClause::from_string(&format!("where {}", value))
580 .map(|c| c.predicates.into_iter().collect())
581 }
582
583 fn from_value(value: &Lit) -> Result<Self> {
584 if let syn::Lit::Str(s) = value {
585 syn::WhereClause::from_value(&syn::Lit::Str(syn::LitStr::new(
586 &format!("where {}", s.value()),
587 value.span(),
588 )))
589 .map(|c| c.predicates.into_iter().collect())
590 } else {
591 Err(Error::unexpected_lit_type(value))
592 }
593 }
594}
595
596impl FromMeta for ident_case::RenameRule {
597 fn from_string(value: &str) -> Result<Self> {
598 value.parse().map_err(|_| Error::unknown_value(value))
599 }
600}
601
602impl<T: FromMeta> FromMeta for Option<T> {
603 fn from_none() -> Option<Self> {
604 Some(None)
605 }
606
607 fn from_meta(item: &Meta) -> Result<Self> {
608 FromMeta::from_meta(item).map(Some)
609 }
610}
611
612impl<T: FromMeta> FromMeta for Result<T> {
613 fn from_none() -> Option<Self> {
614 T::from_none().map(Ok)
615 }
616
617 fn from_list(items: &[NestedMeta]) -> Result<Self> {
621 Ok(FromMeta::from_list(items))
622 }
623
624 fn from_meta(item: &Meta) -> Result<Self> {
625 Ok(FromMeta::from_meta(item))
626 }
627}
628
629macro_rules! smart_pointer_t {
631 ($ty:path, $map_fn:path) => {
632 impl<T: FromMeta> FromMeta for $ty {
633 fn from_none() -> Option<Self> {
634 T::from_none().map($map_fn)
635 }
636
637 fn from_list(items: &[NestedMeta]) -> Result<Self> {
641 FromMeta::from_list(items).map($map_fn)
642 }
643
644 fn from_meta(item: &Meta) -> Result<Self> {
645 FromMeta::from_meta(item).map($map_fn)
646 }
647 }
648 };
649}
650
651smart_pointer_t!(Box<T>, Box::new);
652smart_pointer_t!(Rc<T>, Rc::new);
653smart_pointer_t!(Arc<T>, Arc::new);
654smart_pointer_t!(RefCell<T>, RefCell::new);
655
656impl<T: FromMeta> FromMeta for ::std::result::Result<T, Meta> {
659 fn from_meta(item: &Meta) -> Result<Self> {
660 T::from_meta(item)
661 .map(Ok)
662 .or_else(|_| Ok(Err(item.clone())))
663 }
664}
665
666trait KeyFromPath: Sized {
668 fn from_path(path: &syn::Path) -> Result<Self>;
669 fn to_display(&self) -> Cow<'_, str>;
670}
671
672impl KeyFromPath for String {
673 fn from_path(path: &syn::Path) -> Result<Self> {
674 Ok(path_to_string(path))
675 }
676
677 fn to_display(&self) -> Cow<'_, str> {
678 Cow::Borrowed(self)
679 }
680}
681
682impl KeyFromPath for syn::Path {
683 fn from_path(path: &syn::Path) -> Result<Self> {
684 Ok(path.clone())
685 }
686
687 fn to_display(&self) -> Cow<'_, str> {
688 Cow::Owned(path_to_string(self))
689 }
690}
691
692impl KeyFromPath for syn::Ident {
693 fn from_path(path: &syn::Path) -> Result<Self> {
694 if path.segments.len() == 1
695 && path.leading_colon.is_none()
696 && path.segments[0].arguments.is_empty()
697 {
698 Ok(path.segments[0].ident.clone())
699 } else {
700 Err(Error::custom("Key must be an identifier").with_span(path))
701 }
702 }
703
704 fn to_display(&self) -> Cow<'_, str> {
705 Cow::Owned(self.to_string())
706 }
707}
708
709macro_rules! hash_map {
710 ($key:ty) => {
711 impl<V: FromMeta, S: BuildHasher + Default> FromMeta for HashMap<$key, V, S> {
712 fn from_list(nested: &[NestedMeta]) -> Result<Self> {
713 let pairs = nested
720 .iter()
721 .map(|item| -> Result<(&syn::Path, Result<V>)> {
722 match *item {
723 NestedMeta::Meta(ref inner) => {
724 let path = inner.path();
725 Ok((
726 path,
727 FromMeta::from_meta(inner).map_err(|e| e.at_path(&path)),
728 ))
729 }
730 NestedMeta::Lit(_) => Err(Error::unsupported_format("expression")),
731 }
732 });
733
734 let mut errors = Error::accumulator();
735 let mut seen_keys = HashSet::with_capacity(nested.len());
741
742 let mut map = HashMap::with_capacity_and_hasher(nested.len(), Default::default());
746
747 for item in pairs {
748 if let Some((path, value)) = errors.handle(item) {
749 let key: $key = match KeyFromPath::from_path(path) {
750 Ok(k) => k,
751 Err(e) => {
752 errors.push(e);
753
754 errors.handle(value);
756
757 continue;
758 }
759 };
760
761 let already_seen = seen_keys.contains(&key);
762
763 if already_seen {
764 errors.push(Error::duplicate_field(&key.to_display()).with_span(path));
765 }
766
767 match value {
768 Ok(_) if already_seen => {}
769 Ok(val) => {
770 map.insert(key.clone(), val);
771 }
772 Err(e) => {
773 errors.push(e);
774 }
775 }
776
777 seen_keys.insert(key);
778 }
779 }
780
781 errors.finish_with(map)
782 }
783 }
784 };
785}
786
787hash_map!(String);
790hash_map!(syn::Ident);
791hash_map!(syn::Path);
792
793#[cfg(test)]
796mod tests {
797 use std::num::{NonZeroU32, NonZeroU64};
798
799 use proc_macro2::TokenStream;
800 use quote::quote;
801 use syn::parse_quote;
802
803 use crate::{Error, FromMeta, Result};
804
805 fn pm(tokens: TokenStream) -> ::std::result::Result<syn::Meta, String> {
807 let attribute: syn::Attribute = parse_quote!(#[#tokens]);
808 Ok(attribute.meta)
809 }
810
811 #[track_caller]
812 fn fm<T: FromMeta>(tokens: TokenStream) -> T {
813 FromMeta::from_meta(&pm(tokens).expect("Tests should pass well-formed input"))
814 .expect("Tests should pass valid input")
815 }
816
817 #[test]
818 fn unit_succeeds() {
819 fm::<()>(quote!(ignore));
820 }
821
822 #[test]
823 #[allow(clippy::bool_assert_comparison)]
824 fn bool_succeeds() {
825 assert_eq!(fm::<bool>(quote!(ignore)), true);
827
828 assert_eq!(fm::<bool>(quote!(ignore = true)), true);
830 assert_eq!(fm::<bool>(quote!(ignore = false)), false);
831
832 assert_eq!(fm::<bool>(quote!(ignore = "true")), true);
834 assert_eq!(fm::<bool>(quote!(ignore = "false")), false);
835 }
836
837 #[test]
838 fn char_succeeds() {
839 assert_eq!(fm::<char>(quote!(ignore = '😬')), '😬');
841
842 assert_eq!(fm::<char>(quote!(ignore = "😬")), '😬');
844 }
845
846 #[test]
847 fn string_succeeds() {
848 assert_eq!(&fm::<String>(quote!(ignore = "world")), "world");
850
851 assert_eq!(&fm::<String>(quote!(ignore = r#"world"#)), "world");
853 }
854
855 #[test]
856 fn pathbuf_succeeds() {
857 assert_eq!(
858 fm::<std::path::PathBuf>(quote!(ignore = r#"C:\"#)),
859 std::path::PathBuf::from(r#"C:\"#)
860 );
861 }
862
863 #[test]
864 #[allow(clippy::float_cmp)] fn number_succeeds() {
866 assert_eq!(fm::<u8>(quote!(ignore = "2")), 2u8);
867 assert_eq!(fm::<i16>(quote!(ignore = "-25")), -25i16);
868 assert_eq!(fm::<f64>(quote!(ignore = "1.4e10")), 1.4e10);
869 }
870
871 #[should_panic(expected = "UnknownValue(\"0\")")]
872 #[test]
873 fn nonzero_number_fails() {
874 fm::<NonZeroU64>(quote!(ignore = "0"));
875 }
876
877 #[test]
878 fn nonzero_number_succeeds() {
879 assert_eq!(
880 fm::<NonZeroU32>(quote!(ignore = "2")),
881 NonZeroU32::new(2).unwrap()
882 );
883 }
884
885 #[test]
886 fn int_without_quotes() {
887 assert_eq!(fm::<u8>(quote!(ignore = 2)), 2u8);
888 assert_eq!(fm::<u16>(quote!(ignore = 255)), 255u16);
889 assert_eq!(fm::<u32>(quote!(ignore = 5000)), 5000u32);
890
891 assert_eq!(fm::<u32>(quote!(ignore = 5000i32)), 5000u32);
893 }
894
895 #[test]
896 fn negative_int_without_quotes() {
897 assert_eq!(fm::<i8>(quote!(ignore = -2)), -2i8);
898 assert_eq!(fm::<i32>(quote!(ignore = -255)), -255i32);
899 }
900
901 #[test]
902 #[allow(clippy::float_cmp)] fn float_without_quotes() {
904 assert_eq!(fm::<f32>(quote!(ignore = 2.)), 2.0f32);
905 assert_eq!(fm::<f32>(quote!(ignore = 2.0)), 2.0f32);
906 assert_eq!(fm::<f64>(quote!(ignore = 1.4e10)), 1.4e10f64);
907 }
908
909 #[test]
910 fn too_large_int_produces_error() {
911 assert!(fm::<Result<u8>>(quote!(ignore = 2000)).is_err());
912 }
913
914 #[test]
915 fn meta_succeeds() {
916 use syn::Meta;
917
918 assert_eq!(
919 fm::<Meta>(quote!(hello(world, today))),
920 pm(quote!(hello(world, today))).unwrap()
921 );
922 }
923
924 #[test]
925 fn hash_map_succeeds() {
926 use std::collections::HashMap;
927
928 let comparison = {
929 let mut c = HashMap::new();
930 c.insert("hello".to_string(), true);
931 c.insert("world".to_string(), false);
932 c.insert("there".to_string(), true);
933 c
934 };
935
936 assert_eq!(
937 fm::<HashMap<String, bool>>(quote!(ignore(hello, world = false, there = "true"))),
938 comparison
939 );
940 }
941
942 #[test]
945 fn hash_map_duplicate() {
946 use std::collections::HashMap;
947
948 let err: Result<HashMap<String, bool>> =
949 FromMeta::from_meta(&pm(quote!(ignore(hello, hello = false))).unwrap());
950
951 let err = err.expect_err("Duplicate keys in HashMap should error");
952
953 assert!(err.has_span());
954 assert_eq!(err.to_string(), Error::duplicate_field("hello").to_string());
955 }
956
957 #[test]
958 fn hash_map_multiple_errors() {
959 use std::collections::HashMap;
960
961 let err = HashMap::<String, bool>::from_meta(
962 &pm(quote!(ignore(hello, hello = 3, hello = false))).unwrap(),
963 )
964 .expect_err("Duplicates and bad values should error");
965
966 assert_eq!(err.len(), 3);
967 let errors = err.into_iter().collect::<Vec<_>>();
968 assert!(errors[0].has_span());
969 assert!(errors[1].has_span());
970 assert!(errors[2].has_span());
971 }
972
973 #[test]
974 fn hash_map_ident_succeeds() {
975 use std::collections::HashMap;
976 use syn::parse_quote;
977
978 let comparison = {
979 let mut c = HashMap::<syn::Ident, bool>::new();
980 c.insert(parse_quote!(first), true);
981 c.insert(parse_quote!(second), false);
982 c
983 };
984
985 assert_eq!(
986 fm::<HashMap<syn::Ident, bool>>(quote!(ignore(first, second = false))),
987 comparison
988 );
989 }
990
991 #[test]
992 fn hash_map_ident_rejects_non_idents() {
993 use std::collections::HashMap;
994
995 let err: Result<HashMap<syn::Ident, bool>> =
996 FromMeta::from_meta(&pm(quote!(ignore(first, the::second))).unwrap());
997
998 err.unwrap_err();
999 }
1000
1001 #[test]
1002 fn hash_map_path_succeeds() {
1003 use std::collections::HashMap;
1004 use syn::parse_quote;
1005
1006 let comparison = {
1007 let mut c = HashMap::<syn::Path, bool>::new();
1008 c.insert(parse_quote!(first), true);
1009 c.insert(parse_quote!(the::second), false);
1010 c
1011 };
1012
1013 assert_eq!(
1014 fm::<HashMap<syn::Path, bool>>(quote!(ignore(first, the::second = false))),
1015 comparison
1016 );
1017 }
1018
1019 #[test]
1022 fn darling_result_succeeds() {
1023 fm::<Result<()>>(quote!(ignore)).unwrap();
1024 fm::<Result<()>>(quote!(ignore(world))).unwrap_err();
1025 }
1026
1027 #[test]
1029 fn test_punctuated() {
1030 fm::<syn::punctuated::Punctuated<syn::FnArg, syn::token::Comma>>(quote!(
1031 ignore = "a: u8, b: Type"
1032 ));
1033 fm::<syn::punctuated::Punctuated<syn::Expr, syn::token::Comma>>(quote!(ignore = "a, b, c"));
1034 }
1035
1036 #[test]
1037 fn test_expr_array() {
1038 fm::<syn::ExprArray>(quote!(ignore = "[0x1, 0x2]"));
1039 fm::<syn::ExprArray>(quote!(ignore = "[\"Hello World\", \"Test Array\"]"));
1040 }
1041
1042 #[test]
1043 fn test_expr() {
1044 fm::<syn::Expr>(quote!(ignore = "x + y"));
1045 fm::<syn::Expr>(quote!(ignore = "an_object.method_call()"));
1046 fm::<syn::Expr>(quote!(ignore = "{ a_statement(); in_a_block }"));
1047 }
1048
1049 #[test]
1050 fn test_expr_without_quotes() {
1051 fm::<syn::Expr>(quote!(ignore = x + y));
1052 fm::<syn::Expr>(quote!(ignore = an_object.method_call()));
1053 fm::<syn::Expr>(quote!(
1054 ignore = {
1055 a_statement();
1056 in_a_block
1057 }
1058 ));
1059 }
1060
1061 #[test]
1062 fn test_expr_path() {
1063 fm::<syn::ExprPath>(quote!(ignore = "std::mem::replace"));
1064 fm::<syn::ExprPath>(quote!(ignore = "x"));
1065 fm::<syn::ExprPath>(quote!(ignore = "example::<Test>"));
1066 }
1067
1068 #[test]
1069 fn test_expr_path_without_quotes() {
1070 fm::<syn::ExprPath>(quote!(ignore = std::mem::replace));
1071 fm::<syn::ExprPath>(quote!(ignore = x));
1072 fm::<syn::ExprPath>(quote!(ignore = example::<Test>));
1073 }
1074
1075 #[test]
1076 fn test_path_without_quotes() {
1077 fm::<syn::Path>(quote!(ignore = std::mem::replace));
1078 fm::<syn::Path>(quote!(ignore = x));
1079 fm::<syn::Path>(quote!(ignore = example::<Test>));
1080 }
1081
1082 #[test]
1083 fn test_number_array() {
1084 assert_eq!(fm::<Vec<u8>>(quote!(ignore = [16, 0xff])), vec![0x10, 0xff]);
1085 assert_eq!(
1086 fm::<Vec<u16>>(quote!(ignore = "[32, 0xffff]")),
1087 vec![0x20, 0xffff]
1088 );
1089 assert_eq!(
1090 fm::<Vec<u32>>(quote!(ignore = "[48, 0xffffffff]")),
1091 vec![0x30, 0xffffffff]
1092 );
1093 assert_eq!(
1094 fm::<Vec<u64>>(quote!(ignore = "[64, 0xffffffffffffffff]")),
1095 vec![0x40, 0xffffffffffffffff]
1096 );
1097 assert_eq!(
1098 fm::<Vec<usize>>(quote!(ignore = "[80, 0xffffffff]")),
1099 vec![0x50, 0xffffffff]
1100 );
1101 }
1102
1103 #[test]
1104 fn test_lit_array() {
1105 fm::<Vec<syn::LitStr>>(quote!(ignore = "[\"Hello World\", \"Test Array\"]"));
1106 fm::<Vec<syn::LitStr>>(quote!(ignore = ["Hello World", "Test Array"]));
1107 fm::<Vec<syn::LitChar>>(quote!(ignore = "['a', 'b', 'c']"));
1108 fm::<Vec<syn::LitBool>>(quote!(ignore = "[true]"));
1109 fm::<Vec<syn::LitStr>>(quote!(ignore = "[]"));
1110 fm::<Vec<syn::LitStr>>(quote!(ignore = []));
1111 fm::<Vec<syn::LitBool>>(quote!(ignore = [true, false]));
1112 }
1113}