rustructure_macros/
lib.rs

1use proc_macro::TokenStream;
2use quote::quote;
3
4#[proc_macro_derive(Walkable, attributes(skip_walk))]
5pub fn walkable(tokens: TokenStream) -> TokenStream {
6    let input = syn::parse_macro_input!(tokens as syn::DeriveInput);
7
8    if let syn::Data::Struct(s) = &input.data {
9        struct_walkable(&input, s)
10    } else if let syn::Data::Enum(e) = &input.data {
11        enum_walkable(&input, e)
12    } else {
13        todo!("Unions currently not implemented")
14    }
15}
16
17fn fields_walkable<'i, I: Iterator<Item = &'i syn::Field>>(input: I) -> proc_macro2::TokenStream {
18    let mut fields = Vec::new();
19    let mut index = 0;
20    for field in input {
21        let skip = field
22            .attrs
23            .iter()
24            .any(|a| a.parse_meta().unwrap().path().is_ident("skip_walk"));
25
26        let ident = if let Some(i) = &field.ident {
27            i.to_string()
28        } else {
29            index += 1;
30            format!("{}", index - 1)
31        };
32
33        let ty = &field.ty;
34        if skip {
35            fields.push(quote! { walker.visit_skip_field(#ident) })
36        } else {
37            fields.push(quote! { walker.visit_field::<#ty>(#ident) })
38        }
39    }
40
41    quote! { #(#fields);* }
42}
43
44fn struct_walkable(input: &syn::DeriveInput, ds: &syn::DataStruct) -> TokenStream {
45    let name = &input.ident;
46    let name_str = input.ident.to_string();
47
48    let walk_fields = match &ds.fields {
49        syn::Fields::Unit => quote! {},
50        syn::Fields::Named(u) => fields_walkable(u.named.iter()),
51        syn::Fields::Unnamed(u) => fields_walkable(u.unnamed.iter()),
52    };
53
54    let lifetimes = (&input).generics.lifetimes().collect::<Vec<_>>();
55
56    let lifetime_markers = input.generics.lifetimes().map(|x| {
57        let name = &x.lifetime.ident;
58        quote!{
59            #name: std::marker::PhantomData<&#x u8>
60        }
61    });
62
63    quote! {
64        impl<#(#lifetimes),*> ::rustructure::Walkable for #name<#(#lifetimes),*> {
65            fn walk_with<W: ::rustructure::Walker>(walker: &mut W) {
66                struct Fields<#(#lifetimes),*> { #(#lifetime_markers),* };
67                impl<#(#lifetimes),*> ::rustructure::Walkable for Fields<#(#lifetimes),*> {
68                    fn walk_with<W: ::rustructure::Walker>(walker: &mut W) {
69                        #walk_fields
70                    }
71                }
72
73                walker.visit_struct::<Fields<#(#lifetimes),*>>(#name_str);
74            }
75        }
76    }.into()
77}
78
79fn enum_walkable(input: &syn::DeriveInput, de: &syn::DataEnum) -> TokenStream {
80    let name = &input.ident;
81    let name_str = input.ident.to_string();
82
83    let mut walk_variants = Vec::new();
84
85    for variant in &de.variants {
86        let variant_name = &variant.ident;
87        let variant_name_str = variant_name.to_string();
88
89        let visit_fields = fields_walkable(variant.fields.iter());
90
91        walk_variants.push(quote! {
92            #[allow(non_camel_case_types)]
93            struct #variant_name {};
94            impl ::rustructure::Walkable for #variant_name {
95                fn walk_with<W: ::rustructure::Walker>(walker: &mut W) {
96                    #visit_fields
97                }
98            }
99
100            walker.visit_variant::<#variant_name>(#variant_name_str);
101        });
102    }
103
104    quote! {
105        impl ::rustructure::Walkable for #name {
106            fn walk_with<W: ::rustructure::Walker>(walker: &mut W) {
107
108                struct WalkEnum {};
109                impl ::rustructure::Walkable for WalkEnum {
110                    fn walk_with<W: ::rustructure::Walker>(walker: &mut W) {
111                        #(#walk_variants);*
112                    }
113                }
114
115                walker.visit_enum::<WalkEnum>(#name_str);
116            }
117        }
118    }
119    .into()
120}