rustructure_macros/
lib.rs1use 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}