haste_reflect_derive/
lib.rs1use 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 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 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 expanded.into()
277 }
278
279 Data::Union(_data) => {
280 todo!("Union not supported");
281 }
282 }
283}