1 use crate::ARBITRARY_ATTRIBUTE_NAME; 2 use syn::{ 3 parse::Error, punctuated::Punctuated, DeriveInput, Expr, ExprLit, Lit, Meta, MetaNameValue, 4 Token, 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.meta { 34 Meta::List(ref 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 47 meta_list.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated)? 48 { 49 match nested_meta { 50 Meta::NameValue(MetaNameValue { 51 path, 52 value: 53 Expr::Lit(ExprLit { 54 lit: Lit::Str(bound_str_lit), 55 .. 56 }), 57 .. 58 }) if path.is_ident("bound") => { 59 bounds 60 .get_or_insert_with(Vec::new) 61 .push(bound_str_lit.parse_with(Punctuated::parse_terminated)?); 62 } 63 _ => { 64 return Err(Error::new_spanned( 65 attr, 66 format!( 67 "invalid `{}` attribute. expected `bound = \"..\"`", 68 ARBITRARY_ATTRIBUTE_NAME, 69 ), 70 )) 71 } 72 } 73 } 74 } 75 76 Ok(Self { bounds }) 77 } 78 } 79