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