1#![deny(unsafe_code)]
25
26extern crate proc_macro;
27
28use proc_macro::TokenStream;
29use proc_macro2::TokenStream as TokenStream2;
30use quote::quote;
31use syn::{parse_macro_input, Data, DataEnum, DataStruct, DeriveInput, Field, Fields, Variant};
32
33#[proc_macro_derive(Reflect, attributes(reflect))]
64pub fn derive_reflect(input: TokenStream) -> TokenStream {
65 let input = parse_macro_input!(input as DeriveInput);
66 match derive_reflect_impl(&input) {
67 Ok(tokens) => tokens.into(),
68 Err(e) => e.to_compile_error().into(),
69 }
70}
71
72fn derive_reflect_impl(input: &DeriveInput) -> syn::Result<TokenStream2> {
73 let name = &input.ident;
74 let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
75
76 let body = match &input.data {
77 Data::Struct(data) => struct_body(data)?,
78 Data::Enum(data) => enum_body(data)?,
79 Data::Union(_) => {
80 return Err(syn::Error::new_spanned(
81 name,
82 "Reflect cannot be derived for unions",
83 ));
84 }
85 };
86
87 Ok(quote! {
88 impl #impl_generics reify_reflect_core::Reflect for #name #ty_generics #where_clause {
89 type Value = reify_reflect_core::RuntimeValue;
90
91 fn reflect() -> Self::Value {
92 #body
93 }
94 }
95 })
96}
97
98fn struct_body(data: &DataStruct) -> syn::Result<TokenStream2> {
99 match &data.fields {
100 Fields::Named(named) => {
101 let entries = named_field_entries(&named.named)?;
102 Ok(quote! {
103 reify_reflect_core::RuntimeValue::List(vec![#(#entries),*])
104 })
105 }
106 Fields::Unnamed(unnamed) => {
107 let entries = positional_field_entries(&unnamed.unnamed)?;
108 Ok(quote! {
109 reify_reflect_core::RuntimeValue::List(vec![#(#entries),*])
110 })
111 }
112 Fields::Unit => Ok(quote! {
113 reify_reflect_core::RuntimeValue::Unit
114 }),
115 }
116}
117
118fn enum_body(data: &DataEnum) -> syn::Result<TokenStream2> {
119 let mut variant_entries = Vec::with_capacity(data.variants.len());
120 for variant in &data.variants {
121 variant_entries.push(variant_entry(variant)?);
122 }
123 Ok(quote! {
124 reify_reflect_core::RuntimeValue::List(vec![#(#variant_entries),*])
125 })
126}
127
128fn variant_entry(variant: &Variant) -> syn::Result<TokenStream2> {
129 let name_str = variant.ident.to_string();
130 let name_lit = name_bytes_literal(&name_str);
131
132 let payload = match &variant.fields {
133 Fields::Unit => quote! { reify_reflect_core::RuntimeValue::Unit },
134 Fields::Named(named) => {
135 let entries = named_field_entries(&named.named)?;
136 quote! {
137 reify_reflect_core::RuntimeValue::List(vec![#(#entries),*])
138 }
139 }
140 Fields::Unnamed(unnamed) => {
141 let entries = positional_field_entries(&unnamed.unnamed)?;
142 quote! {
143 reify_reflect_core::RuntimeValue::List(vec![#(#entries),*])
144 }
145 }
146 };
147
148 Ok(quote! {
149 reify_reflect_core::RuntimeValue::List(vec![
150 #name_lit,
151 #payload,
152 ])
153 })
154}
155
156fn named_field_entries(
157 fields: &syn::punctuated::Punctuated<Field, syn::Token![,]>,
158) -> syn::Result<Vec<TokenStream2>> {
159 let mut entries = Vec::new();
160 for field in fields {
161 if has_skip_attr(field)? {
162 continue;
163 }
164 let name_str = field
165 .ident
166 .as_ref()
167 .expect("named field must have ident")
168 .to_string();
169 let name_lit = name_bytes_literal(&name_str);
170 let ty = &field.ty;
171 entries.push(quote! {
172 reify_reflect_core::RuntimeValue::List(vec![
173 #name_lit,
174 <#ty as reify_reflect_core::Reflect>::reflect(),
175 ])
176 });
177 }
178 Ok(entries)
179}
180
181fn positional_field_entries(
182 fields: &syn::punctuated::Punctuated<Field, syn::Token![,]>,
183) -> syn::Result<Vec<TokenStream2>> {
184 let mut entries = Vec::new();
185 for field in fields {
186 if has_skip_attr(field)? {
187 continue;
188 }
189 let ty = &field.ty;
190 entries.push(quote! {
191 <#ty as reify_reflect_core::Reflect>::reflect()
192 });
193 }
194 Ok(entries)
195}
196
197fn name_bytes_literal(s: &str) -> TokenStream2 {
198 quote! {
199 reify_reflect_core::RuntimeValue::List(
200 #s.bytes()
201 .map(|b| reify_reflect_core::RuntimeValue::Nat(b as u64))
202 .collect()
203 )
204 }
205}
206
207fn has_skip_attr(field: &Field) -> syn::Result<bool> {
208 for attr in &field.attrs {
209 if attr.path().is_ident("reflect") {
210 let mut skip = false;
211 attr.parse_nested_meta(|meta| {
212 if meta.path.is_ident("skip") {
213 skip = true;
214 Ok(())
215 } else {
216 Err(meta.error("expected `skip`"))
217 }
218 })?;
219 if skip {
220 return Ok(true);
221 }
222 }
223 }
224 Ok(false)
225}