1use proc_macro::TokenStream;
2use proc_macro2::TokenStream as TokenStream2;
3use quote::quote;
4use regex::Regex;
5use syn::{parse_macro_input, DeriveInput, Type};
6
7fn canonicalized_type_name(ty: &Type) -> String {
10 let type_name = quote! {#ty}.to_string();
11 let re = Regex::new(r"\s*;\s*").expect("Bad regex");
12 re.replace_all(&type_name, r"; ").to_string()
13}
14
15fn add_type(ty: &Type, auto_types: &mut Vec<TokenStream2>) {
17 match ty {
18 Type::Array(a) => add_type(&a.elem, auto_types),
19 Type::Tuple(t) => {
20 for elem in &t.elems {
21 add_type(elem, auto_types);
22 }
23 }
24 _ => (),
25 }
26
27 auto_types.push(quote! {
28 <#ty>::add_to_btf(btf)?;
29 });
30}
31
32#[proc_macro_derive(AddToBtf, attributes(field))]
36pub fn derive_add_to_bpf(input: TokenStream) -> TokenStream {
37 let input: DeriveInput = parse_macro_input!(input);
38 let name = &input.ident;
39
40 let mut auto_types = vec![];
41 let mut fields = vec![];
42 match input.data {
43 syn::Data::Struct(s) => {
44 for field in s.fields {
45 let field_name = field.ident.expect("Field has no name").to_string();
46 let ty = field.ty;
47 add_type(&ty, &mut auto_types);
48 let type_name = canonicalized_type_name(&ty);
49 fields.push(quote! {(#field_name, #type_name)})
50 }
51 }
52 _ => panic!("Not a structure."),
53 }
54
55 let gen = quote! {
56 impl AddToBtf for #name {
57 fn add_to_btf(btf: &mut btf::BtfTypes) -> Option<&btf::types::Type> {
58 const STRUCT_FIELDS: &[(&str, &str)] = &[#(#fields),*];
59 usize::add_to_btf(btf)?;
60 #(#auto_types)*
61 btf.add_struct(stringify!(#name), STRUCT_FIELDS)
62 }
63 }
64 };
65
66 gen.into()
67}