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, ¶m);
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}