parametric_derive/
lib.rs

1use proc_macro::TokenStream;
2use quote::quote;
3use syn::{Data, DeriveInput, Ident, parse_macro_input};
4
5mod parametric;
6
7#[proc_macro_derive(Parametric, attributes(parametric, skip))]
8pub fn general_derive(input: TokenStream) -> TokenStream {
9    let input = parse_macro_input!(input as DeriveInput);
10
11    let param = first_gen_param(&input);
12
13    let fields = get_fields(&input);
14
15    let impl_ = parametric::implementation(&input, &fields, &param);
16
17    TokenStream::from(quote! {
18        #[automatically_derived]
19        #impl_
20    })
21}
22
23struct MaybeSkipField {
24    name: Ident,
25    skip: bool,
26}
27
28fn first_gen_param(input: &DeriveInput) -> Ident {
29    let msg = "struct must be parameterized with at least one parameter";
30    let param = input.generics.type_params().next().expect(msg);
31    param.ident.clone()
32}
33
34fn get_fields(input: &DeriveInput) -> Vec<MaybeSkipField> {
35    let Data::Struct(ref data) = input.data else {
36        panic!("`Parametric` can only be derived for structs")
37    };
38    let syn::Fields::Named(fields) = &data.fields else {
39        panic!("`Parametric` can only be derived for structs with named fields")
40    };
41
42    let match_parametric_skip = |attr: &syn::Attribute| {
43        if !attr.path().is_ident("parametric") {
44            return false;
45        }
46
47        match attr.parse_args::<Ident>() {
48            Ok(ident) => ident == "skip",
49            Err(_) => false,
50        }
51    };
52
53    Vec::from_iter(fields.named.iter().map(|field| MaybeSkipField {
54        name: field.ident.clone().unwrap(),
55        skip: field.attrs.iter().any(match_parametric_skip),
56    }))
57}