priority_lfu_derive/
lib.rs1extern crate proc_macro;
8
9use proc_macro2::TokenStream;
10use quote::{quote, quote_spanned};
11use syn::spanned::Spanned;
12use syn::{
13 Data, DeriveInput, Fields, GenericParam, Generics, Index, parse_macro_input, parse_quote,
14};
15
16fn crate_path() -> TokenStream {
19 let is_priority_lfu_lib =
26 std::env::var("CARGO_CRATE_NAME").is_ok_and(|name| name == "priority_lfu");
27 let is_doctest = std::env::var_os("UNSTABLE_RUSTDOC_TEST_PATH").is_some()
28 || std::env::var_os("RUSTDOC").is_some();
29
30 if is_priority_lfu_lib && !is_doctest {
31 quote!(crate)
32 } else {
33 quote!(::priority_lfu)
34 }
35}
36
37#[proc_macro_derive(DeepSizeOf)]
38pub fn derive_deep_size(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
39 let input = parse_macro_input!(input as DeriveInput);
41
42 let name = input.ident;
44
45 let krate = crate_path();
46
47 let generics = add_trait_bounds(input.generics, &krate);
49 let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
50
51 let sum = deepsize_sum(&input.data, &name, &krate);
53
54 let expanded = quote! {
55 impl #impl_generics #krate::DeepSizeOf for #name #ty_generics #where_clause {
57 fn deep_size_of_children(&self, context: &mut #krate::Context) -> usize {
58 #sum
59 }
60 }
61 };
62
63 proc_macro::TokenStream::from(expanded)
65}
66
67fn add_trait_bounds(mut generics: Generics, krate: &TokenStream) -> Generics {
69 for param in &mut generics.params {
70 if let GenericParam::Type(ref mut type_param) = *param {
71 type_param.bounds.push(parse_quote!(#krate::DeepSizeOf));
72 }
73 }
74 generics
75}
76
77fn match_fields(fields: &syn::Fields, krate: &TokenStream) -> TokenStream {
78 match fields {
79 Fields::Named(fields) => {
80 let recurse = fields.named.iter().map(|f| {
81 let name = &f.ident;
82 quote_spanned! {f.span()=>
83 #krate::DeepSizeOf::deep_size_of_children(&self.#name, context)
84 }
85 });
86 quote! {
87 0 #(+ #recurse)*
88 }
89 }
90 Fields::Unnamed(fields) => {
91 let recurse = fields.unnamed.iter().enumerate().map(|(i, f)| {
92 let index = Index::from(i);
93 quote_spanned! {f.span()=>
94 #krate::DeepSizeOf::deep_size_of_children(&self.#index, context)
95 }
96 });
97 quote! {
98 0 #(+ #recurse)*
99 }
100 }
101 Fields::Unit => {
102 quote!(0)
104 }
105 }
106}
107
108fn match_enum_fields(fields: &syn::Fields, krate: &TokenStream) -> TokenStream {
109 match fields {
110 Fields::Named(fields) => {
111 let recurse = fields.named.iter().map(|f| {
112 let name = &f.ident;
113 quote_spanned! {f.span()=>
114 #krate::DeepSizeOf::deep_size_of_children(#name, context)
115 }
116 });
117 quote! {
118 0 #(+ #recurse)*
119 }
120 }
121 Fields::Unnamed(fields) => {
122 let recurse = fields.unnamed.iter().enumerate().map(|(i, f)| {
123 let i = syn::Ident::new(&format!("_{}", i), proc_macro2::Span::call_site());
124 quote_spanned! {f.span()=>
125 #krate::DeepSizeOf::deep_size_of_children(#i, context)
126 }
127 });
128 quote! {
129 0 #(+ #recurse)*
130 }
131 }
132 Fields::Unit => {
133 quote!(0)
135 }
136 }
137}
138
139fn get_matcher(var: &syn::Variant) -> TokenStream {
140 let matcher = match &var.fields {
141 Fields::Unit => TokenStream::new(),
142 Fields::Unnamed(fields) => {
143 let fields: TokenStream = (0..fields.unnamed.len())
144 .map(|n| {
145 let i = syn::Ident::new(&format!("_{}", n), proc_macro2::Span::call_site());
146 quote!(#i,)
147 })
148 .collect();
149 quote!((#fields))
150 }
151 Fields::Named(fields) => {
152 let fields: TokenStream = fields
153 .named
154 .iter()
155 .map(|f| {
156 let i = f.ident.as_ref().expect("Field identifier should be present");
157 quote!(#i,)
158 })
159 .collect();
160 quote!({#fields})
161 }
162 };
163
164 quote!(#matcher)
165}
166
167fn deepsize_sum(data: &Data, struct_name: &proc_macro2::Ident, krate: &TokenStream) -> TokenStream {
169 match *data {
170 Data::Struct(ref inner) => match_fields(&inner.fields, krate),
171 Data::Enum(ref inner) => {
172 let arms = inner.variants.iter().map(|var| {
173 let matcher = get_matcher(var);
174 let output = match_enum_fields(&var.fields, krate);
175 let name = &var.ident;
176 let ident = quote!(#struct_name::#name);
177 quote!(#ident #matcher => #output,)
178 });
179
180 quote! {
181 match self {
182 #(#arms)*
183 _ => 0 }
185 }
186 }
187 Data::Union(_) => unimplemented!(),
188 }
189}