haste_reflect_derive/
lib.rs

1use proc_macro::TokenStream;
2use quote::quote;
3use syn::{Attribute, Data, DeriveInput, Expr, Field, Lit, Meta, parse_macro_input};
4
5fn get_attribute_rename(attrs: &[Attribute]) -> Option<String> {
6    attrs.iter().find_map(|attr| match &attr.meta {
7        Meta::NameValue(name_value) => {
8            if name_value.path.is_ident("rename_field") {
9                match &name_value.value {
10                    Expr::Lit(lit) => match &lit.lit {
11                        Lit::Str(lit) => Some(lit.value()),
12                        _ => panic!("Expected a string literal"),
13                    },
14                    _ => panic!("Expected a string literal"),
15                }
16            } else {
17                None
18            }
19        }
20        _ => None,
21    })
22}
23
24fn is_optional(field: &Field) -> bool {
25    if let syn::Type::Path(type_path) = &field.ty {
26        if let Some(segment) = type_path.path.segments.first() {
27            return segment.ident == "Option";
28        }
29    }
30    false
31}
32
33#[proc_macro_derive(Reflect, attributes(rename_field))]
34pub fn haste_reflect(input: TokenStream) -> TokenStream {
35    // Parse the input tokens into a syntax tree
36    let input = parse_macro_input!(input as DeriveInput);
37
38    match input.data {
39        Data::Struct(data) => {
40            let all_fields = data
41                .fields
42                .iter()
43                .map(|field| field.ident.to_owned().unwrap().to_string());
44
45            let name = input.ident;
46            let name_str = name.to_string();
47
48            let accessors = data.fields.iter().map(|field| {
49                let renamed = get_attribute_rename(&field.attrs);
50                let name = if let Some(renamed_field) = renamed {
51                    renamed_field
52                } else {
53                    field.ident.to_owned().unwrap().to_string()
54                };
55
56                let accessor = field.ident.to_owned().unwrap();
57                if is_optional(field) {
58                    quote! {
59                         #name => if let Some(v) = self.#accessor.as_ref() {
60                             Some(v)
61                         } else {
62                             None
63                         }
64                    }
65                } else {
66                    quote! {
67                        #name => Some(&self.#accessor)
68                    }
69                }
70            });
71
72            let mutable_accessor = data.fields.iter().map(|field| {
73                let renamed = get_attribute_rename(&field.attrs);
74                let name = if let Some(renamed_field) = renamed {
75                    renamed_field
76                } else {
77                    field.ident.to_owned().unwrap().to_string()
78                };
79
80                let accessor = field.ident.to_owned().unwrap();
81                // For mutable accessors, we return nested Option types that are None
82                // So that the caller can choose to initialize them if needed.
83                quote! {
84                    #name => Some(&mut self.#accessor)
85                }
86            });
87
88            let expanded = quote! {
89                impl haste_reflect::MetaValue for #name {
90                    fn fields(&self) -> Vec<&'static str> {
91                        vec![
92                            #(#all_fields),*
93                        ]
94                    }
95
96                    fn get_field<'a>(&'a self, field: &str) -> Option<&'a dyn MetaValue> {
97                        match field {
98                            #(#accessors),*
99                            ,_ => None,
100                        }
101                    }
102
103                    fn get_field_mut<'a>(&'a mut self, field: &str) -> Option<&'a mut dyn MetaValue> {
104                         match field {
105                            #(#mutable_accessor),*
106                            ,_ => None,
107                        }
108                    }
109
110                    fn get_index_mut<'a>(&'a mut self, index: usize) -> Option<&'a mut dyn MetaValue> {
111                        None
112                    }
113
114                    fn get_index<'a>(&'a self, _index: usize) -> Option<&'a dyn MetaValue> {
115                        None
116                    }
117
118                    fn typename(&self) -> &'static str {
119                        #name_str
120                    }
121
122                    fn as_any(&self) -> &dyn std::any::Any {
123                        self
124                    }
125
126                    fn flatten(&self) -> Vec<&dyn MetaValue> {
127                        vec![self]
128                    }
129
130                }
131            };
132
133            expanded.into()
134        }
135
136        Data::Enum(data) => {
137            let enum_name = input.ident;
138
139            let variants_fields = data.variants.iter().map(|variant| {
140                let name = variant.ident.to_owned();
141                quote! {
142                    Self::#name(k) => k.fields()
143                }
144            });
145
146            let variants_get_field = data.variants.iter().map(|variant| {
147                let name = variant.ident.to_owned();
148                quote! {
149                    Self::#name(k) => k.get_field(field)
150                }
151            });
152
153            let variants_get_index = data.variants.iter().map(|variant| {
154                let name = variant.ident.to_owned();
155                quote! {
156                    Self::#name(k) => k.get_index(field)
157                }
158            });
159
160            let variants_get_field_mut = data.variants.iter().map(|variant| {
161                let name = variant.ident.to_owned();
162                quote! {
163                    Self::#name(k) => k.get_field_mut(field)
164                }
165            });
166
167            let variants_get_index_mut = data.variants.iter().map(|variant| {
168                let name = variant.ident.to_owned();
169                quote! {
170                    Self::#name(k) => k.get_index_mut(index)
171                }
172            });
173
174            let variants_typename = data.variants.iter().map(|variant| {
175                let name = variant.ident.to_owned();
176                quote! {
177                    Self::#name(k) => k.typename()
178                }
179            });
180
181            let variants_as_any = data.variants.iter().map(|variant| {
182                let name = variant.ident.to_owned();
183                quote! {
184                    Self::#name(k) => k.as_any()
185                }
186            });
187
188            let variants_flatten = data.variants.iter().map(|variant| {
189                let name = variant.ident.to_owned();
190                quote! {
191                    Self::#name(k) => k.flatten()
192                }
193            });
194
195            let expanded = quote! {
196                impl haste_reflect::MetaValue for #enum_name {
197                    fn fields(&self) -> Vec<&'static str> {
198                        match self {
199                            #(#variants_fields),*
200                        }
201                    }
202
203                    fn get_field<'a>(&'a self, field: &str) -> Option<&'a dyn MetaValue> {
204                        match self {
205                            #(#variants_get_field),*
206                        }
207                    }
208
209                    fn get_index<'a>(&'a self, field: usize) -> Option<&'a dyn MetaValue> {
210                        match self {
211                            #(#variants_get_index),*
212                        }
213                    }
214
215                    fn get_field_mut<'a>(&'a mut self, field: &str) -> Option<&'a mut dyn MetaValue> {
216                         match self {
217                            #(#variants_get_field_mut),*
218                        }
219                    }
220
221                    fn get_index_mut<'a>(&'a mut self, index: usize) -> Option<&'a mut dyn MetaValue> {
222                        match self {
223                            #(#variants_get_index_mut),*
224                        }
225                    }
226
227                    fn typename(&self) ->  &'static str {
228                        match self {
229                            #(#variants_typename),*
230                        }
231                    }
232
233                    fn as_any(&self) -> &dyn std::any::Any {
234                        match self {
235                            #(#variants_as_any),*
236                        }
237                    }
238
239                    fn flatten(&self) -> Vec<&dyn MetaValue> {
240                        match self {
241                            #(#variants_flatten),*
242                        }
243                    }
244                }
245            };
246
247            // println!("{}", expanded);
248
249            expanded.into()
250        }
251
252        Data::Union(_data) => {
253            todo!("Union not supported");
254        }
255    }
256}