cfx_vm_tracer_derive/
lib.rs1use proc_macro::TokenStream;
2use quote::quote;
3use syn::{Data, DataStruct, DeriveInput, Error, Fields, Ident};
4
5type Result<T> = std::result::Result<T, Error>;
6
7macro_rules! unwrap_or_compile_error {
8 ($e:expr) => {
9 match $e {
10 Ok(x) => x,
11 Err(e) => return e.into_compile_error().into(),
12 }
13 };
14}
15
16#[proc_macro_derive(AsTracer, attributes(skip_tracer))]
17pub fn generate_as_tracer_function(input: TokenStream) -> TokenStream {
18 let input = syn::parse_macro_input!(input as DeriveInput);
19 let name = &input.ident;
20
21 let data = unwrap_or_compile_error!(get_struct_data(&input));
22 let skipped_fields = unwrap_or_compile_error!(check_all_fields(data));
23 let field_names = unwrap_or_compile_error!(get_field_names(data, name));
24 unwrap_or_compile_error!(check_num_fields(&field_names, name, 10));
25
26 let mut match_arms = Vec::new();
27 let match_more_fields = if skipped_fields {
28 quote!(, ..)
29 } else {
30 quote!()
31 };
32
33 for mask in 0..(1 << field_names.len()) {
34 let mut this_combination = Vec::new();
35 let mut tuple_elements = Vec::new();
36 for (index, field) in field_names.iter().enumerate() {
37 if (mask >> index) & 1 == 1 {
38 this_combination.push(quote! { #field: Some(#field) });
39 tuple_elements.push(quote! { #field });
40 } else {
41 this_combination.push(quote! { #field: None });
42 }
43 }
44 let match_arm = match tuple_elements.len() {
45 0 => quote! {
46 #name {#(#this_combination),* #match_more_fields} => Box::new(()) },
48 1 => quote! {
49 #name {#(#this_combination),* #match_more_fields} => Box::new(#(#tuple_elements),*) },
51 _ => quote! {
52 #name {#(#this_combination),* #match_more_fields} => Box::new((#(#tuple_elements),*)) },
54 };
55 match_arms.push(match_arm);
56 }
57
58 let expanded = quote! {
59 impl AsTracer for #name {
60 fn as_tracer<'a>(&'a mut self) -> Box<dyn 'a + TracerTrait> {
61 match self {
62 #(#match_arms,)*
63 }
64 }
65 }
66 };
67
68 expanded.into()
69}
70
71#[proc_macro_derive(DrainTrace)]
72pub fn generate_drain_trace_function(input: TokenStream) -> TokenStream {
73 let input = syn::parse_macro_input!(input as DeriveInput);
74 let name = &input.ident;
75
76 let data = unwrap_or_compile_error!(get_struct_data(&input));
77 let field_names = unwrap_or_compile_error!(get_field_names(data, name));
78
79 let drain_statements = field_names.iter().map(|field| {
80 quote! { self.#field.drain_trace(map); }
81 });
82
83 let expanded = quote! {
84 impl DrainTrace for #name {
85 fn drain_trace(self, map: &mut typemap::ShareDebugMap) {
86 #(#drain_statements)*
87 }
88 }
89 };
90
91 expanded.into()
92}
93
94fn get_struct_data(input: &DeriveInput) -> Result<&DataStruct> {
95 match &input.data {
96 Data::Struct(data) => Ok(data),
97 _ => Err(Error::new_spanned(&input.ident, "Only struct is supported")),
98 }
99}
100
101fn check_all_fields(data: &DataStruct) -> Result<bool> {
102 let mut type_error = vec![];
103 let mut skipped_field = false;
104 for field in &data.fields {
105 if field
106 .attrs
107 .iter()
108 .any(|attr| attr.path.is_ident("skip_tracer"))
109 {
110 skipped_field = true;
111 continue;
112 }
113
114 if let syn::Type::Path(type_path) = &field.ty {
115 if type_path
116 .path
117 .segments
118 .last()
119 .map_or(false, |seg| seg.ident == "Option")
120 {
121 continue;
122 }
123 }
124 type_error.push(Error::new_spanned(
125 &field.ty,
126 "All fields must be of type Option",
127 ));
128 }
129 if !type_error.is_empty() {
130 let mut type_error_iter = type_error.into_iter();
131 let mut error = type_error_iter.next().unwrap();
132 error.extend(type_error_iter);
133 Err(error)
134 } else {
135 Ok(skipped_field)
136 }
137}
138
139fn get_field_names<'a>(
140 data: &'a DataStruct, name: &Ident,
141) -> Result<Vec<&'a Option<Ident>>> {
142 match &data.fields {
143 Fields::Named(fields) => Ok(fields
144 .named
145 .iter()
146 .filter(|f| {
147 !f.attrs.iter().any(|attr| attr.path.is_ident("skip_tracer"))
148 })
149 .map(|f| &f.ident)
150 .collect::<Vec<_>>()),
151 _ => Err(Error::new_spanned(&name, "Only named struct is supported")),
152 }
153}
154
155fn check_num_fields(
156 field_names: &Vec<&Option<Ident>>, name: &Ident, max_entries: usize,
157) -> Result<()> {
158 if field_names.len() > max_entries {
159 Err(Error::new_spanned(
160 name,
161 "Too many fields in the struct! Limit is 10.",
162 ))
163 } else {
164 Ok(())
165 }
166}