struct_reflection_derive/
lib.rs1extern crate proc_macro;
2
3use proc_macro::TokenStream;
4use quote::quote;
5use syn::{parse_macro_input, Data, DeriveInput, Fields};
6
7#[proc_macro_derive(StructReflection)]
8pub fn struct_reflection(input: TokenStream) -> TokenStream {
9 let input = parse_macro_input!(input as DeriveInput);
10
11 let struct_name = input.ident;
12 let generics = input.generics;
13 let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
14
15 let generic_types: Vec<_> = generics.type_params().collect();
16
17 let field_list_code = match input.data {
18 Data::Struct(data_struct) => match data_struct.fields {
19 Fields::Named(fields) => fields
20 .named
21 .iter()
22 .map(|field| generate_field_code(field, &generic_types))
23 .collect(),
24 Fields::Unnamed(fields) => fields
25 .unnamed
26 .iter()
27 .enumerate()
28 .map(|(i, field)| generate_unnamed_field_code(i, field, &generic_types))
29 .collect(),
30 Fields::Unit => vec![],
31 },
32 _ => panic!("StructReflection can only be used on structs."),
33 };
34
35 let expanded = quote! {
36 impl #impl_generics StructReflectionHelper for #struct_name #ty_generics #where_clause {
37 fn struct_reflection() -> Option<Vec<String>> {
38 let mut fields = Vec::new();
39 #(#field_list_code)*
40 Some(fields)
41 }
42 }
43 };
44
45 TokenStream::from(expanded)
46}
47
48fn generate_field_code(
49 field: &syn::Field,
50 generic_types: &[&syn::TypeParam],
51) -> proc_macro2::TokenStream {
52 let field_name = field.ident.as_ref().unwrap().to_string();
53 let field_type = &field.ty;
54
55 if is_primitive_type(field_type) {
56 return quote! {
57 fields.push(#field_name.to_string());
58 };
59 }
60
61 for generic_type in generic_types {
62 if is_generic_parameter(field_type, generic_type) {
63 return quote! {
64 fields.push(#field_name.to_string());
65 };
66 }
67 }
68
69 if let Some(token_stream) = handle_tuple_type(field_type, &field_name, generic_types) {
70 return token_stream;
71 }
72
73 if let Some(token_stream) = handle_array_type(field_type, &field_name, generic_types) {
74 return token_stream;
75 }
76
77 quote! {
78 if let Some(inner_fields) = <#field_type as StructReflectionHelper>::struct_reflection() {
79 for inner_field in inner_fields {
80 fields.push(format!("{}__{}", #field_name, inner_field));
81 }
82 } else {
83 fields.push(#field_name.to_string());
84 }
85 }
86}
87
88fn generate_unnamed_field_code(
89 index: usize,
90 field: &syn::Field,
91 generic_types: &[&syn::TypeParam],
92) -> proc_macro2::TokenStream {
93 let field_type = &field.ty;
94 let index_str = index.to_string();
95
96 if is_primitive_type(field_type) {
97 return quote! {
98 fields.push(#index_str.to_string());
99 };
100 }
101
102 for generic_type in generic_types {
103 if is_generic_parameter(field_type, generic_type) {
104 return quote! {
105 fields.push(#index_str.to_string());
106 };
107 }
108 }
109
110 if let Some(token_stream) = handle_tuple_type(field_type, &index_str, generic_types) {
111 return token_stream;
112 }
113
114 if let Some(token_stream) = handle_array_type(field_type, &index_str, generic_types) {
115 return token_stream;
116 }
117
118 quote! {
119 if let Some(inner_fields) = <#field_type as StructReflectionHelper>::struct_reflection() {
120 for inner_field in inner_fields {
121 fields.push(format!("{}__{}", #index, inner_field));
122 }
123 } else {
124 fields.push(#index.to_string());
125 }
126 }
127}
128
129fn is_primitive_type(ty: &syn::Type) -> bool {
131 if let syn::Type::Path(type_path) = ty {
132 if let Some(segment) = type_path.path.segments.last() {
133 let type_name = &segment.ident.to_string();
134 return matches!(
135 type_name.as_str(),
136 "bool"
137 | "char"
138 | "u8"
139 | "u16"
140 | "u32"
141 | "u64"
142 | "u128"
143 | "usize"
144 | "i8"
145 | "i16"
146 | "i32"
147 | "i64"
148 | "i128"
149 | "isize"
150 | "f32"
151 | "f64"
152 | "str"
153 | "String"
154 );
155 }
156 }
157 false
158}
159
160fn is_generic_parameter(ty: &syn::Type, generic_type: &syn::TypeParam) -> bool {
161 if let syn::Type::Path(type_path) = ty {
162 return type_path.path.is_ident(&generic_type.ident);
163 }
164 false
165}
166
167fn is_tuple_type(ty: &syn::Type) -> bool {
168 if let syn::Type::Tuple(_) = ty {
169 return true;
170 }
171 false
172}
173
174fn handle_tuple_type(
175 ty: &syn::Type,
176 field_name: &str,
177 generic_types: &[&syn::TypeParam],
178) -> Option<proc_macro2::TokenStream> {
179 if let syn::Type::Tuple(tuple_type) = ty {
180 let mut element_handling = Vec::new();
181
182 for (i, elem) in tuple_type.elems.iter().enumerate() {
184 let index = proc_macro2::Literal::usize_unsuffixed(i);
185
186 let mut is_generic = false;
188 for generic_type in generic_types {
189 if is_generic_parameter(elem, generic_type) {
190 is_generic = true;
191 element_handling.push(quote! {
192 fields.push(format!("{}__{}", #field_name, #index));
193 });
194 break;
195 }
196 }
197
198 if !is_generic {
200 element_handling.push(quote! {
201 if let Some(inner_fields) = <#elem as StructReflectionHelper>::struct_reflection() {
202 for inner_field in inner_fields {
203 fields.push(format!("{}__{}__{}", #field_name, #index, inner_field));
204 }
205 } else {
206 fields.push(format!("{}__{}", #field_name, #index));
207 }
208 });
209 }
210 }
211
212 return Some(quote! {
213 #(#element_handling)*
214 });
215 }
216 None
217}
218
219fn handle_array_type(
221 ty: &syn::Type,
222 field_name: &str,
223 generic_types: &[&syn::TypeParam],
224) -> Option<proc_macro2::TokenStream> {
225 if let syn::Type::Array(array_type) = ty {
226 let elem_type = &*array_type.elem;
227 let array_len = &array_type.len;
228
229 if is_tuple_type(elem_type) {
231 if let syn::Type::Tuple(tuple_type) = elem_type {
232 let tuple_size = tuple_type.elems.len();
233
234 return Some(quote! {
235 for i in 0..#array_len {
236 for j in 0..#tuple_size {
237 fields.push(format!("{}__{}__{}", #field_name, i, j));
238 }
239 }
240 });
241 }
242 }
243
244 if let syn::Type::Array(inner_array_type) = elem_type {
246 let inner_elem_type = &*inner_array_type.elem;
247 let inner_array_len = &inner_array_type.len;
248
249 if is_tuple_type(inner_elem_type) {
250 if let syn::Type::Tuple(tuple_type) = inner_elem_type {
251 let tuple_size = tuple_type.elems.len();
252
253 return Some(quote! {
254 for i in 0..#array_len {
255 for j in 0..#inner_array_len {
256 for k in 0..#tuple_size {
257 fields.push(format!("{}__{}__{}__{}", #field_name, i, j, k));
258 }
259 }
260 }
261 });
262 }
263 }
264 }
265
266 if is_primitive_type(elem_type) {
268 return Some(quote! {
269 for i in 0..#array_len {
270 fields.push(format!("{}__{}", #field_name, i));
271 }
272 });
273 }
274
275 for generic_type in generic_types {
277 if is_generic_parameter(elem_type, generic_type) {
278 return Some(quote! {
279 for i in 0..#array_len {
280 fields.push(format!("{}__{}", #field_name, i));
281 }
282 });
283 }
284 }
285
286 if let syn::Type::Array(inner_array_type) = elem_type {
288 let inner_elem_type = &*inner_array_type.elem;
289 let inner_array_len = &inner_array_type.len;
290
291 if is_primitive_type(inner_elem_type) {
293 return Some(quote! {
294 for i in 0..#array_len {
295 for j in 0..#inner_array_len {
296 fields.push(format!("{}__{}__{}", #field_name, i, j));
297 }
298 }
299 });
300 }
301
302 for generic_type in generic_types {
304 if is_generic_parameter(inner_elem_type, generic_type) {
305 return Some(quote! {
306 for i in 0..#array_len {
307 for j in 0..#inner_array_len {
308 fields.push(format!("{}__{}__{}", #field_name, i, j));
309 }
310 }
311 });
312 }
313 }
314 }
315
316 return Some(quote! {
318 if let Some(sub_fields) = <#elem_type as StructReflectionHelper>::struct_reflection() {
319 for i in 0..#array_len {
320 for sub_field in &sub_fields {
321 fields.push(format!("{}__{}__{}", #field_name, i, sub_field));
322 }
323 }
324 } else {
325 for i in 0..#array_len {
326 fields.push(format!("{}__{}", #field_name, i));
327 }
328 }
329 });
330 }
331 None
332}