cu29_reflect_derive/
lib.rs1use proc_macro::TokenStream;
2use quote::{ToTokens, quote};
3use syn::{Attribute, DeriveInput, parse_macro_input};
4
5#[proc_macro_derive(Reflect, attributes(reflect))]
11pub fn derive_reflect(input: TokenStream) -> TokenStream {
12 let input = parse_macro_input!(input as DeriveInput);
13 derive_reflect_impl(&input).into()
14}
15
16fn derive_reflect_impl(input: &DeriveInput) -> proc_macro2::TokenStream {
17 if reflect_disables_type_path(&input.attrs) {
18 return proc_macro2::TokenStream::new();
19 }
20
21 let ident = &input.ident;
22 let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
23
24 quote! {
25 impl #impl_generics cu29::reflect::TypePath for #ident #ty_generics #where_clause {}
26 }
27}
28
29fn reflect_disables_type_path(attrs: &[Attribute]) -> bool {
30 attrs
31 .iter()
32 .filter(|attr| attr.path().is_ident("reflect"))
33 .any(|attr| {
34 attr.meta
35 .to_token_stream()
36 .to_string()
37 .chars()
38 .filter(|ch| !ch.is_whitespace())
39 .collect::<String>()
40 .contains("type_path=false")
41 })
42}
43
44#[cfg(test)]
45mod tests {
46 use super::{derive_reflect_impl, reflect_disables_type_path};
47 use syn::parse_quote;
48
49 #[test]
50 fn emits_type_path_impl_by_default() {
51 let input = parse_quote! {
52 struct Payload;
53 };
54
55 let output = derive_reflect_impl(&input).to_string();
56
57 assert!(output.contains("impl cu29 :: reflect :: TypePath for Payload"));
58 }
59
60 #[test]
61 fn respects_type_path_false() {
62 let input = parse_quote! {
63 #[reflect(from_reflect = false, type_path = false)]
64 struct Payload<T>(T);
65 };
66
67 assert!(derive_reflect_impl(&input).is_empty());
68 assert!(reflect_disables_type_path(&input.attrs));
69 }
70}