1use proc_macro::TokenStream;
12use proc_macro2::TokenStream as TokenStream2;
13use quote::{format_ident, quote};
14use std::collections::hash_map::DefaultHasher;
15use std::hash::{Hash, Hasher};
16use syn::{parse_macro_input, DeriveInput, Type};
17
18fn calculate_hash<T: Hash>(t: &T) -> u64 {
20 let mut s = DefaultHasher::new();
21 t.hash(&mut s);
22 s.finish()
23}
24
25fn add_type(ty: &Type, inner_types: &mut Vec<TokenStream2>) {
27 match ty {
28 Type::Array(a) => add_type(&a.elem, inner_types),
29 Type::Tuple(t) => {
30 for elem in &t.elems {
31 add_type(elem, inner_types);
32 }
33 }
34 _ => (),
35 }
36
37 let local_name = format_ident!("local_{}", calculate_hash(&ty));
38 inner_types.push(quote! {
39 let #local_name = <#ty>::add_to_database(database)?;
40 });
41}
42
43#[proc_macro_derive(AddToTypeDatabase, attributes(field))]
46pub fn derive_add_to_database(input: TokenStream) -> TokenStream {
47 let input: DeriveInput = parse_macro_input!(input);
48 let name = &input.ident;
49
50 let mut inner_types = vec![];
51 let mut fields = vec![];
52 match input.data {
53 syn::Data::Struct(s) => {
54 for field in s.fields {
55 let field_name = field.ident.expect("Field has no name").to_string();
56 let ty = field.ty;
57 add_type(&ty, &mut inner_types);
58 let local_name = format_ident!("local_{}", calculate_hash(&ty));
59 fields.push(quote! {(#field_name, #local_name)});
60 }
61 }
62 _ => panic!("Not a structure."),
63 }
64
65 let gen = quote! {
66 impl bpf_script::types::AddToTypeDatabase for #name {
67 fn add_to_database(database: &mut bpf_script::types::TypeDatabase) -> bpf_script::error::Result<usize> {
68 #(#inner_types)*
69 let struct_fields = [#(#fields),*].to_vec();
70 database.add_struct_by_ids(Some(stringify!(#name)), struct_fields.as_slice())
71 }
72 }
73 };
74
75 gen.into()
76}