bevy_inspector_egui_derive/
lib.rs1use proc_macro2::TokenStream;
2use quote::quote;
3use syn::{DataEnum, DataStruct, DataUnion, DeriveInput};
4
5mod attributes;
6
7#[proc_macro_derive(InspectorOptions, attributes(inspector))]
9pub fn inspectable(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
10 let input = syn::parse_macro_input!(input as syn::DeriveInput);
11
12 let result = match &input.data {
13 syn::Data::Struct(data) => expand_struct(&input, data),
14 syn::Data::Enum(data) => expand_enum(&input, data),
15 syn::Data::Union(data) => expand_union(&input, data),
16 };
17
18 result.unwrap_or_else(|err| err.into_compile_error()).into()
19}
20
21fn expand_struct(input: &DeriveInput, data: &DataStruct) -> syn::Result<TokenStream> {
22 let bevy_reflect = quote! { ::bevy_inspector_egui::__macro_exports::bevy_reflect };
23
24 let fields = data
25 .fields
26 .iter()
27 .filter(|field| !attributes::is_reflect_ignore_field(field))
28 .enumerate()
29 .filter_map(|(i, field)| {
30 let ty = &field.ty;
31 let attrs = match attributes::extract_inspector_attributes(&field.attrs) {
32 Ok(attrs) => attrs,
33 Err(e) => return Some(Err(e)),
34 };
35 if attrs.is_empty() {
36 return None;
37 }
38 let attrs = attrs.into_iter().map(|attribute| {
39 let name = attribute.lhs();
40 let value = attribute.rhs();
41 quote! {
42 field_options.#name = ::std::convert::Into::into(#value);
43 }
44 });
45
46 Some(Ok(quote! {
47 let mut field_options = <#ty as ::bevy_inspector_egui::inspector_options::InspectorOptionsType>::DeriveOptions::default();
48 #(#attrs)*
49 options.insert(::bevy_inspector_egui::inspector_options::Target::Field(#i), <#ty as ::bevy_inspector_egui::inspector_options::InspectorOptionsType>::options_from_derive(field_options));
50 }))
51 })
52 .collect::<syn::Result<Vec<_>>>()?;
53
54 let type_name = &input.ident;
55 let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
56
57 Ok(quote! {
58 impl #impl_generics #bevy_reflect::FromType<#type_name #ty_generics> for ::bevy_inspector_egui::InspectorOptions
59 #where_clause
60 {
61 fn from_type() -> Self {
62 let mut options = ::bevy_inspector_egui::InspectorOptions::default();
63
64 #(#fields)*
65
66 options
67 }
68 }
69 })
70}
71
72fn expand_enum(input: &DeriveInput, data: &DataEnum) -> syn::Result<TokenStream> {
73 let bevy_reflect = quote! { ::bevy_inspector_egui::__macro_exports::bevy_reflect };
74
75 let fields = data
76 .variants
77 .iter()
78 .enumerate()
79 .map(|(variant_index, variant)| {
80 let attrs = variant
81 .fields
82 .iter()
83 .filter(|field| !attributes::is_reflect_ignore_field(field))
84 .enumerate()
85 .filter_map(|(field_index, field)| {
86 let ty = &field.ty;
87 let attrs = match attributes::extract_inspector_attributes(&field.attrs) {
88 Ok(attrs) => attrs,
89 Err(e) => return Some(Err(e)),
90 };
91 if attrs.is_empty() {
92 return None;
93 }
94 let attrs = attrs.into_iter().map(|attribute| {
95 let name = attribute.lhs();
96 let value = attribute.rhs();
97 quote! {
98 field_options.#name = std::convert::Into::into(#value);
99 }
100 });
101
102 Some(Ok(quote! {
103 let mut field_options = <#ty as ::bevy_inspector_egui::inspector_options::InspectorOptionsType>::DeriveOptions::default();
104 #(#attrs)*
105 options.insert(
106 ::bevy_inspector_egui::inspector_options::Target::VariantField {
107 variant_index: #variant_index,
108 field_index: #field_index,
109 },
110 <#ty as ::bevy_inspector_egui::inspector_options::InspectorOptionsType>::options_from_derive(field_options)
111 );
112 }))
113 })
114 .collect::<syn::Result<Vec<_>>>()?;
115 Ok(attrs)
116 })
117 .collect::<syn::Result<Vec<_>>>()?;
118
119 let type_name = &input.ident;
120 let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
121
122 Ok(quote! {
123 impl #impl_generics #bevy_reflect::FromType<#type_name #ty_generics> for ::bevy_inspector_egui::InspectorOptions
124 #where_clause
125 {
126 fn from_type() -> Self {
127 let mut options = ::bevy_inspector_egui::InspectorOptions::default();
128
129 #(#(#fields)*)*
130
131 options
132 }
133 }
134 })
135}
136fn expand_union(_: &DeriveInput, data: &DataUnion) -> syn::Result<TokenStream> {
137 Err(syn::Error::new_spanned(
138 data.union_token,
139 "`InspectorOptions` for unions is not implemented",
140 ))
141}