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