vitaminc_random_derives/
lib.rs1use 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}