bevy_inspector_egui_derive/
lib.rs

1use proc_macro2::TokenStream;
2use quote::quote;
3use syn::{DataEnum, DataStruct, DataUnion, DeriveInput};
4
5mod attributes;
6
7/// Derive macro used to derive `InspectorOptions`
8#[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}