Add default attribute to TestRandom derive.

Allows for generating the item from default instead of randomizing it.
This commit is contained in:
Paul Hauner 2019-03-16 14:30:21 +11:00
parent f739bb5551
commit 01bfd38637
No known key found for this signature in database
GPG Key ID: D362883A9218FCC6

View File

@ -4,7 +4,20 @@ use crate::proc_macro::TokenStream;
use quote::quote; use quote::quote;
use syn::{parse_macro_input, DeriveInput}; use syn::{parse_macro_input, DeriveInput};
#[proc_macro_derive(TestRandom)] /// Returns true if some field has an attribute declaring it should be generated from default (not
/// randomized).
///
/// The field attribute is: `#[test_random(default)]`
fn should_use_default(field: &syn::Field) -> bool {
for attr in &field.attrs {
if attr.tts.to_string() == "( default )" {
return true;
}
}
false
}
#[proc_macro_derive(TestRandom, attributes(test_random))]
pub fn test_random_derive(input: TokenStream) -> TokenStream { pub fn test_random_derive(input: TokenStream) -> TokenStream {
let derived_input = parse_macro_input!(input as DeriveInput); let derived_input = parse_macro_input!(input as DeriveInput);
let name = &derived_input.ident; let name = &derived_input.ident;
@ -14,14 +27,32 @@ pub fn test_random_derive(input: TokenStream) -> TokenStream {
_ => panic!("test_random_derive only supports structs."), _ => panic!("test_random_derive only supports structs."),
}; };
let field_names = get_named_field_idents(&struct_data); // Build quotes for fields that should be generated and those that should be built from
// `Default`.
let mut quotes = vec![];
for field in &struct_data.fields {
match &field.ident {
Some(ref ident) => {
if should_use_default(field) {
quotes.push(quote! {
#ident: <_>::default(),
});
} else {
quotes.push(quote! {
#ident: <_>::random_for_test(rng),
});
}
}
_ => panic!("test_random_derive only supports named struct fields."),
};
}
let output = quote! { let output = quote! {
impl<T: RngCore> TestRandom<T> for #name { impl<T: RngCore> TestRandom<T> for #name {
fn random_for_test(rng: &mut T) -> Self { fn random_for_test(rng: &mut T) -> Self {
Self { Self {
#( #(
#field_names: <_>::random_for_test(rng), #quotes
)* )*
} }
} }
@ -30,14 +61,3 @@ pub fn test_random_derive(input: TokenStream) -> TokenStream {
output.into() output.into()
} }
fn get_named_field_idents(struct_data: &syn::DataStruct) -> Vec<(&syn::Ident)> {
struct_data
.fields
.iter()
.map(|f| match &f.ident {
Some(ref ident) => ident,
_ => panic!("test_random_derive only supports named struct fields."),
})
.collect()
}