1#![deny(unsafe_code)]
62
63extern crate proc_macro;
64
65use proc_macro::TokenStream;
66use proc_macro2::TokenStream as TokenStream2;
67use quote::quote;
68use syn::{parse_macro_input, Data, DataEnum, DataStruct, DeriveInput, Field, Fields, Variant};
69
70#[proc_macro_derive(Reflect, attributes(reflect))]
101pub fn derive_reflect(input: TokenStream) -> TokenStream {
102 let input = parse_macro_input!(input as DeriveInput);
103 match derive_reflect_impl(&input) {
104 Ok(tokens) => tokens.into(),
105 Err(e) => e.to_compile_error().into(),
106 }
107}
108
109fn derive_reflect_impl(input: &DeriveInput) -> syn::Result<TokenStream2> {
110 let name = &input.ident;
111 let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
112
113 let body = match &input.data {
114 Data::Struct(data) => struct_body(data)?,
115 Data::Enum(data) => enum_body(data)?,
116 Data::Union(_) => {
117 return Err(syn::Error::new_spanned(
118 name,
119 "Reflect cannot be derived for unions",
120 ));
121 }
122 };
123
124 Ok(quote! {
125 impl #impl_generics reify_reflect_core::Reflect for #name #ty_generics #where_clause {
126 type Value = reify_reflect_core::RuntimeValue;
127
128 fn reflect() -> Self::Value {
129 #body
130 }
131 }
132 })
133}
134
135fn struct_body(data: &DataStruct) -> syn::Result<TokenStream2> {
136 match &data.fields {
137 Fields::Named(named) => {
138 let entries = named_field_entries(&named.named)?;
139 Ok(quote! {
140 reify_reflect_core::RuntimeValue::List(vec![#(#entries),*])
141 })
142 }
143 Fields::Unnamed(unnamed) => {
144 let entries = positional_field_entries(&unnamed.unnamed)?;
145 Ok(quote! {
146 reify_reflect_core::RuntimeValue::List(vec![#(#entries),*])
147 })
148 }
149 Fields::Unit => Ok(quote! {
150 reify_reflect_core::RuntimeValue::Unit
151 }),
152 }
153}
154
155fn enum_body(data: &DataEnum) -> syn::Result<TokenStream2> {
156 let mut variant_entries = Vec::with_capacity(data.variants.len());
157 for variant in &data.variants {
158 variant_entries.push(variant_entry(variant)?);
159 }
160 Ok(quote! {
161 reify_reflect_core::RuntimeValue::List(vec![#(#variant_entries),*])
162 })
163}
164
165fn variant_entry(variant: &Variant) -> syn::Result<TokenStream2> {
166 let name_str = variant.ident.to_string();
167 let name_lit = name_bytes_literal(&name_str);
168
169 let payload = match &variant.fields {
170 Fields::Unit => quote! { reify_reflect_core::RuntimeValue::Unit },
171 Fields::Named(named) => {
172 let entries = named_field_entries(&named.named)?;
173 quote! {
174 reify_reflect_core::RuntimeValue::List(vec![#(#entries),*])
175 }
176 }
177 Fields::Unnamed(unnamed) => {
178 let entries = positional_field_entries(&unnamed.unnamed)?;
179 quote! {
180 reify_reflect_core::RuntimeValue::List(vec![#(#entries),*])
181 }
182 }
183 };
184
185 Ok(quote! {
186 reify_reflect_core::RuntimeValue::List(vec![
187 #name_lit,
188 #payload,
189 ])
190 })
191}
192
193fn named_field_entries(
194 fields: &syn::punctuated::Punctuated<Field, syn::Token![,]>,
195) -> syn::Result<Vec<TokenStream2>> {
196 let mut entries = Vec::new();
197 for field in fields {
198 if has_skip_attr(field)? {
199 continue;
200 }
201 let name_str = field
202 .ident
203 .as_ref()
204 .expect("named field must have ident")
205 .to_string();
206 let name_lit = name_bytes_literal(&name_str);
207 let ty = &field.ty;
208 entries.push(quote! {
209 reify_reflect_core::RuntimeValue::List(vec![
210 #name_lit,
211 <#ty as reify_reflect_core::Reflect>::reflect(),
212 ])
213 });
214 }
215 Ok(entries)
216}
217
218fn positional_field_entries(
219 fields: &syn::punctuated::Punctuated<Field, syn::Token![,]>,
220) -> syn::Result<Vec<TokenStream2>> {
221 let mut entries = Vec::new();
222 for field in fields {
223 if has_skip_attr(field)? {
224 continue;
225 }
226 let ty = &field.ty;
227 entries.push(quote! {
228 <#ty as reify_reflect_core::Reflect>::reflect()
229 });
230 }
231 Ok(entries)
232}
233
234fn name_bytes_literal(s: &str) -> TokenStream2 {
235 quote! {
236 reify_reflect_core::RuntimeValue::List(
237 #s.bytes()
238 .map(|b| reify_reflect_core::RuntimeValue::Nat(b as u64))
239 .collect()
240 )
241 }
242}
243
244fn has_skip_attr(field: &Field) -> syn::Result<bool> {
245 for attr in &field.attrs {
246 if attr.path().is_ident("reflect") {
247 let mut skip = false;
248 attr.parse_nested_meta(|meta| {
249 if meta.path.is_ident("skip") {
250 skip = true;
251 Ok(())
252 } else {
253 Err(meta.error("expected `skip`"))
254 }
255 })?;
256 if skip {
257 return Ok(true);
258 }
259 }
260 }
261 Ok(false)
262}