• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use crate::ARBITRARY_ATTRIBUTE_NAME;
2 use syn::{
3     parse::Error, punctuated::Punctuated, DeriveInput, Lit, Meta, MetaNameValue, NestedMeta, Token,
4     TypeParam,
5 };
6 
7 pub struct ContainerAttributes {
8     /// Specify type bounds to be applied to the derived `Arbitrary` implementation instead of the
9     /// default inferred bounds.
10     ///
11     /// ```ignore
12     /// #[arbitrary(bound = "T: Default, U: Debug")]
13     /// ```
14     ///
15     /// Multiple attributes will be combined as long as they don't conflict, e.g.
16     ///
17     /// ```ignore
18     /// #[arbitrary(bound = "T: Default")]
19     /// #[arbitrary(bound = "U: Default")]
20     /// ```
21     pub bounds: Option<Vec<Punctuated<TypeParam, Token![,]>>>,
22 }
23 
24 impl ContainerAttributes {
from_derive_input(derive_input: &DeriveInput) -> Result<Self, Error>25     pub fn from_derive_input(derive_input: &DeriveInput) -> Result<Self, Error> {
26         let mut bounds = None;
27 
28         for attr in &derive_input.attrs {
29             if !attr.path.is_ident(ARBITRARY_ATTRIBUTE_NAME) {
30                 continue;
31             }
32 
33             let meta_list = match attr.parse_meta()? {
34                 Meta::List(l) => l,
35                 _ => {
36                     return Err(Error::new_spanned(
37                         attr,
38                         format!(
39                             "invalid `{}` attribute. expected list",
40                             ARBITRARY_ATTRIBUTE_NAME
41                         ),
42                     ))
43                 }
44             };
45 
46             for nested_meta in meta_list.nested.iter() {
47                 match nested_meta {
48                     NestedMeta::Meta(Meta::NameValue(MetaNameValue {
49                         path,
50                         lit: Lit::Str(bound_str_lit),
51                         ..
52                     })) if path.is_ident("bound") => {
53                         bounds
54                             .get_or_insert_with(Vec::new)
55                             .push(bound_str_lit.parse_with(Punctuated::parse_terminated)?);
56                     }
57                     _ => {
58                         return Err(Error::new_spanned(
59                             attr,
60                             format!(
61                                 "invalid `{}` attribute. expected `bound = \"..\"`",
62                                 ARBITRARY_ATTRIBUTE_NAME,
63                             ),
64                         ))
65                     }
66                 }
67             }
68         }
69 
70         Ok(Self { bounds })
71     }
72 }
73