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 && let Some(segment) = type_path.path.segments.first()
27 {
28 return segment.ident == "Option";
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
52fn expand_struct(input: &DeriveInput, data: &syn::DataStruct) -> TokenStream {
53 let fhir_type = get_attribute_fhir_type(&input.attrs);
54 let all_fields = data
55 .fields
56 .iter()
57 .map(|field| field.ident.as_ref().unwrap().to_string());
58
59 let name = &input.ident;
60
61 let accessors = data.fields.iter().map(|field| {
62 let renamed = get_attribute_rename(&field.attrs);
63 let name = renamed.unwrap_or_else(|| field.ident.as_ref().unwrap().to_string());
64
65 let accessor = field.ident.clone().unwrap();
66
67 if is_optional(field) {
68 quote! {
69 #name => if let Some(v) = self.#accessor.as_ref() {
70 Some(v)
71 } else {
72 None
73 }
74 }
75 } else {
76 quote! {
77 #name => Some(&self.#accessor)
78 }
79 }
80 });
81
82 let mutable_accessor = data.fields.iter().map(|field| {
83 let renamed = get_attribute_rename(&field.attrs);
84 let name = renamed.unwrap_or_else(|| field.ident.as_ref().unwrap().to_string());
85
86 let accessor = field.ident.clone().unwrap();
87
88 quote! {
89 #name => Some(&mut self.#accessor)
90 }
91 });
92
93 quote! {
94 impl haste_reflect::MetaValue for #name {
95 fn fields(&self) -> Vec<&'static str> {
96 vec![#(#all_fields),*]
97 }
98
99 fn get_field<'a>(&'a self, field: &str) -> Option<&'a dyn MetaValue> {
100 match field {
101 #(#accessors),*,
102 _ => None,
103 }
104 }
105
106 fn get_field_mut<'a>(&'a mut self, field: &str) -> Option<&'a mut dyn MetaValue> {
107 match field {
108 #(#mutable_accessor),*,
109 _ => None,
110 }
111 }
112
113 fn get_index_mut<'a>(&'a mut self, _index: usize) -> Option<&'a mut dyn MetaValue> {
114 None
115 }
116
117 fn get_index<'a>(&'a self, _index: usize) -> Option<&'a dyn MetaValue> {
118 None
119 }
120
121 fn fhir_type(&self) -> &'static str {
122 #fhir_type
123 }
124
125 fn as_any(&self) -> &dyn std::any::Any {
126 self
127 }
128
129 fn flatten(&self) -> Vec<&dyn MetaValue> {
130 vec![self]
131 }
132
133 fn is_many(&self) -> bool {
134 false
135 }
136 }
137 }
138 .into()
139}
140
141fn expand_enum(input: &DeriveInput, data: &syn::DataEnum) -> TokenStream {
142 let enum_name = &input.ident;
143
144 let variants_fields = data.variants.iter().map(|variant| {
145 let name = variant.ident.clone();
146 quote! { Self::#name(k) => k.fields() }
147 });
148
149 let variants_get_field = data.variants.iter().map(|variant| {
150 let name = variant.ident.clone();
151 quote! { Self::#name(k) => k.get_field(field) }
152 });
153
154 let variants_get_index = data.variants.iter().map(|variant| {
155 let name = variant.ident.clone();
156 quote! { Self::#name(k) => k.get_index(field) }
157 });
158
159 let variants_get_field_mut = data.variants.iter().map(|variant| {
160 let name = variant.ident.clone();
161 quote! { Self::#name(k) => k.get_field_mut(field) }
162 });
163
164 let variants_get_index_mut = data.variants.iter().map(|variant| {
165 let name = variant.ident.clone();
166 quote! { Self::#name(k) => k.get_index_mut(index) }
167 });
168
169 let variants_as_any = data.variants.iter().map(|variant| {
170 let name = variant.ident.clone();
171 quote! { Self::#name(k) => k.as_any() }
172 });
173
174 let variants_flatten = data.variants.iter().map(|variant| {
175 let name = variant.ident.clone();
176 quote! { Self::#name(k) => k.flatten() }
177 });
178
179 let variants_fhir_type = data.variants.iter().map(|variant| {
180 let name = variant.ident.clone();
181 quote! { Self::#name(k) => k.fhir_type() }
182 });
183
184 quote! {
185 impl haste_reflect::MetaValue for #enum_name {
186 fn fields(&self) -> Vec<&'static str> {
187 match self {
188 #(#variants_fields),*
189 }
190 }
191
192 fn get_field<'a>(&'a self, field: &str) -> Option<&'a dyn MetaValue> {
193 match self {
194 #(#variants_get_field),*
195 }
196 }
197
198 fn get_index<'a>(&'a self, field: usize) -> Option<&'a dyn MetaValue> {
199 match self {
200 #(#variants_get_index),*
201 }
202 }
203
204 fn get_field_mut<'a>(&'a mut self, field: &str) -> Option<&'a mut dyn MetaValue> {
205 match self {
206 #(#variants_get_field_mut),*
207 }
208 }
209
210 fn get_index_mut<'a>(&'a mut self, index: usize) -> Option<&'a mut dyn MetaValue> {
211 match self {
212 #(#variants_get_index_mut),*
213 }
214 }
215
216 fn fhir_type(&self) -> &'static str {
217 match self {
218 #(#variants_fhir_type),*
219 }
220 }
221
222 fn as_any(&self) -> &dyn std::any::Any {
223 match self {
224 #(#variants_as_any),*
225 }
226 }
227
228 fn flatten(&self) -> Vec<&dyn MetaValue> {
229 match self {
230 #(#variants_flatten),*
231 }
232 }
233
234 fn is_many(&self) -> bool {
235 false
236 }
237 }
238 }
239 .into()
240}
241
242#[proc_macro_derive(Reflect, attributes(rename_field, fhir_type))]
243pub fn haste_reflect(input: TokenStream) -> TokenStream {
244 let input = parse_macro_input!(input as DeriveInput);
245
246 match input.data {
247 Data::Struct(ref data) => expand_struct(&input, data),
248 Data::Enum(ref data) => expand_enum(&input, data),
249 Data::Union(_) => todo!("Union not supported"),
250 }
251}