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