bpf_script_derive/
lib.rs

1//! [![Build Status](https://github.com/arcjustin/bpf-script-derive/workflows/build/badge.svg)](https://github.com/arcjustin/bpf-script-derive/actions?query=workflow%3Abuild)
2//! [![crates.io](https://img.shields.io/crates/v/bpf-script-derive.svg)](https://crates.io/crates/bpf-script-derive)
3//! [![mio](https://docs.rs/bpf-script-derive/badge.svg)](https://docs.rs/bpf-script-derive/)
4//! [![Lines of Code](https://tokei.rs/b1/github/arcjustin/bpf-script-derive?category=code)](https://tokei.rs/b1/github/arcjustin/bpf-script-derive?category=code)
5//!
6//! Provides a derive macro for `AddToDatabase` to make adding Rust types to a `bpf_script::types::TypeDatabase` easier.
7//!
8//! ## License
9//!
10//! * [MIT license](http://opensource.org/licenses/MIT)
11use 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
18/// Hashes a given value.
19fn calculate_hash<T: Hash>(t: &T) -> u64 {
20    let mut s = DefaultHasher::new();
21    t.hash(&mut s);
22    s.finish()
23}
24
25/// Recursively adds inner types to the inner_types list.
26fn 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/// Implements AddToTypeDatabase for the type. If the type is a structure, it will
44/// create a new type for the structure with the same name and all its inner fields.
45#[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}