Skip to main content

bevy_ecs_macros/
lib.rs

1//! Macros for deriving ECS traits.
2
3#![cfg_attr(docsrs, feature(doc_cfg))]
4
5extern crate proc_macro;
6
7mod event;
8mod message;
9mod query_data;
10mod query_filter;
11mod resource;
12mod template;
13mod variant_defaults;
14mod world_query;
15
16use crate::{query_data::derive_query_data_impl, query_filter::derive_query_filter_impl};
17use bevy_ecs_macro_logic::{
18    component::{DeriveComponent, StorageAttribute, StorageTy},
19    map_entities::map_entities,
20};
21use bevy_macro_utils::{
22    derive_label, ensure_no_collision,
23    fq_std::{FQDefault, FQIterator, FQOption, FQResult},
24    get_struct_fields, pascal_to_snake_case, BevyManifest,
25};
26use proc_macro::TokenStream;
27use proc_macro2::{Ident, Span};
28use quote::{format_ident, quote, ToTokens};
29use syn::{
30    parse_macro_input, parse_quote, punctuated::Punctuated, token::Comma, ConstParam, Data,
31    DeriveInput, Fields, GenericParam, TypeParam,
32};
33
34enum BundleFieldKind {
35    Component,
36    Ignore,
37}
38
39const BUNDLE_ATTRIBUTE_NAME: &str = "bundle";
40const BUNDLE_ATTRIBUTE_IGNORE_NAME: &str = "ignore";
41const BUNDLE_ATTRIBUTE_NO_FROM_COMPONENTS: &str = "ignore_from_components";
42
43#[derive(Debug)]
44struct BundleAttributes {
45    impl_from_components: bool,
46}
47
48impl Default for BundleAttributes {
49    fn default() -> Self {
50        Self {
51            impl_from_components: true,
52        }
53    }
54}
55
56/// Implement the `Bundle` trait.
57#[proc_macro_derive(Bundle, attributes(bundle))]
58pub fn derive_bundle(input: TokenStream) -> TokenStream {
59    let ast = parse_macro_input!(input as DeriveInput);
60    let ecs_path = bevy_ecs_path();
61
62    let mut attributes = BundleAttributes::default();
63
64    for attr in &ast.attrs {
65        if attr.path().is_ident(BUNDLE_ATTRIBUTE_NAME) {
66            let parsing = attr.parse_nested_meta(|meta| {
67                if meta.path.is_ident(BUNDLE_ATTRIBUTE_NO_FROM_COMPONENTS) {
68                    attributes.impl_from_components = false;
69                    return Ok(());
70                }
71
72                Err(meta.error(format!("Invalid bundle container attribute. Allowed attributes: `{BUNDLE_ATTRIBUTE_NO_FROM_COMPONENTS}`")))
73            });
74
75            if let Err(e) = parsing {
76                return e.into_compile_error().into();
77            }
78        }
79    }
80
81    let fields = match get_struct_fields(&ast.data, "derive(Bundle)") {
82        Ok(fields) => fields,
83        Err(e) => return e.into_compile_error().into(),
84    };
85
86    let mut field_kinds = Vec::with_capacity(fields.len());
87
88    for field in fields {
89        let mut kind = BundleFieldKind::Component;
90
91        for attr in field
92            .attrs
93            .iter()
94            .filter(|a| a.path().is_ident(BUNDLE_ATTRIBUTE_NAME))
95        {
96            if let Err(error) = attr.parse_nested_meta(|meta| {
97                if meta.path.is_ident(BUNDLE_ATTRIBUTE_IGNORE_NAME) {
98                    kind = BundleFieldKind::Ignore;
99                    Ok(())
100                } else {
101                    Err(meta.error(format!(
102                        "Invalid bundle attribute. Use `{BUNDLE_ATTRIBUTE_IGNORE_NAME}`"
103                    )))
104                }
105            }) {
106                return error.into_compile_error().into();
107            }
108        }
109
110        field_kinds.push(kind);
111    }
112
113    let field_types = fields.iter().map(|field| &field.ty).collect::<Vec<_>>();
114
115    let mut active_field_types = Vec::new();
116    let mut active_field_members = Vec::new();
117    let mut active_field_locals = Vec::new();
118    let mut inactive_field_members = Vec::new();
119    for ((field_member, field_type), field_kind) in
120        fields.members().zip(field_types).zip(field_kinds)
121    {
122        let field_local = format_ident!("field_{}", field_member);
123
124        match field_kind {
125            BundleFieldKind::Component => {
126                active_field_types.push(field_type);
127                active_field_locals.push(field_local);
128                active_field_members.push(field_member);
129            }
130            BundleFieldKind::Ignore => inactive_field_members.push(field_member),
131        }
132    }
133    let generics = ast.generics;
134    let generics_ty_list = generics.type_params().map(|p| p.ident.clone());
135    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
136    let struct_name = &ast.ident;
137
138    let bundle_impl = quote! {
139        // SAFETY:
140        // - ComponentId is returned in field-definition-order. [get_components] uses field-definition-order
141        // - `Bundle::get_components` is exactly once for each member. Rely's on the Component -> Bundle implementation to properly pass
142        //   the correct `StorageType` into the callback.
143        unsafe impl #impl_generics #ecs_path::bundle::Bundle for #struct_name #ty_generics #where_clause {
144            fn component_ids(
145                components: &mut #ecs_path::component::ComponentsRegistrator,
146            ) -> impl #FQIterator<Item = #ecs_path::component::ComponentId> + use<#(#generics_ty_list,)*> {
147                ::core::iter::empty()#(.chain(<#active_field_types as #ecs_path::bundle::Bundle>::component_ids(components)))*
148            }
149
150            fn get_component_ids(
151                components: &#ecs_path::component::Components,
152            ) -> impl #FQIterator<Item = #FQOption<#ecs_path::component::ComponentId>> {
153                ::core::iter::empty()#(.chain(<#active_field_types as #ecs_path::bundle::Bundle>::get_component_ids(components)))*
154            }
155        }
156    };
157
158    let dynamic_bundle_impl = quote! {
159        impl #impl_generics #ecs_path::bundle::DynamicBundle for #struct_name #ty_generics #where_clause {
160            type Effect = ();
161            #[allow(unused_variables)]
162            #[inline]
163            unsafe fn get_components(
164                ptr: #ecs_path::ptr::MovingPtr<'_, Self>,
165                func: &mut impl ::core::ops::FnMut(#ecs_path::component::StorageType, #ecs_path::ptr::OwningPtr<'_>)
166            ) {
167                use #ecs_path::__macro_exports::DebugCheckedUnwrap;
168
169                #ecs_path::ptr::deconstruct_moving_ptr!({
170                    let #struct_name { #(#active_field_members: #active_field_locals,)* #(#inactive_field_members: _,)* } = ptr;
171                });
172                #(
173                    <#active_field_types as #ecs_path::bundle::DynamicBundle>::get_components(
174                        #active_field_locals,
175                        func
176                    );
177                )*
178            }
179
180            #[allow(unused_variables)]
181            #[inline]
182            unsafe fn apply_effect(
183                ptr: #ecs_path::ptr::MovingPtr<'_, ::core::mem::MaybeUninit<Self>>,
184                func: &mut #ecs_path::world::EntityWorldMut<'_>,
185            ) {
186            }
187        }
188    };
189
190    let fqdefault = FQDefault.into_token_stream();
191    let from_components_impl = attributes.impl_from_components.then(|| quote! {
192        // SAFETY:
193        // - ComponentId is returned in field-definition-order. [from_components] uses field-definition-order
194        unsafe impl #impl_generics #ecs_path::bundle::BundleFromComponents for #struct_name #ty_generics #where_clause {
195            #[allow(unused_variables, non_snake_case)]
196            unsafe fn from_components<__T, __F>(ctx: &mut __T, func: &mut __F) -> Self
197            where
198                __F: ::core::ops::FnMut(&mut __T) -> #ecs_path::ptr::OwningPtr<'_>
199            {
200                Self {
201                    #(#active_field_members: <#active_field_types as #ecs_path::bundle::BundleFromComponents>::from_components(ctx, &mut *func),)*
202                    #(#inactive_field_members: #fqdefault::default(),)*
203                }
204            }
205        }
206    });
207    TokenStream::from(quote! {
208        #bundle_impl
209        #from_components_impl
210        #dynamic_bundle_impl
211    })
212}
213
214/// Implement the `MapEntities` trait.
215#[proc_macro_derive(MapEntities, attributes(entities))]
216pub fn derive_map_entities(input: TokenStream) -> TokenStream {
217    let ast = parse_macro_input!(input as DeriveInput);
218    let ecs_path = bevy_ecs_path();
219
220    let map_entities_impl = map_entities(
221        &ast.data,
222        &ecs_path,
223        Ident::new("self", Span::call_site()),
224        false,
225        false,
226        None,
227    );
228
229    let struct_name = &ast.ident;
230    let (impl_generics, type_generics, where_clause) = &ast.generics.split_for_impl();
231    TokenStream::from(quote! {
232        impl #impl_generics #ecs_path::entity::MapEntities for #struct_name #type_generics #where_clause {
233            fn map_entities<M: #ecs_path::entity::EntityMapper>(&mut self, mapper: &mut M) {
234                #map_entities_impl
235            }
236        }
237    })
238}
239
240/// Implement `SystemParam` to use a struct as a parameter in a system
241#[proc_macro_derive(SystemParam, attributes(system_param))]
242pub fn derive_system_param(input: TokenStream) -> TokenStream {
243    let token_stream = input.clone();
244    let ast = parse_macro_input!(input as DeriveInput);
245
246    match derive_system_param_impl(token_stream, ast) {
247        Ok(t) => t,
248        Err(e) => e.into_compile_error().into(),
249    }
250}
251fn derive_system_param_impl(
252    token_stream: TokenStream,
253    ast: DeriveInput,
254) -> syn::Result<TokenStream> {
255    let fields = get_struct_fields(&ast.data, "derive(SystemParam)")?;
256    let path = bevy_ecs_path();
257
258    let field_locals = fields
259        .members()
260        .map(|m| format_ident!("field{}", m))
261        .collect::<Vec<_>>();
262    let field_members = fields.members().collect::<Vec<_>>();
263    let field_types = fields.iter().map(|f| &f.ty).collect::<Vec<_>>();
264
265    let field_validation_names = fields.members().map(|m| format!("::{}", quote! { #m }));
266    let mut field_validation_messages = Vec::with_capacity(fields.len());
267    for attr in fields
268        .iter()
269        .map(|f| f.attrs.iter().find(|a| a.path().is_ident("system_param")))
270    {
271        let mut field_validation_message = None;
272        if let Some(attr) = attr {
273            attr.parse_nested_meta(|nested| {
274                if nested.path.is_ident("validation_message") {
275                    field_validation_message = Some(nested.value()?.parse()?);
276                    Ok(())
277                } else {
278                    Err(nested.error("Unsupported attribute"))
279                }
280            })?;
281        }
282        field_validation_messages
283            .push(field_validation_message.unwrap_or_else(|| quote! { err.message }));
284    }
285
286    let generics = ast.generics;
287
288    // Emit an error if there's any unrecognized lifetime names.
289    let w = format_ident!("w");
290    let s = format_ident!("s");
291    for lt in generics.lifetimes() {
292        let ident = &lt.lifetime.ident;
293        if ident != &w && ident != &s {
294            return Err(syn::Error::new_spanned(
295                lt,
296                r#"invalid lifetime name: expected `'w` or `'s`
297 'w -- refers to data stored in the World.
298 's -- refers to data stored in the SystemParam's state.'"#,
299            ));
300        }
301    }
302
303    let (_impl_generics, ty_generics, where_clause) = generics.split_for_impl();
304
305    let lifetimeless_generics: Vec<_> = generics
306        .params
307        .iter()
308        .filter(|g| !matches!(g, GenericParam::Lifetime(_)))
309        .collect();
310
311    let shadowed_lifetimes: Vec<_> = generics.lifetimes().map(|_| quote!('_)).collect();
312
313    let mut punctuated_generics = Punctuated::<_, Comma>::new();
314    punctuated_generics.extend(lifetimeless_generics.iter().map(|g| match g {
315        GenericParam::Type(g) => GenericParam::Type(TypeParam {
316            default: None,
317            ..g.clone()
318        }),
319        GenericParam::Const(g) => GenericParam::Const(ConstParam {
320            default: None,
321            ..g.clone()
322        }),
323        _ => unreachable!(),
324    }));
325
326    let mut punctuated_generic_idents = Punctuated::<_, Comma>::new();
327    punctuated_generic_idents.extend(lifetimeless_generics.iter().map(|g| match g {
328        GenericParam::Type(g) => &g.ident,
329        GenericParam::Const(g) => &g.ident,
330        _ => unreachable!(),
331    }));
332
333    let punctuated_generics_no_bounds: Punctuated<_, Comma> = lifetimeless_generics
334        .iter()
335        .map(|&g| match g.clone() {
336            GenericParam::Type(mut g) => {
337                g.bounds.clear();
338                GenericParam::Type(g)
339            }
340            g => g,
341        })
342        .collect();
343
344    let mut tuple_types: Vec<_> = field_types.iter().map(ToTokens::to_token_stream).collect();
345    let mut tuple_patterns: Vec<_> = field_locals.iter().map(ToTokens::to_token_stream).collect();
346
347    // If the number of fields exceeds the 16-parameter limit,
348    // fold the fields into tuples of tuples until we are below the limit.
349    const LIMIT: usize = 16;
350    while tuple_types.len() > LIMIT {
351        let end = Vec::from_iter(tuple_types.drain(..LIMIT));
352        tuple_types.push(parse_quote!( (#(#end,)*) ));
353
354        let end = Vec::from_iter(tuple_patterns.drain(..LIMIT));
355        tuple_patterns.push(parse_quote!( (#(#end,)*) ));
356    }
357    // Create a where clause for the `ReadOnlySystemParam` impl.
358    // Ensure that each field implements `ReadOnlySystemParam`.
359    let mut read_only_generics = generics.clone();
360    let read_only_where_clause = read_only_generics.make_where_clause();
361    for field_type in &field_types {
362        read_only_where_clause
363            .predicates
364            .push(syn::parse_quote!(#field_type: #path::system::ReadOnlySystemParam));
365    }
366
367    let fields_alias =
368        ensure_no_collision(format_ident!("__StructFieldsAlias"), token_stream.clone());
369
370    let struct_name = &ast.ident;
371    let state_struct_visibility = &ast.vis;
372    let state_struct_name = ensure_no_collision(format_ident!("FetchState"), token_stream);
373
374    let mut builder_name = None;
375    for meta in ast
376        .attrs
377        .iter()
378        .filter(|a| a.path().is_ident("system_param"))
379    {
380        meta.parse_nested_meta(|nested| {
381            if nested.path.is_ident("builder") {
382                builder_name = Some(format_ident!("{struct_name}Builder"));
383                Ok(())
384            } else {
385                Err(nested.error("Unsupported attribute"))
386            }
387        })?;
388    }
389
390    let builder = builder_name.map(|builder_name| {
391        let builder_type_parameters: Vec<Ident> = field_members.iter().map(|m| format_ident!("B{}", m)).collect();
392        let builder_doc_comment = format!("A [`SystemParamBuilder`] for a [`{struct_name}`].");
393        let builder_struct = quote! {
394            #[doc = #builder_doc_comment]
395            struct #builder_name<#(#[allow(non_camel_case_types, reason = "generated from snake-case field name")] #builder_type_parameters,)*> {
396                #(#field_members: #builder_type_parameters,)*
397            }
398        };
399        let lifetimes: Vec<_> = generics.lifetimes().collect();
400        let generic_struct = quote!{ #struct_name <#(#lifetimes,)* #punctuated_generic_idents> };
401        let builder_impl = quote!{
402            // SAFETY: This delegates to the `SystemParamBuilder` for tuples.
403            unsafe impl<
404                #(#lifetimes,)*
405                #(#[allow(non_camel_case_types, reason = "generated from snake-case field name")] #builder_type_parameters: #path::system::SystemParamBuilder<#field_types>,)*
406                #punctuated_generics
407            > #path::system::SystemParamBuilder<#generic_struct> for #builder_name<#(#builder_type_parameters,)*>
408                #where_clause
409            {
410                fn build(self, world: &mut #path::world::World) -> <#generic_struct as #path::system::SystemParam>::State {
411                    let #builder_name { #(#field_members: #field_locals,)* } = self;
412                    #state_struct_name {
413                        state: #path::system::SystemParamBuilder::build((#(#tuple_patterns,)*), world)
414                    }
415                }
416            }
417        };
418        (builder_struct, builder_impl)
419    });
420    let (builder_struct, builder_impl) = builder.unzip();
421
422    Ok(TokenStream::from(quote! {
423        // We define the FetchState struct in an anonymous scope to avoid polluting the user namespace.
424        // The struct can still be accessed via SystemParam::State, e.g. MessageReaderState can be accessed via
425        // <MessageReader<'static, 'static, T> as SystemParam>::State
426        const _: () = {
427            // Allows rebinding the lifetimes of each field type.
428            type #fields_alias <'w, 's, #punctuated_generics_no_bounds> = (#(#tuple_types,)*);
429
430            #[doc(hidden)]
431            #state_struct_visibility struct #state_struct_name <#(#lifetimeless_generics,)*>
432            #where_clause {
433                state: <#fields_alias::<'static, 'static, #punctuated_generic_idents> as #path::system::SystemParam>::State,
434            }
435
436            unsafe impl<#punctuated_generics> #path::system::SystemParam for
437                #struct_name <#(#shadowed_lifetimes,)* #punctuated_generic_idents> #where_clause
438            {
439                type State = #state_struct_name<#punctuated_generic_idents>;
440                type Item<'w, 's> = #struct_name #ty_generics;
441
442                fn init_state(world: &mut #path::world::World) -> Self::State {
443                    #state_struct_name {
444                        state: <#fields_alias::<'_, '_, #punctuated_generic_idents> as #path::system::SystemParam>::init_state(world),
445                    }
446                }
447
448                fn init_access(state: &Self::State, system_meta: &mut #path::system::SystemMeta, component_access_set: &mut #path::query::FilteredAccessSet, world: &mut #path::world::World) {
449                    <#fields_alias::<'_, '_, #punctuated_generic_idents> as #path::system::SystemParam>::init_access(&state.state, system_meta, component_access_set, world);
450                }
451
452                fn apply(state: &mut Self::State, system_meta: &#path::system::SystemMeta, world: &mut #path::world::World) {
453                    <#fields_alias::<'_, '_, #punctuated_generic_idents> as #path::system::SystemParam>::apply(&mut state.state, system_meta, world);
454                }
455
456                fn queue(state: &mut Self::State, system_meta: &#path::system::SystemMeta, world: #path::world::DeferredWorld) {
457                    <#fields_alias::<'_, '_, #punctuated_generic_idents> as #path::system::SystemParam>::queue(&mut state.state, system_meta, world);
458                }
459
460                #[inline]
461                unsafe fn get_param<'w, 's>(
462                    state: &'s mut Self::State,
463                    system_meta: &#path::system::SystemMeta,
464                    world: #path::world::unsafe_world_cell::UnsafeWorldCell<'w>,
465                    change_tick: #path::change_detection::Tick,
466                ) -> #FQResult<Self::Item<'w, 's>, #path::system::SystemParamValidationError> {
467                    let (#(#tuple_patterns,)*) = &mut state.state;
468                    #(
469                        let #field_locals = unsafe {
470                            <#field_types as #path::system::SystemParam>::get_param(#field_locals, system_meta, world, change_tick)
471                        }.map_err(|err| #path::system::SystemParamValidationError::new::<Self>(err.skipped, #field_validation_messages, #field_validation_names))?;
472                    )*
473                    #FQResult::Ok(#struct_name {
474                        #(#field_members: #field_locals,)*
475                    })
476                }
477            }
478
479            // Safety: Each field is `ReadOnlySystemParam`, so this can only read from the `World`
480            unsafe impl<'w, 's, #punctuated_generics> #path::system::ReadOnlySystemParam for #struct_name #ty_generics #read_only_where_clause {}
481
482            #builder_impl
483        };
484
485        #builder_struct
486    }))
487}
488
489/// Implement `QueryData` to use a struct as a data parameter in a query
490#[proc_macro_derive(QueryData, attributes(query_data))]
491pub fn derive_query_data(input: TokenStream) -> TokenStream {
492    derive_query_data_impl(input)
493}
494
495/// Implement `QueryFilter` to use a struct as a filter parameter in a query
496#[proc_macro_derive(QueryFilter, attributes(query_filter))]
497pub fn derive_query_filter(input: TokenStream) -> TokenStream {
498    derive_query_filter_impl(input)
499}
500
501/// Derive macro generating an impl of the trait `ScheduleLabel`.
502///
503/// This does not work for unions.
504#[proc_macro_derive(ScheduleLabel)]
505pub fn derive_schedule_label(input: TokenStream) -> TokenStream {
506    let input = parse_macro_input!(input as DeriveInput);
507    let mut trait_path = bevy_ecs_path();
508    trait_path.segments.push(format_ident!("schedule").into());
509    trait_path
510        .segments
511        .push(format_ident!("ScheduleLabel").into());
512    derive_label(input, "ScheduleLabel", &trait_path)
513}
514
515/// Derive macro generating an impl of the trait `SystemSet`.
516///
517/// This does not work for unions.
518#[proc_macro_derive(SystemSet)]
519pub fn derive_system_set(input: TokenStream) -> TokenStream {
520    let input = parse_macro_input!(input as DeriveInput);
521    let mut trait_path = bevy_ecs_path();
522    trait_path.segments.push(format_ident!("schedule").into());
523    trait_path.segments.push(format_ident!("SystemSet").into());
524    derive_label(input, "SystemSet", &trait_path)
525}
526
527pub(crate) fn bevy_ecs_path() -> syn::Path {
528    BevyManifest::shared(|manifest| manifest.get_path("bevy_ecs"))
529}
530
531pub(crate) fn bevy_settings_path() -> syn::Path {
532    BevyManifest::shared(|manifest| manifest.get_path("bevy-settings"))
533}
534
535/// Implement the `Event` trait.
536#[proc_macro_derive(Event, attributes(event))]
537pub fn derive_event(input: TokenStream) -> TokenStream {
538    event::derive_event(input)
539}
540
541/// Cheat sheet for derive syntax,
542/// see full explanation on `EntityEvent` trait docs.
543///
544/// ```ignore
545/// #[derive(EntityEvent)]
546/// /// Enable propagation, which defaults to using the ChildOf component
547/// #[entity_event(propagate)]
548/// /// Enable propagation using the given Traversal implementation
549/// #[entity_event(propagate = &'static ChildOf)]
550/// /// Always propagate
551/// #[entity_event(auto_propagate)]
552/// struct MyEvent;
553/// ```
554#[proc_macro_derive(EntityEvent, attributes(entity_event, event_target))]
555pub fn derive_entity_event(input: TokenStream) -> TokenStream {
556    event::derive_entity_event(input)
557}
558
559/// Implement the `Message` trait.
560#[proc_macro_derive(Message)]
561pub fn derive_message(input: TokenStream) -> TokenStream {
562    message::derive_message(input)
563}
564
565/// Implement the `Resource` trait.
566///
567/// ## Immutability
568/// ```ignore
569/// #[derive(Resource)]
570/// #[component(immutable)]
571/// struct MyResource;
572/// ```
573///
574/// ## Hooks
575/// ```ignore
576/// #[derive(Resource)]
577/// #[component(hook_name = function)]
578/// struct MyResource;
579/// ```
580/// where `hook_name` is `on_add`, `on_insert`, `on_discard` or `on_remove`;
581/// `function` can be either a path, e.g. `some_function::<Self>`,
582/// or a function call that returns a function that can be turned into
583/// a `ComponentHook`, e.g. `get_closure("Hi!")`.
584/// `function` can be elided if the path is `Self::on_add`, `Self::on_insert` etc.
585#[proc_macro_derive(Resource, attributes(component, require))]
586pub fn derive_resource(input: TokenStream) -> TokenStream {
587    let mut ast = parse_macro_input!(input as DeriveInput);
588    TokenStream::from(resource::derive_resource(&mut ast))
589}
590
591/// Cheat sheet for derive syntax,
592///
593/// ## Group Override
594/// ```ignore
595/// #[derive(SettingsGroup)]
596/// #[settings_group(group = "my_group")]
597/// struct MySettings {
598///     test: true
599/// }
600/// ```
601/// results in:
602/// ```ignore
603/// [my_group]
604/// test = true
605/// ```
606///
607/// ## File Override
608/// ```ignore
609/// #[derive(SettingsGroup)]
610/// #[settings_group(file = "my_file")]
611/// struct MySettings {
612///     test: true
613/// }
614/// ```
615/// results in a different file being used as the source of the settings.
616///
617/// ## Key Override
618/// Only valid for enums, as struct keys are always derived from the field name.
619/// ```ignore
620/// #[derive(SettingsGroup)]
621/// #[settings_group(key = "my_key")]
622/// enum MySettingsEnum {
623///     Variant1,
624///     Variant2
625/// };
626/// ```
627/// results in:
628/// ```ignore
629/// [my_settings_enum]
630/// my_key = "variant1"
631/// ```
632#[proc_macro_derive(SettingsGroup, attributes(settings_group))]
633pub fn derive_settings_group(input: TokenStream) -> TokenStream {
634    let input = parse_macro_input!(input as DeriveInput);
635
636    let name = &input.ident;
637
638    let path = bevy_settings_path();
639
640    let (override_group_name, override_key_name, override_file) = {
641        let mut override_group_name: Option<String> = None;
642        let mut override_key_name: Option<String> = None;
643        let mut override_file: Option<String> = None;
644
645        input
646            .attrs
647            .iter()
648            .find(|attr| attr.path().is_ident("settings_group"))
649            .and_then(|attr| {
650                attr.parse_nested_meta(|meta| {
651                    if meta.path.is_ident("group") {
652                        let value = meta.value()?;
653                        let s: syn::LitStr = value.parse()?;
654                        override_group_name = Some(s.value());
655                        Ok(())
656                    } else if meta.path.is_ident("key") {
657                        let value = meta.value()?;
658                        let s: syn::LitStr = value.parse()?;
659                        override_key_name = Some(s.value());
660                        Ok(())
661                    } else if meta.path.is_ident("file") {
662                        let value = meta.value()?;
663                        let s: syn::LitStr = value.parse()?;
664                        override_file = Some(s.value());
665                        Ok(())
666                    } else {
667                        Err(meta.error("unsupported attribute"))
668                    }
669                })
670                .ok()
671            });
672
673        (override_group_name, override_key_name, override_file)
674    };
675
676    let key_name = match &input.data {
677        Data::Struct(data) => match data.fields {
678            Fields::Named(_) if override_key_name.is_some() => {
679                return syn::Error::new(
680                    Span::call_site(),
681                    "The `key` attribute is not supported for structs with named fields",
682                )
683                .into_compile_error()
684                .into();
685            }
686            Fields::Named(_) => None,
687            Fields::Unnamed(_) | Fields::Unit => {
688                override_key_name.or_else(|| Some(pascal_to_snake_case(&name.to_string())))
689            }
690        },
691        Data::Enum(_) => override_key_name.or(Some(pascal_to_snake_case(&name.to_string()))),
692        Data::Union(_) => {
693            return syn::Error::new(
694                Span::call_site(),
695                "SettingsGroup cannot be derived for unions",
696            )
697            .into_compile_error()
698            .into();
699        }
700    };
701
702    let group_name = override_group_name.unwrap_or(pascal_to_snake_case(&name.to_string()));
703    let key_name = key_name
704        .map(|f| quote! { #FQOption::Some(#f) })
705        .unwrap_or(quote! { #FQOption::None });
706    let file_name = override_file
707        .map(|f| quote! { #FQOption::Some(#f) })
708        .unwrap_or(quote! { #FQOption::None });
709
710    let expanded = quote! {
711        impl #path::SettingsGroup for #name {
712            fn settings_group_name() -> &'static str {
713                #group_name
714            }
715
716            fn settings_key_name() -> #FQOption<&'static str> {
717                #key_name
718            }
719
720            fn settings_source() -> #FQOption<&'static str> {
721                #file_name
722            }
723        }
724    };
725
726    TokenStream::from(expanded)
727}
728
729/// Cheat sheet for derive syntax,
730/// see full explanation and examples on the `Component` trait doc.
731///
732/// ## Immutability
733/// ```ignore
734/// #[derive(Component)]
735/// #[component(immutable)]
736/// struct MyComponent;
737/// ```
738///
739/// ## Sparse instead of table-based storage
740/// ```ignore
741/// #[derive(Component)]
742/// #[component(storage = "SparseSet")]
743/// struct MyComponent;
744/// ```
745///
746/// ## Required Components
747///
748/// ```ignore
749/// #[derive(Component)]
750/// #[require(
751///     // `Default::default()`
752///     A,
753///     // tuple structs
754///     B(1),
755///     // named-field structs
756///     C {
757///         x: 1,
758///         ..default()
759///     },
760///     // unit structs/variants
761///     D::One,
762///     // associated consts
763///     E::ONE,
764///     // constructors
765///     F::new(1),
766///     // arbitrary expressions
767///     G = make(1, 2, 3)
768/// )]
769/// struct MyComponent;
770/// ```
771///
772/// ## Relationships
773/// ```ignore
774/// #[derive(Component)]
775/// #[relationship(relationship_target = Children)]
776/// pub struct ChildOf {
777///     // Marking the field is not necessary if there is only one.
778///     #[relationship]
779///     pub parent: Entity,
780///     internal: u8,
781/// };
782///
783/// #[derive(Component)]
784/// #[relationship_target(relationship = ChildOf)]
785/// pub struct Children(Vec<Entity>);
786/// ```
787///
788/// On despawn, also despawn all related entities:
789/// ```ignore
790/// #[derive(Component)]
791/// #[relationship_target(relationship = ChildOf, linked_spawn)]
792/// pub struct Children(Vec<Entity>);
793/// ```
794///
795/// Allow relationships to point to their own entity:
796/// ```ignore
797/// #[derive(Component)]
798/// #[relationship(relationship_target = PeopleILike, allow_self_referential)]
799/// pub struct LikedBy(pub Entity);
800/// ```
801/// ## Warning
802///
803/// When `allow_self_referential` is enabled, be careful when using recursive traversal methods
804/// like `iter_ancestors` or `root_ancestor`, as they will loop infinitely if an entity points to itself.
805///
806/// ## Hooks
807/// ```ignore
808/// #[derive(Component)]
809/// #[component(hook_name = function)]
810/// struct MyComponent;
811/// ```
812/// where `hook_name` is `on_add`, `on_insert`, `on_discard` or `on_remove`;
813/// `function` can be either a path, e.g. `some_function::<Self>`,
814/// or a function call that returns a function that can be turned into
815/// a `ComponentHook`, e.g. `get_closure("Hi!")`.
816/// `function` can be elided if the path is `Self::on_add`, `Self::on_insert` etc.
817///
818/// ## Ignore this component when cloning an entity
819/// ```ignore
820/// #[derive(Component)]
821/// #[component(clone_behavior = Ignore)]
822/// struct MyComponent;
823/// ```
824#[proc_macro_derive(
825    Component,
826    attributes(component, require, relationship, relationship_target, entities)
827)]
828pub fn derive_component(input: TokenStream) -> TokenStream {
829    let mut ast = parse_macro_input!(input as DeriveInput);
830    let derive_component = match DeriveComponent::parse(&ast, StorageAttribute::Allowed) {
831        Ok(value) => value,
832        Err(e) => return e.into_compile_error().into(),
833    };
834    let bevy_ecs = bevy_ecs_path();
835    let impl_component =
836        match derive_component.impl_component(&mut ast, &bevy_ecs, StorageTy::Table) {
837            Ok(value) => value,
838            Err(err) => return err.into_compile_error().into(),
839        };
840    TokenStream::from(impl_component)
841}
842
843/// Implement the `FromWorld` trait.
844#[proc_macro_derive(FromWorld, attributes(from_world))]
845pub fn derive_from_world(input: TokenStream) -> TokenStream {
846    let bevy_ecs_path = bevy_ecs_path();
847    let ast = parse_macro_input!(input as DeriveInput);
848    let name = ast.ident;
849    let (impl_generics, ty_generics, where_clauses) = ast.generics.split_for_impl();
850
851    let (fields, variant_ident) = match &ast.data {
852        Data::Struct(data) => (&data.fields, None),
853        Data::Enum(data) => {
854            match data.variants.iter().find(|variant| {
855                variant
856                    .attrs
857                    .iter()
858                    .any(|attr| attr.path().is_ident("from_world"))
859            }) {
860                Some(variant) => (&variant.fields, Some(&variant.ident)),
861                None => {
862                    return syn::Error::new(
863                        Span::call_site(),
864                        "No variant found with the `#[from_world]` attribute",
865                    )
866                    .into_compile_error()
867                    .into();
868                }
869            }
870        }
871        Data::Union(_) => {
872            return syn::Error::new(
873                Span::call_site(),
874                "#[derive(FromWorld)]` does not support unions",
875            )
876            .into_compile_error()
877            .into();
878        }
879    };
880
881    let field_init_expr = quote!(#bevy_ecs_path::world::FromWorld::from_world(world));
882    let members = fields.members();
883
884    let field_initializers = match variant_ident {
885        Some(variant_ident) => quote!( Self::#variant_ident {
886            #(#members: #field_init_expr),*
887        }),
888        None => quote!( Self {
889            #(#members: #field_init_expr),*
890        }),
891    };
892
893    TokenStream::from(quote! {
894            impl #impl_generics #bevy_ecs_path::world::FromWorld for #name #ty_generics #where_clauses {
895                fn from_world(world: &mut #bevy_ecs_path::world::World) -> Self {
896                    #field_initializers
897                }
898            }
899    })
900}
901
902/// Derives `FromTemplate`.
903#[proc_macro_derive(FromTemplate, attributes(template, default))]
904pub fn derive_from_template(input: TokenStream) -> TokenStream {
905    template::derive_from_template(input)
906}
907
908/// Derives `VariantDefaults`.
909#[proc_macro_derive(VariantDefaults)]
910pub fn derive_variant_defaults(input: TokenStream) -> TokenStream {
911    variant_defaults::derive_variant_defaults(input)
912}