Skip to main content

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                    fn is_many(&self) -> bool {
131                        false
132                    }
133
134                }
135            };
136
137            expanded.into()
138        }
139
140        Data::Enum(data) => {
141            let enum_name = input.ident;
142
143            let variants_fields = data.variants.iter().map(|variant| {
144                let name = variant.ident.to_owned();
145                quote! {
146                    Self::#name(k) => k.fields()
147                }
148            });
149
150            let variants_get_field = data.variants.iter().map(|variant| {
151                let name = variant.ident.to_owned();
152                quote! {
153                    Self::#name(k) => k.get_field(field)
154                }
155            });
156
157            let variants_get_index = data.variants.iter().map(|variant| {
158                let name = variant.ident.to_owned();
159                quote! {
160                    Self::#name(k) => k.get_index(field)
161                }
162            });
163
164            let variants_get_field_mut = data.variants.iter().map(|variant| {
165                let name = variant.ident.to_owned();
166                quote! {
167                    Self::#name(k) => k.get_field_mut(field)
168                }
169            });
170
171            let variants_get_index_mut = data.variants.iter().map(|variant| {
172                let name = variant.ident.to_owned();
173                quote! {
174                    Self::#name(k) => k.get_index_mut(index)
175                }
176            });
177
178            let variants_typename = data.variants.iter().map(|variant| {
179                let name = variant.ident.to_owned();
180                quote! {
181                    Self::#name(k) => k.typename()
182                }
183            });
184
185            let variants_as_any = data.variants.iter().map(|variant| {
186                let name = variant.ident.to_owned();
187                quote! {
188                    Self::#name(k) => k.as_any()
189                }
190            });
191
192            let variants_flatten = data.variants.iter().map(|variant| {
193                let name = variant.ident.to_owned();
194                quote! {
195                    Self::#name(k) => k.flatten()
196                }
197            });
198
199            let expanded = quote! {
200                impl haste_reflect::MetaValue for #enum_name {
201                    fn fields(&self) -> Vec<&'static str> {
202                        match self {
203                            #(#variants_fields),*
204                        }
205                    }
206
207                    fn get_field<'a>(&'a self, field: &str) -> Option<&'a dyn MetaValue> {
208                        match self {
209                            #(#variants_get_field),*
210                        }
211                    }
212
213                    fn get_index<'a>(&'a self, field: usize) -> Option<&'a dyn MetaValue> {
214                        match self {
215                            #(#variants_get_index),*
216                        }
217                    }
218
219                    fn get_field_mut<'a>(&'a mut self, field: &str) -> Option<&'a mut dyn MetaValue> {
220                         match self {
221                            #(#variants_get_field_mut),*
222                        }
223                    }
224
225                    fn get_index_mut<'a>(&'a mut self, index: usize) -> Option<&'a mut dyn MetaValue> {
226                        match self {
227                            #(#variants_get_index_mut),*
228                        }
229                    }
230
231                    fn typename(&self) ->  &'static str {
232                        match self {
233                            #(#variants_typename),*
234                        }
235                    }
236
237                    fn as_any(&self) -> &dyn std::any::Any {
238                        match self {
239                            #(#variants_as_any),*
240                        }
241                    }
242
243                    fn flatten(&self) -> Vec<&dyn MetaValue> {
244                        match self {
245                            #(#variants_flatten),*
246                        }
247                    }
248
249                    fn is_many(&self) -> bool {
250                        false
251                    }
252                }
253            };
254
255            // println!("{}", expanded);
256
257            expanded.into()
258        }
259
260        Data::Union(_data) => {
261            todo!("Union not supported");
262        }
263    }
264}