vitaminc_random_derives/
lib.rs

1use proc_macro::TokenStream;
2use quote::quote;
3use syn::{parse_macro_input, Data, DeriveInput, Fields};
4
5#[proc_macro_derive(Generatable)]
6pub fn derive_generatable(input: TokenStream) -> TokenStream {
7    let input = parse_macro_input!(input as DeriveInput);
8    let name = input.ident;
9
10    let result_expr = match &input.data {
11        Data::Struct(data_struct) => match &data_struct.fields {
12            Fields::Named(fields_named) => {
13                let field_inits = fields_named.named.iter().map(|f| {
14                    let field_name = f.ident.as_ref().unwrap();
15                    quote! {
16                        #field_name: Generatable::random(rng)?
17                    }
18                });
19                quote! {
20                    Ok(Self {
21                        #(#field_inits),*
22                    })
23                }
24            }
25            Fields::Unnamed(fields_unnamed) => {
26                let field_inits = fields_unnamed.unnamed.iter().map(|_| {
27                    quote! {
28                        Generatable::random(rng)?
29                    }
30                });
31                quote! {
32                    Ok(Self(
33                        #(#field_inits),*
34                    ))
35                }
36            }
37            Fields::Unit => {
38                quote! { Ok(Self) }
39            }
40        },
41        Data::Enum(_) => {
42            return syn::Error::new_spanned(
43                name,
44                "#[derive(Generatable)] is not supported for enums — implement Generatable manually for cryptographic safety."
45            )
46            .to_compile_error()
47            .into();
48        }
49        Data::Union(_) => {
50            return syn::Error::new_spanned(
51                name,
52                "#[derive(Generatable)] cannot be used on unions.",
53            )
54            .to_compile_error()
55            .into();
56        }
57    };
58
59    let expanded = quote! {
60        impl Generatable for #name {
61            fn random(rng: &mut vitaminc_random::SafeRand) -> Result<Self, vitaminc_random::RandomError> {
62                #result_expr
63            }
64        };
65    };
66
67    TokenStream::from(expanded)
68}