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 fn from_list(items: &[NestedMeta]) -> Result<Self> {
167 let mut errors = Error::accumulator();
168 for item in items {
169 errors.push(match item {
170 NestedMeta::Meta(meta) => Error::unknown_field_path(meta.path()).with_span(meta),
177 NestedMeta::Lit(lit) => Error::unexpected_expr_type(
178 &(syn::ExprLit {
179 attrs: vec![],
180 lit: lit.clone(),
181 }
182 .into()),
183 )
184 .with_span(lit),
185 });
186 }
187
188 errors.finish()
189 }
190}
191
192impl FromMeta for bool {
193 fn from_word() -> Result<Self> {
194 Ok(true)
195 }
196
197 #[allow(clippy::wrong_self_convention)] fn from_bool(value: bool) -> Result<Self> {
199 Ok(value)
200 }
201
202 fn from_string(value: &str) -> Result<Self> {
203 value.parse().map_err(|_| Error::unknown_value(value))
204 }
205}
206
207impl FromMeta for AtomicBool {
208 fn from_meta(mi: &Meta) -> Result<Self> {
209 FromMeta::from_meta(mi)
210 .map(AtomicBool::new)
211 .map_err(|e| e.with_span(mi))
212 }
213}
214
215impl FromMeta for char {
216 #[allow(clippy::wrong_self_convention)] fn from_char(value: char) -> Result<Self> {
218 Ok(value)
219 }
220
221 fn from_string(s: &str) -> Result<Self> {
222 let mut chars = s.chars();
223 let char1 = chars.next();
224 let char2 = chars.next();
225
226 if let (Some(char), None) = (char1, char2) {
227 Ok(char)
228 } else {
229 Err(Error::unexpected_type("string"))
230 }
231 }
232}
233
234impl FromMeta for String {
235 fn from_string(s: &str) -> Result<Self> {
236 Ok(s.to_string())
237 }
238}
239
240impl FromMeta for std::path::PathBuf {
241 fn from_string(s: &str) -> Result<Self> {
242 Ok(s.into())
243 }
244}
245
246macro_rules! from_meta_num {
249 ($ty:path) => {
250 impl FromMeta for $ty {
251 fn from_string(s: &str) -> Result<Self> {
252 s.parse().map_err(|_| Error::unknown_value(s))
253 }
254
255 fn from_value(value: &Lit) -> Result<Self> {
256 (match *value {
257 Lit::Str(ref s) => Self::from_string(&s.value()),
258 Lit::Int(ref s) => s.base10_parse::<$ty>().map_err(Error::from),
259 _ => Err(Error::unexpected_lit_type(value)),
260 })
261 .map_err(|e| e.with_span(value))
262 }
263 }
264 };
265}
266
267from_meta_num!(u8);
268from_meta_num!(u16);
269from_meta_num!(u32);
270from_meta_num!(u64);
271from_meta_num!(u128);
272from_meta_num!(usize);
273from_meta_num!(i8);
274from_meta_num!(i16);
275from_meta_num!(i32);
276from_meta_num!(i64);
277from_meta_num!(i128);
278from_meta_num!(isize);
279from_meta_num!(num::NonZeroU8);
280from_meta_num!(num::NonZeroU16);
281from_meta_num!(num::NonZeroU32);
282from_meta_num!(num::NonZeroU64);
283from_meta_num!(num::NonZeroU128);
284from_meta_num!(num::NonZeroUsize);
285from_meta_num!(num::NonZeroI8);
286from_meta_num!(num::NonZeroI16);
287from_meta_num!(num::NonZeroI32);
288from_meta_num!(num::NonZeroI64);
289from_meta_num!(num::NonZeroI128);
290from_meta_num!(num::NonZeroIsize);
291
292macro_rules! from_meta_float {
295 ($ty:ident) => {
296 impl FromMeta for $ty {
297 fn from_string(s: &str) -> Result<Self> {
298 s.parse().map_err(|_| Error::unknown_value(s))
299 }
300
301 fn from_value(value: &Lit) -> Result<Self> {
302 (match *value {
303 Lit::Str(ref s) => Self::from_string(&s.value()),
304 Lit::Float(ref s) => s.base10_parse::<$ty>().map_err(Error::from),
305 _ => Err(Error::unexpected_lit_type(value)),
306 })
307 .map_err(|e| e.with_span(value))
308 }
309 }
310 };
311}
312
313from_meta_float!(f32);
314from_meta_float!(f64);
315
316impl<T: syn::parse::Parse, P: syn::parse::Parse> FromMeta for syn::punctuated::Punctuated<T, P> {
320 fn from_value(value: &Lit) -> Result<Self> {
321 if let Lit::Str(ref ident) = *value {
322 ident
323 .parse_with(syn::punctuated::Punctuated::parse_terminated)
324 .map_err(|_| Error::unknown_lit_str_value(ident))
325 } else {
326 Err(Error::unexpected_lit_type(value))
327 }
328 }
329}
330
331impl FromMeta for syn::Expr {
340 fn from_expr(expr: &Expr) -> Result<Self> {
341 match expr {
342 Expr::Lit(syn::ExprLit {
343 lit: lit @ syn::Lit::Str(_),
344 ..
345 }) => Self::from_value(lit),
346 Expr::Group(group) => Self::from_expr(&group.expr), _ => Ok(expr.clone()),
348 }
349 }
350
351 fn from_string(value: &str) -> Result<Self> {
352 syn::parse_str(value).map_err(|_| Error::unknown_value(value))
353 }
354
355 fn from_value(value: &::syn::Lit) -> Result<Self> {
356 if let ::syn::Lit::Str(ref v) = *value {
357 v.parse::<syn::Expr>()
358 .map_err(|_| Error::unknown_lit_str_value(v))
359 } else {
360 Err(Error::unexpected_lit_type(value))
361 }
362 }
363}
364
365impl FromMeta for syn::Path {
367 fn from_string(value: &str) -> Result<Self> {
368 syn::parse_str(value).map_err(|_| Error::unknown_value(value))
369 }
370
371 fn from_value(value: &::syn::Lit) -> Result<Self> {
372 if let ::syn::Lit::Str(ref v) = *value {
373 v.parse().map_err(|_| Error::unknown_lit_str_value(v))
374 } else {
375 Err(Error::unexpected_lit_type(value))
376 }
377 }
378
379 fn from_expr(expr: &Expr) -> Result<Self> {
380 match expr {
381 Expr::Lit(lit) => Self::from_value(&lit.lit),
382 Expr::Path(path) => Ok(path.path.clone()),
383 Expr::Group(group) => Self::from_expr(&group.expr), _ => Err(Error::unexpected_expr_type(expr)),
385 }
386 }
387}
388
389impl FromMeta for syn::Ident {
390 fn from_string(value: &str) -> Result<Self> {
391 syn::parse_str(value).map_err(|_| Error::unknown_value(value))
392 }
393
394 fn from_value(value: &syn::Lit) -> Result<Self> {
395 if let syn::Lit::Str(ref v) = *value {
396 v.parse().map_err(|_| Error::unknown_lit_str_value(v))
397 } else {
398 Err(Error::unexpected_lit_type(value))
399 }
400 }
401
402 fn from_expr(expr: &Expr) -> Result<Self> {
403 match expr {
404 Expr::Lit(lit) => Self::from_value(&lit.lit),
405 Expr::Path(path) => match path.path.get_ident() {
409 Some(ident) => Ok(ident.clone()),
410 None => Err(Error::unexpected_expr_type(expr)),
411 },
412 Expr::Group(group) => Self::from_expr(&group.expr), _ => Err(Error::unexpected_expr_type(expr)),
414 }
415 }
416}
417
418macro_rules! from_syn_expr_type {
429 ($ty:path, $variant:ident) => {
430 impl FromMeta for $ty {
431 fn from_expr(expr: &syn::Expr) -> Result<Self> {
432 match expr {
433 syn::Expr::$variant(body) => Ok(body.clone()),
434 syn::Expr::Lit(expr_lit) => Self::from_value(&expr_lit.lit),
435 syn::Expr::Group(group) => Self::from_expr(&group.expr), _ => Err(Error::unexpected_expr_type(expr)),
437 }
438 }
439
440 fn from_value(value: &::syn::Lit) -> Result<Self> {
441 if let syn::Lit::Str(body) = &value {
442 body.parse::<$ty>()
443 .map_err(|_| Error::unknown_lit_str_value(body))
444 } else {
445 Err(Error::unexpected_lit_type(value))
446 }
447 }
448 }
449 };
450}
451
452from_syn_expr_type!(syn::ExprArray, Array);
453from_syn_expr_type!(syn::ExprPath, Path);
454from_syn_expr_type!(syn::ExprRange, Range);
455
456macro_rules! from_syn_parse {
462 ($ty:path) => {
463 impl FromMeta for $ty {
464 fn from_string(value: &str) -> Result<Self> {
465 syn::parse_str(value).map_err(|_| Error::unknown_value(value))
466 }
467
468 fn from_value(value: &::syn::Lit) -> Result<Self> {
469 if let ::syn::Lit::Str(ref v) = *value {
470 v.parse::<$ty>()
471 .map_err(|_| Error::unknown_lit_str_value(v))
472 } else {
473 Err(Error::unexpected_lit_type(value))
474 }
475 }
476 }
477 };
478}
479
480from_syn_parse!(syn::Type);
481from_syn_parse!(syn::TypeArray);
482from_syn_parse!(syn::TypeBareFn);
483from_syn_parse!(syn::TypeGroup);
484from_syn_parse!(syn::TypeImplTrait);
485from_syn_parse!(syn::TypeInfer);
486from_syn_parse!(syn::TypeMacro);
487from_syn_parse!(syn::TypeNever);
488from_syn_parse!(syn::TypeParam);
489from_syn_parse!(syn::TypeParen);
490from_syn_parse!(syn::TypePtr);
491from_syn_parse!(syn::TypeReference);
492from_syn_parse!(syn::TypeSlice);
493from_syn_parse!(syn::TypeTraitObject);
494from_syn_parse!(syn::TypeTuple);
495from_syn_parse!(syn::Visibility);
496from_syn_parse!(syn::WhereClause);
497
498impl FromMeta for syn::TypePath {
499 fn from_expr(expr: &Expr) -> Result<Self> {
501 match expr {
502 Expr::Path(body) => {
503 if body.attrs.is_empty() {
504 Ok(syn::TypePath {
505 qself: body.qself.clone(),
506 path: body.path.clone(),
507 })
508 } else {
509 Err(Error::custom("attributes are not allowed").with_span(body))
510 }
511 }
512 Expr::Lit(expr_lit) => Self::from_value(&expr_lit.lit),
513 Expr::Group(group) => Self::from_expr(&group.expr),
514 _ => Err(Error::unexpected_expr_type(expr)),
515 }
516 }
517
518 fn from_string(value: &str) -> Result<Self> {
519 syn::parse_str(value).map_err(|_| Error::unknown_value(value))
520 }
521
522 fn from_value(value: &Lit) -> Result<Self> {
523 if let Lit::Str(ref v) = *value {
524 v.parse().map_err(|_| Error::unknown_lit_str_value(v))
525 } else {
526 Err(Error::unexpected_lit_type(value))
527 }
528 }
529}
530
531macro_rules! from_numeric_array {
532 ($ty:ident) => {
533 impl FromMeta for Vec<$ty> {
535 fn from_expr(expr: &syn::Expr) -> Result<Self> {
536 match expr {
537 syn::Expr::Array(expr_array) => expr_array
538 .elems
539 .iter()
540 .map(|expr| {
541 let unexpected = || {
542 Error::custom("Expected array of unsigned integers").with_span(expr)
543 };
544 match expr {
545 Expr::Lit(lit) => $ty::from_value(&lit.lit),
546 Expr::Group(group) => match &*group.expr {
547 Expr::Lit(lit) => $ty::from_value(&lit.lit),
548 _ => Err(unexpected()),
549 },
550 _ => Err(unexpected()),
551 }
552 })
553 .collect::<Result<Vec<$ty>>>(),
554 syn::Expr::Lit(expr_lit) => Self::from_value(&expr_lit.lit),
555 syn::Expr::Group(group) => Self::from_expr(&group.expr), _ => Err(Error::unexpected_expr_type(expr)),
557 }
558 }
559
560 fn from_value(value: &Lit) -> Result<Self> {
561 let expr_array = syn::ExprArray::from_value(value)?;
562 Self::from_expr(&syn::Expr::Array(expr_array))
563 }
564 }
565 };
566}
567
568from_numeric_array!(u8);
569from_numeric_array!(u16);
570from_numeric_array!(u32);
571from_numeric_array!(u64);
572from_numeric_array!(usize);
573
574impl FromMeta for syn::Lit {
575 fn from_value(value: &Lit) -> Result<Self> {
576 Ok(value.clone())
577 }
578}
579
580macro_rules! from_meta_lit {
581 ($impl_ty:path, $lit_variant:path) => {
582 impl FromMeta for $impl_ty {
583 fn from_value(value: &Lit) -> Result<Self> {
584 if let $lit_variant(ref value) = *value {
585 Ok(value.clone())
586 } else {
587 Err(Error::unexpected_lit_type(value))
588 }
589 }
590 }
591
592 impl FromMeta for Vec<$impl_ty> {
593 fn from_list(items: &[NestedMeta]) -> Result<Self> {
594 items
595 .iter()
596 .map(<$impl_ty as FromMeta>::from_nested_meta)
597 .collect()
598 }
599
600 fn from_value(value: &syn::Lit) -> Result<Self> {
601 let expr_array = syn::ExprArray::from_value(value)?;
602 Self::from_expr(&syn::Expr::Array(expr_array))
603 }
604
605 fn from_expr(expr: &syn::Expr) -> Result<Self> {
606 match expr {
607 syn::Expr::Array(expr_array) => expr_array
608 .elems
609 .iter()
610 .map(<$impl_ty as FromMeta>::from_expr)
611 .collect::<Result<Vec<_>>>(),
612 syn::Expr::Lit(expr_lit) => Self::from_value(&expr_lit.lit),
613 syn::Expr::Group(g) => Self::from_expr(&g.expr),
614 _ => Err(Error::unexpected_expr_type(expr)),
615 }
616 }
617 }
618 };
619}
620
621from_meta_lit!(syn::LitInt, Lit::Int);
622from_meta_lit!(syn::LitFloat, Lit::Float);
623from_meta_lit!(syn::LitStr, Lit::Str);
624from_meta_lit!(syn::LitByte, Lit::Byte);
625from_meta_lit!(syn::LitByteStr, Lit::ByteStr);
626from_meta_lit!(syn::LitChar, Lit::Char);
627from_meta_lit!(syn::LitBool, Lit::Bool);
628from_meta_lit!(proc_macro2::Literal, Lit::Verbatim);
629
630impl FromMeta for syn::Meta {
631 fn from_meta(value: &syn::Meta) -> Result<Self> {
632 Ok(value.clone())
633 }
634}
635
636impl FromMeta for Vec<syn::WherePredicate> {
637 fn from_string(value: &str) -> Result<Self> {
638 syn::WhereClause::from_string(&format!("where {}", value))
639 .map(|c| c.predicates.into_iter().collect())
640 }
641
642 fn from_value(value: &Lit) -> Result<Self> {
643 if let syn::Lit::Str(s) = value {
644 syn::WhereClause::from_value(&syn::Lit::Str(syn::LitStr::new(
645 &format!("where {}", s.value()),
646 value.span(),
647 )))
648 .map(|c| c.predicates.into_iter().collect())
649 } else {
650 Err(Error::unexpected_lit_type(value))
651 }
652 }
653}
654
655impl FromMeta for ident_case::RenameRule {
656 fn from_string(value: &str) -> Result<Self> {
657 value.parse().map_err(|_| Error::unknown_value(value))
658 }
659}
660
661impl<T: FromMeta> FromMeta for Option<T> {
662 fn from_none() -> Option<Self> {
663 Some(None)
664 }
665
666 fn from_meta(item: &Meta) -> Result<Self> {
667 FromMeta::from_meta(item).map(Some)
668 }
669}
670
671impl<T: FromMeta> FromMeta for Result<T> {
672 fn from_none() -> Option<Self> {
673 T::from_none().map(Ok)
674 }
675
676 fn from_list(items: &[NestedMeta]) -> Result<Self> {
680 Ok(FromMeta::from_list(items))
681 }
682
683 fn from_meta(item: &Meta) -> Result<Self> {
684 Ok(FromMeta::from_meta(item))
685 }
686}
687
688macro_rules! smart_pointer_t {
690 ($ty:path, $map_fn:path) => {
691 impl<T: FromMeta> FromMeta for $ty {
692 fn from_none() -> Option<Self> {
693 T::from_none().map($map_fn)
694 }
695
696 fn from_list(items: &[NestedMeta]) -> Result<Self> {
700 FromMeta::from_list(items).map($map_fn)
701 }
702
703 fn from_meta(item: &Meta) -> Result<Self> {
704 FromMeta::from_meta(item).map($map_fn)
705 }
706 }
707 };
708}
709
710smart_pointer_t!(Box<T>, Box::new);
711smart_pointer_t!(Rc<T>, Rc::new);
712smart_pointer_t!(Arc<T>, Arc::new);
713smart_pointer_t!(RefCell<T>, RefCell::new);
714
715impl<T: FromMeta> FromMeta for ::std::result::Result<T, Meta> {
718 fn from_meta(item: &Meta) -> Result<Self> {
719 T::from_meta(item)
720 .map(Ok)
721 .or_else(|_| Ok(Err(item.clone())))
722 }
723}
724
725trait KeyFromPath: Sized {
727 fn from_path(path: &syn::Path) -> Result<Self>;
728 fn to_display(&self) -> Cow<'_, str>;
729}
730
731impl KeyFromPath for String {
732 fn from_path(path: &syn::Path) -> Result<Self> {
733 Ok(path_to_string(path))
734 }
735
736 fn to_display(&self) -> Cow<'_, str> {
737 Cow::Borrowed(self)
738 }
739}
740
741impl KeyFromPath for syn::Path {
742 fn from_path(path: &syn::Path) -> Result<Self> {
743 Ok(path.clone())
744 }
745
746 fn to_display(&self) -> Cow<'_, str> {
747 Cow::Owned(path_to_string(self))
748 }
749}
750
751impl KeyFromPath for syn::Ident {
752 fn from_path(path: &syn::Path) -> Result<Self> {
753 if path.segments.len() == 1
754 && path.leading_colon.is_none()
755 && path.segments[0].arguments.is_empty()
756 {
757 Ok(path.segments[0].ident.clone())
758 } else {
759 Err(Error::custom("Key must be an identifier").with_span(path))
760 }
761 }
762
763 fn to_display(&self) -> Cow<'_, str> {
764 Cow::Owned(self.to_string())
765 }
766}
767
768macro_rules! map {
769 (hash_map, $key:ty, $nested:ident) => {
770 impl<V: FromMeta, S: BuildHasher + Default> FromMeta for HashMap<$key, V, S> {
771 map!(
772 HashMap::with_capacity_and_hasher($nested.len(), Default::default()),
773 $key,
774 $nested
775 );
776 }
777 };
778
779 (btree_map, $key:ty, $nested:ident) => {
780 impl<V: FromMeta> FromMeta for BTreeMap<$key, V> {
781 map!(BTreeMap::new(), $key, $nested);
782 }
783 };
784
785 ($new:expr, $key:ty, $nested:ident) => {
786 fn from_list($nested: &[NestedMeta]) -> Result<Self> {
787 let pairs = $nested
794 .iter()
795 .map(|item| -> Result<(&syn::Path, Result<V>)> {
796 match *item {
797 NestedMeta::Meta(ref inner) => {
798 let path = inner.path();
799 Ok((
800 path,
801 FromMeta::from_meta(inner).map_err(|e| e.at_path(&path)),
802 ))
803 }
804 NestedMeta::Lit(_) => Err(Error::unsupported_format("expression")),
805 }
806 });
807
808 let mut errors = Error::accumulator();
809 let mut seen_keys = HashSet::with_capacity($nested.len());
815
816 let mut map = $new;
820
821 for item in pairs {
822 if let Some((path, value)) = errors.handle(item) {
823 let key: $key = match KeyFromPath::from_path(path) {
824 Ok(k) => k,
825 Err(e) => {
826 errors.push(e);
827
828 errors.handle(value);
830
831 continue;
832 }
833 };
834
835 let already_seen = seen_keys.contains(&key);
836
837 if already_seen {
838 errors.push(Error::duplicate_field(&key.to_display()).with_span(path));
839 }
840
841 match value {
842 Ok(_) if already_seen => {}
843 Ok(val) => {
844 map.insert(key.clone(), val);
845 }
846 Err(e) => {
847 errors.push(e);
848 }
849 }
850
851 seen_keys.insert(key);
852 }
853 }
854
855 errors.finish_with(map)
856 }
857 };
858}
859
860map!(hash_map, String, nested);
863map!(hash_map, syn::Ident, nested);
864map!(hash_map, syn::Path, nested);
865
866map!(btree_map, String, nested);
867map!(btree_map, syn::Ident, nested);
868
869#[cfg(test)]
872mod tests {
873 use std::num::{NonZeroU32, NonZeroU64};
874
875 use proc_macro2::TokenStream;
876 use quote::quote;
877 use syn::parse_quote;
878
879 use crate::{Error, FromMeta, Result};
880
881 fn pm(tokens: TokenStream) -> ::std::result::Result<syn::Meta, String> {
883 let attribute: syn::Attribute = parse_quote!(#[#tokens]);
884 Ok(attribute.meta)
885 }
886
887 #[track_caller]
888 fn fm<T: FromMeta>(tokens: TokenStream) -> T {
889 FromMeta::from_meta(&pm(tokens).expect("Tests should pass well-formed input"))
890 .expect("Tests should pass valid input")
891 }
892
893 #[test]
894 fn unit_succeeds() {
895 fm::<()>(quote!(ignore));
896 fm::<()>(quote!(ignore()));
897 }
898
899 #[test]
900 #[should_panic(expected = "UnknownField")]
901 fn unit_fails() {
902 fm::<()>(quote!(ignore(foo = "bar")));
903 }
904
905 #[test]
906 #[allow(clippy::bool_assert_comparison)]
907 fn bool_succeeds() {
908 assert_eq!(fm::<bool>(quote!(ignore)), true);
910
911 assert_eq!(fm::<bool>(quote!(ignore = true)), true);
913 assert_eq!(fm::<bool>(quote!(ignore = false)), false);
914
915 assert_eq!(fm::<bool>(quote!(ignore = "true")), true);
917 assert_eq!(fm::<bool>(quote!(ignore = "false")), false);
918 }
919
920 #[test]
921 fn char_succeeds() {
922 assert_eq!(fm::<char>(quote!(ignore = '😬')), '😬');
924
925 assert_eq!(fm::<char>(quote!(ignore = "😬")), '😬');
927 }
928
929 #[test]
930 fn string_succeeds() {
931 assert_eq!(&fm::<String>(quote!(ignore = "world")), "world");
933
934 assert_eq!(&fm::<String>(quote!(ignore = r#"world"#)), "world");
936 }
937
938 #[test]
939 fn pathbuf_succeeds() {
940 assert_eq!(
941 fm::<std::path::PathBuf>(quote!(ignore = r#"C:\"#)),
942 std::path::PathBuf::from(r#"C:\"#)
943 );
944 }
945
946 #[test]
947 #[allow(clippy::float_cmp)] fn number_succeeds() {
949 assert_eq!(fm::<u8>(quote!(ignore = "2")), 2u8);
950 assert_eq!(fm::<i16>(quote!(ignore = "-25")), -25i16);
951 assert_eq!(fm::<f64>(quote!(ignore = "1.4e10")), 1.4e10);
952 }
953
954 #[should_panic(expected = "UnknownValue")]
955 #[test]
956 fn nonzero_number_fails() {
957 fm::<NonZeroU64>(quote!(ignore = "0"));
958 }
959
960 #[test]
961 fn nonzero_number_succeeds() {
962 assert_eq!(
963 fm::<NonZeroU32>(quote!(ignore = "2")),
964 NonZeroU32::new(2).unwrap()
965 );
966 }
967
968 #[test]
969 fn int_without_quotes() {
970 assert_eq!(fm::<u8>(quote!(ignore = 2)), 2u8);
971 assert_eq!(fm::<u16>(quote!(ignore = 255)), 255u16);
972 assert_eq!(fm::<u32>(quote!(ignore = 5000)), 5000u32);
973
974 assert_eq!(fm::<u32>(quote!(ignore = 5000i32)), 5000u32);
976 }
977
978 #[test]
979 fn negative_int_without_quotes() {
980 assert_eq!(fm::<i8>(quote!(ignore = -2)), -2i8);
981 assert_eq!(fm::<i32>(quote!(ignore = -255)), -255i32);
982 }
983
984 #[test]
985 #[allow(clippy::float_cmp)] fn float_without_quotes() {
987 assert_eq!(fm::<f32>(quote!(ignore = 2.)), 2.0f32);
988 assert_eq!(fm::<f32>(quote!(ignore = 2.0)), 2.0f32);
989 assert_eq!(fm::<f64>(quote!(ignore = 1.4e10)), 1.4e10f64);
990 }
991
992 #[test]
993 fn too_large_int_produces_error() {
994 assert!(fm::<Result<u8>>(quote!(ignore = 2000)).is_err());
995 }
996
997 #[test]
998 fn meta_succeeds() {
999 use syn::Meta;
1000
1001 assert_eq!(
1002 fm::<Meta>(quote!(hello(world, today))),
1003 pm(quote!(hello(world, today))).unwrap()
1004 );
1005 }
1006
1007 #[test]
1008 fn hash_map_succeeds() {
1009 use std::collections::HashMap;
1010
1011 let comparison = {
1012 let mut c = HashMap::new();
1013 c.insert("hello".to_string(), true);
1014 c.insert("world".to_string(), false);
1015 c.insert("there".to_string(), true);
1016 c
1017 };
1018
1019 assert_eq!(
1020 fm::<HashMap<String, bool>>(quote!(ignore(hello, world = false, there = "true"))),
1021 comparison
1022 );
1023 }
1024
1025 #[test]
1028 fn hash_map_duplicate() {
1029 use std::collections::HashMap;
1030
1031 let err: Result<HashMap<String, bool>> =
1032 FromMeta::from_meta(&pm(quote!(ignore(hello, hello = false))).unwrap());
1033
1034 let err = err.expect_err("Duplicate keys in HashMap should error");
1035
1036 assert!(err.has_span());
1037 assert_eq!(err.to_string(), Error::duplicate_field("hello").to_string());
1038 }
1039
1040 #[test]
1041 fn hash_map_multiple_errors() {
1042 use std::collections::HashMap;
1043
1044 let err = HashMap::<String, bool>::from_meta(
1045 &pm(quote!(ignore(hello, hello = 3, hello = false))).unwrap(),
1046 )
1047 .expect_err("Duplicates and bad values should error");
1048
1049 assert_eq!(err.len(), 3);
1050 let errors = err.into_iter().collect::<Vec<_>>();
1051 assert!(errors[0].has_span());
1052 assert!(errors[1].has_span());
1053 assert!(errors[2].has_span());
1054 }
1055
1056 #[test]
1057 fn hash_map_ident_succeeds() {
1058 use std::collections::HashMap;
1059 use syn::parse_quote;
1060
1061 let comparison = {
1062 let mut c = HashMap::<syn::Ident, bool>::new();
1063 c.insert(parse_quote!(first), true);
1064 c.insert(parse_quote!(second), false);
1065 c
1066 };
1067
1068 assert_eq!(
1069 fm::<HashMap<syn::Ident, bool>>(quote!(ignore(first, second = false))),
1070 comparison
1071 );
1072 }
1073
1074 #[test]
1075 fn hash_map_ident_rejects_non_idents() {
1076 use std::collections::HashMap;
1077
1078 let err: Result<HashMap<syn::Ident, bool>> =
1079 FromMeta::from_meta(&pm(quote!(ignore(first, the::second))).unwrap());
1080
1081 err.unwrap_err();
1082 }
1083
1084 #[test]
1085 fn hash_map_path_succeeds() {
1086 use std::collections::HashMap;
1087 use syn::parse_quote;
1088
1089 let comparison = {
1090 let mut c = HashMap::<syn::Path, bool>::new();
1091 c.insert(parse_quote!(first), true);
1092 c.insert(parse_quote!(the::second), false);
1093 c
1094 };
1095
1096 assert_eq!(
1097 fm::<HashMap<syn::Path, bool>>(quote!(ignore(first, the::second = false))),
1098 comparison
1099 );
1100 }
1101
1102 #[test]
1103 fn btree_map_succeeds() {
1104 use std::collections::BTreeMap;
1105
1106 let comparison = {
1107 let mut c = BTreeMap::new();
1108 c.insert("hello".to_string(), true);
1109 c.insert("world".to_string(), false);
1110 c.insert("there".to_string(), true);
1111 c
1112 };
1113
1114 assert_eq!(
1115 fm::<BTreeMap<String, bool>>(quote!(ignore(hello, world = false, there = "true"))),
1116 comparison
1117 );
1118 }
1119
1120 #[test]
1123 fn btree_map_duplicate() {
1124 use std::collections::BTreeMap;
1125
1126 let err: Result<BTreeMap<String, bool>> =
1127 FromMeta::from_meta(&pm(quote!(ignore(hello, hello = false))).unwrap());
1128
1129 let err = err.expect_err("Duplicate keys in BTreeMap should error");
1130
1131 assert!(err.has_span());
1132 assert_eq!(err.to_string(), Error::duplicate_field("hello").to_string());
1133 }
1134
1135 #[test]
1136 fn btree_map_multiple_errors() {
1137 use std::collections::BTreeMap;
1138
1139 let err = BTreeMap::<String, bool>::from_meta(
1140 &pm(quote!(ignore(hello, hello = 3, hello = false))).unwrap(),
1141 )
1142 .expect_err("Duplicates and bad values should error");
1143
1144 assert_eq!(err.len(), 3);
1145 let errors = err.into_iter().collect::<Vec<_>>();
1146 assert!(errors[0].has_span());
1147 assert!(errors[1].has_span());
1148 assert!(errors[2].has_span());
1149 }
1150
1151 #[test]
1152 fn btree_map_ident_succeeds() {
1153 use std::collections::BTreeMap;
1154 use syn::parse_quote;
1155
1156 let comparison = {
1157 let mut c = BTreeMap::<syn::Ident, bool>::new();
1158 c.insert(parse_quote!(first), true);
1159 c.insert(parse_quote!(second), false);
1160 c
1161 };
1162
1163 assert_eq!(
1164 fm::<BTreeMap<syn::Ident, bool>>(quote!(ignore(first, second = false))),
1165 comparison
1166 );
1167 }
1168
1169 #[test]
1170 fn btree_map_ident_rejects_non_idents() {
1171 use std::collections::BTreeMap;
1172
1173 let err: Result<BTreeMap<syn::Ident, bool>> =
1174 FromMeta::from_meta(&pm(quote!(ignore(first, the::second))).unwrap());
1175
1176 err.unwrap_err();
1177 }
1178
1179 #[test]
1180 fn btree_map_expr_values_succeed() {
1181 use std::collections::BTreeMap;
1182 use syn::parse_quote;
1183
1184 let comparison: BTreeMap<String, syn::Expr> = vec![
1185 ("hello", parse_quote!(2 + 2)),
1186 ("world", parse_quote!(x.foo())),
1187 ]
1188 .into_iter()
1189 .map(|(k, v)| (k.to_string(), v))
1190 .collect();
1191
1192 assert_eq!(
1193 fm::<BTreeMap<String, syn::Expr>>(quote!(ignore(hello = 2 + 2, world = x.foo()))),
1194 comparison
1195 );
1196 }
1197
1198 #[test]
1201 fn darling_result_succeeds() {
1202 fm::<Result<()>>(quote!(ignore)).unwrap();
1203 fm::<Result<()>>(quote!(ignore(world))).unwrap_err();
1204 }
1205
1206 #[test]
1208 fn test_punctuated() {
1209 fm::<syn::punctuated::Punctuated<syn::FnArg, syn::token::Comma>>(quote!(
1210 ignore = "a: u8, b: Type"
1211 ));
1212 fm::<syn::punctuated::Punctuated<syn::Expr, syn::token::Comma>>(quote!(ignore = "a, b, c"));
1213 }
1214
1215 #[test]
1216 fn test_expr_array() {
1217 fm::<syn::ExprArray>(quote!(ignore = "[0x1, 0x2]"));
1218 fm::<syn::ExprArray>(quote!(ignore = "[\"Hello World\", \"Test Array\"]"));
1219 }
1220
1221 #[test]
1222 fn test_expr() {
1223 fm::<syn::Expr>(quote!(ignore = "x + y"));
1224 fm::<syn::Expr>(quote!(ignore = "an_object.method_call()"));
1225 fm::<syn::Expr>(quote!(ignore = "{ a_statement(); in_a_block }"));
1226 }
1227
1228 #[test]
1229 fn test_expr_without_quotes() {
1230 fm::<syn::Expr>(quote!(ignore = x + y));
1231 fm::<syn::Expr>(quote!(ignore = an_object.method_call()));
1232 fm::<syn::Expr>(quote!(
1233 ignore = {
1234 a_statement();
1235 in_a_block
1236 }
1237 ));
1238 }
1239
1240 #[test]
1241 fn test_expr_path() {
1242 fm::<syn::ExprPath>(quote!(ignore = "std::mem::replace"));
1243 fm::<syn::ExprPath>(quote!(ignore = "x"));
1244 fm::<syn::ExprPath>(quote!(ignore = "example::<Test>"));
1245 }
1246
1247 #[test]
1248 fn test_expr_path_without_quotes() {
1249 fm::<syn::ExprPath>(quote!(ignore = std::mem::replace));
1250 fm::<syn::ExprPath>(quote!(ignore = x));
1251 fm::<syn::ExprPath>(quote!(ignore = example::<Test>));
1252 }
1253
1254 #[test]
1255 fn test_path_without_quotes() {
1256 fm::<syn::Path>(quote!(ignore = std::mem::replace));
1257 fm::<syn::Path>(quote!(ignore = x));
1258 fm::<syn::Path>(quote!(ignore = example::<Test>));
1259 }
1260
1261 #[test]
1262 fn test_number_array() {
1263 assert_eq!(fm::<Vec<u8>>(quote!(ignore = [16, 0xff])), vec![0x10, 0xff]);
1264 assert_eq!(
1265 fm::<Vec<u16>>(quote!(ignore = "[32, 0xffff]")),
1266 vec![0x20, 0xffff]
1267 );
1268 assert_eq!(
1269 fm::<Vec<u32>>(quote!(ignore = "[48, 0xffffffff]")),
1270 vec![0x30, 0xffffffff]
1271 );
1272 assert_eq!(
1273 fm::<Vec<u64>>(quote!(ignore = "[64, 0xffffffffffffffff]")),
1274 vec![0x40, 0xffffffffffffffff]
1275 );
1276 assert_eq!(
1277 fm::<Vec<usize>>(quote!(ignore = "[80, 0xffffffff]")),
1278 vec![0x50, 0xffffffff]
1279 );
1280 }
1281
1282 #[test]
1283 fn test_lit_array() {
1284 fm::<Vec<syn::LitStr>>(quote!(ignore = "[\"Hello World\", \"Test Array\"]"));
1285 fm::<Vec<syn::LitStr>>(quote!(ignore = ["Hello World", "Test Array"]));
1286 fm::<Vec<syn::LitChar>>(quote!(ignore = "['a', 'b', 'c']"));
1287 fm::<Vec<syn::LitBool>>(quote!(ignore = "[true]"));
1288 fm::<Vec<syn::LitStr>>(quote!(ignore = "[]"));
1289 fm::<Vec<syn::LitStr>>(quote!(ignore = []));
1290 fm::<Vec<syn::LitBool>>(quote!(ignore = [true, false]));
1291 }
1292
1293 #[test]
1294 fn expr_range_without_quotes() {
1295 fm::<syn::ExprRange>(quote!(ignore = 0..5));
1296 fm::<syn::ExprRange>(quote!(ignore = 0..=5));
1297 fm::<syn::ExprRange>(quote!(ignore = ..5));
1298 fm::<syn::ExprRange>(quote!(ignore = ..(x + y)));
1299 }
1300}