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 component;
8mod event;
9mod message;
10mod query_data;
11mod query_filter;
12mod world_query;
13
14use crate::{
15    component::map_entities, query_data::derive_query_data_impl,
16    query_filter::derive_query_filter_impl,
17};
18use bevy_macro_utils::{derive_label, ensure_no_collision, get_struct_fields, BevyManifest};
19use proc_macro::TokenStream;
20use proc_macro2::{Ident, Span};
21use quote::{format_ident, quote, ToTokens};
22use syn::{
23    parse_macro_input, parse_quote, punctuated::Punctuated, token::Comma, ConstParam, Data,
24    DeriveInput, GenericParam, TypeParam,
25};
26
27enum BundleFieldKind {
28    Component,
29    Ignore,
30}
31
32const BUNDLE_ATTRIBUTE_NAME: &str = "bundle";
33const BUNDLE_ATTRIBUTE_IGNORE_NAME: &str = "ignore";
34const BUNDLE_ATTRIBUTE_NO_FROM_COMPONENTS: &str = "ignore_from_components";
35
36#[derive(Debug)]
37struct BundleAttributes {
38    impl_from_components: bool,
39}
40
41impl Default for BundleAttributes {
42    fn default() -> Self {
43        Self {
44            impl_from_components: true,
45        }
46    }
47}
48
49/// Implement the `Bundle` trait.
50#[proc_macro_derive(Bundle, attributes(bundle))]
51pub fn derive_bundle(input: TokenStream) -> TokenStream {
52    let ast = parse_macro_input!(input as DeriveInput);
53    let ecs_path = bevy_ecs_path();
54
55    let mut attributes = BundleAttributes::default();
56
57    for attr in &ast.attrs {
58        if attr.path().is_ident(BUNDLE_ATTRIBUTE_NAME) {
59            let parsing = attr.parse_nested_meta(|meta| {
60                if meta.path.is_ident(BUNDLE_ATTRIBUTE_NO_FROM_COMPONENTS) {
61                    attributes.impl_from_components = false;
62                    return Ok(());
63                }
64
65                Err(meta.error(format!("Invalid bundle container attribute. Allowed attributes: `{BUNDLE_ATTRIBUTE_NO_FROM_COMPONENTS}`")))
66            });
67
68            if let Err(e) = parsing {
69                return e.into_compile_error().into();
70            }
71        }
72    }
73
74    let fields = match get_struct_fields(&ast.data, "derive(Bundle)") {
75        Ok(fields) => fields,
76        Err(e) => return e.into_compile_error().into(),
77    };
78
79    let mut field_kinds = Vec::with_capacity(fields.len());
80
81    for field in fields {
82        let mut kind = BundleFieldKind::Component;
83
84        for attr in field
85            .attrs
86            .iter()
87            .filter(|a| a.path().is_ident(BUNDLE_ATTRIBUTE_NAME))
88        {
89            if let Err(error) = attr.parse_nested_meta(|meta| {
90                if meta.path.is_ident(BUNDLE_ATTRIBUTE_IGNORE_NAME) {
91                    kind = BundleFieldKind::Ignore;
92                    Ok(())
93                } else {
94                    Err(meta.error(format!(
95                        "Invalid bundle attribute. Use `{BUNDLE_ATTRIBUTE_IGNORE_NAME}`"
96                    )))
97                }
98            }) {
99                return error.into_compile_error().into();
100            }
101        }
102
103        field_kinds.push(kind);
104    }
105
106    let field_types = fields.iter().map(|field| &field.ty).collect::<Vec<_>>();
107
108    let mut active_field_types = Vec::new();
109    let mut active_field_members = Vec::new();
110    let mut active_field_locals = Vec::new();
111    let mut inactive_field_members = Vec::new();
112    for ((field_member, field_type), field_kind) in
113        fields.members().zip(field_types).zip(field_kinds)
114    {
115        let field_local = format_ident!("field_{}", field_member);
116
117        match field_kind {
118            BundleFieldKind::Component => {
119                active_field_types.push(field_type);
120                active_field_locals.push(field_local);
121                active_field_members.push(field_member);
122            }
123            BundleFieldKind::Ignore => inactive_field_members.push(field_member),
124        }
125    }
126    let generics = ast.generics;
127    let generics_ty_list = generics.type_params().map(|p| p.ident.clone());
128    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
129    let struct_name = &ast.ident;
130
131    let bundle_impl = quote! {
132        // SAFETY:
133        // - ComponentId is returned in field-definition-order. [get_components] uses field-definition-order
134        // - `Bundle::get_components` is exactly once for each member. Rely's on the Component -> Bundle implementation to properly pass
135        //   the correct `StorageType` into the callback.
136        unsafe impl #impl_generics #ecs_path::bundle::Bundle for #struct_name #ty_generics #where_clause {
137            fn component_ids(
138                components: &mut #ecs_path::component::ComponentsRegistrator,
139            ) -> impl Iterator<Item = #ecs_path::component::ComponentId> + use<#(#generics_ty_list,)*> {
140                core::iter::empty()#(.chain(<#active_field_types as #ecs_path::bundle::Bundle>::component_ids(components)))*
141            }
142
143            fn get_component_ids(
144                components: &#ecs_path::component::Components,
145            ) -> impl Iterator<Item = Option<#ecs_path::component::ComponentId>> {
146                core::iter::empty()#(.chain(<#active_field_types as #ecs_path::bundle::Bundle>::get_component_ids(components)))*
147            }
148        }
149    };
150
151    let dynamic_bundle_impl = quote! {
152        impl #impl_generics #ecs_path::bundle::DynamicBundle for #struct_name #ty_generics #where_clause {
153            type Effect = ();
154            #[allow(unused_variables)]
155            #[inline]
156            unsafe fn get_components(
157                ptr: #ecs_path::ptr::MovingPtr<'_, Self>,
158                func: &mut impl FnMut(#ecs_path::component::StorageType, #ecs_path::ptr::OwningPtr<'_>)
159            ) {
160                use #ecs_path::__macro_exports::DebugCheckedUnwrap;
161
162                #ecs_path::ptr::deconstruct_moving_ptr!({
163                    let #struct_name { #(#active_field_members: #active_field_locals,)* #(#inactive_field_members: _,)* } = ptr;
164                });
165                #(
166                    <#active_field_types as #ecs_path::bundle::DynamicBundle>::get_components(
167                        #active_field_locals,
168                        func
169                    );
170                )*
171            }
172
173            #[allow(unused_variables)]
174            #[inline]
175            unsafe fn apply_effect(
176                ptr: #ecs_path::ptr::MovingPtr<'_, core::mem::MaybeUninit<Self>>,
177                func: &mut #ecs_path::world::EntityWorldMut<'_>,
178            ) {
179            }
180        }
181    };
182
183    let from_components_impl = attributes.impl_from_components.then(|| quote! {
184        // SAFETY:
185        // - ComponentId is returned in field-definition-order. [from_components] uses field-definition-order
186        unsafe impl #impl_generics #ecs_path::bundle::BundleFromComponents for #struct_name #ty_generics #where_clause {
187            #[allow(unused_variables, non_snake_case)]
188            unsafe fn from_components<__T, __F>(ctx: &mut __T, func: &mut __F) -> Self
189            where
190                __F: FnMut(&mut __T) -> #ecs_path::ptr::OwningPtr<'_>
191            {
192                Self {
193                    #(#active_field_members: <#active_field_types as #ecs_path::bundle::BundleFromComponents>::from_components(ctx, &mut *func),)*
194                    #(#inactive_field_members: ::core::default::Default::default(),)*
195                }
196            }
197        }
198    });
199    TokenStream::from(quote! {
200        #bundle_impl
201        #from_components_impl
202        #dynamic_bundle_impl
203    })
204}
205
206/// Implement the `MapEntities` trait.
207#[proc_macro_derive(MapEntities, attributes(entities))]
208pub fn derive_map_entities(input: TokenStream) -> TokenStream {
209    let ast = parse_macro_input!(input as DeriveInput);
210    let ecs_path = bevy_ecs_path();
211
212    let map_entities_impl = map_entities(
213        &ast.data,
214        &ecs_path,
215        Ident::new("self", Span::call_site()),
216        false,
217        false,
218        None,
219    );
220
221    let struct_name = &ast.ident;
222    let (impl_generics, type_generics, where_clause) = &ast.generics.split_for_impl();
223    TokenStream::from(quote! {
224        impl #impl_generics #ecs_path::entity::MapEntities for #struct_name #type_generics #where_clause {
225            fn map_entities<M: #ecs_path::entity::EntityMapper>(&mut self, mapper: &mut M) {
226                #map_entities_impl
227            }
228        }
229    })
230}
231
232/// Implement `SystemParam` to use a struct as a parameter in a system
233#[proc_macro_derive(SystemParam, attributes(system_param))]
234pub fn derive_system_param(input: TokenStream) -> TokenStream {
235    let token_stream = input.clone();
236    let ast = parse_macro_input!(input as DeriveInput);
237
238    match derive_system_param_impl(token_stream, ast) {
239        Ok(t) => t,
240        Err(e) => e.into_compile_error().into(),
241    }
242}
243fn derive_system_param_impl(
244    token_stream: TokenStream,
245    ast: DeriveInput,
246) -> syn::Result<TokenStream> {
247    let fields = get_struct_fields(&ast.data, "derive(SystemParam)")?;
248    let path = bevy_ecs_path();
249
250    let field_locals = fields
251        .members()
252        .map(|m| format_ident!("field{}", m))
253        .collect::<Vec<_>>();
254    let field_members = fields.members().collect::<Vec<_>>();
255    let field_types = fields.iter().map(|f| &f.ty).collect::<Vec<_>>();
256
257    let field_validation_names = fields.members().map(|m| format!("::{}", quote! { #m }));
258    let mut field_validation_messages = Vec::with_capacity(fields.len());
259    for attr in fields
260        .iter()
261        .map(|f| f.attrs.iter().find(|a| a.path().is_ident("system_param")))
262    {
263        let mut field_validation_message = None;
264        if let Some(attr) = attr {
265            attr.parse_nested_meta(|nested| {
266                if nested.path.is_ident("validation_message") {
267                    field_validation_message = Some(nested.value()?.parse()?);
268                    Ok(())
269                } else {
270                    Err(nested.error("Unsupported attribute"))
271                }
272            })?;
273        }
274        field_validation_messages
275            .push(field_validation_message.unwrap_or_else(|| quote! { err.message }));
276    }
277
278    let generics = ast.generics;
279
280    // Emit an error if there's any unrecognized lifetime names.
281    let w = format_ident!("w");
282    let s = format_ident!("s");
283    for lt in generics.lifetimes() {
284        let ident = &lt.lifetime.ident;
285        if ident != &w && ident != &s {
286            return Err(syn::Error::new_spanned(
287                lt,
288                r#"invalid lifetime name: expected `'w` or `'s`
289 'w -- refers to data stored in the World.
290 's -- refers to data stored in the SystemParam's state.'"#,
291            ));
292        }
293    }
294
295    let (_impl_generics, ty_generics, where_clause) = generics.split_for_impl();
296
297    let lifetimeless_generics: Vec<_> = generics
298        .params
299        .iter()
300        .filter(|g| !matches!(g, GenericParam::Lifetime(_)))
301        .collect();
302
303    let shadowed_lifetimes: Vec<_> = generics.lifetimes().map(|_| quote!('_)).collect();
304
305    let mut punctuated_generics = Punctuated::<_, Comma>::new();
306    punctuated_generics.extend(lifetimeless_generics.iter().map(|g| match g {
307        GenericParam::Type(g) => GenericParam::Type(TypeParam {
308            default: None,
309            ..g.clone()
310        }),
311        GenericParam::Const(g) => GenericParam::Const(ConstParam {
312            default: None,
313            ..g.clone()
314        }),
315        _ => unreachable!(),
316    }));
317
318    let mut punctuated_generic_idents = Punctuated::<_, Comma>::new();
319    punctuated_generic_idents.extend(lifetimeless_generics.iter().map(|g| match g {
320        GenericParam::Type(g) => &g.ident,
321        GenericParam::Const(g) => &g.ident,
322        _ => unreachable!(),
323    }));
324
325    let punctuated_generics_no_bounds: Punctuated<_, Comma> = lifetimeless_generics
326        .iter()
327        .map(|&g| match g.clone() {
328            GenericParam::Type(mut g) => {
329                g.bounds.clear();
330                GenericParam::Type(g)
331            }
332            g => g,
333        })
334        .collect();
335
336    let mut tuple_types: Vec<_> = field_types.iter().map(ToTokens::to_token_stream).collect();
337    let mut tuple_patterns: Vec<_> = field_locals.iter().map(ToTokens::to_token_stream).collect();
338
339    // If the number of fields exceeds the 16-parameter limit,
340    // fold the fields into tuples of tuples until we are below the limit.
341    const LIMIT: usize = 16;
342    while tuple_types.len() > LIMIT {
343        let end = Vec::from_iter(tuple_types.drain(..LIMIT));
344        tuple_types.push(parse_quote!( (#(#end,)*) ));
345
346        let end = Vec::from_iter(tuple_patterns.drain(..LIMIT));
347        tuple_patterns.push(parse_quote!( (#(#end,)*) ));
348    }
349    // Create a where clause for the `ReadOnlySystemParam` impl.
350    // Ensure that each field implements `ReadOnlySystemParam`.
351    let mut read_only_generics = generics.clone();
352    let read_only_where_clause = read_only_generics.make_where_clause();
353    for field_type in &field_types {
354        read_only_where_clause
355            .predicates
356            .push(syn::parse_quote!(#field_type: #path::system::ReadOnlySystemParam));
357    }
358
359    let fields_alias =
360        ensure_no_collision(format_ident!("__StructFieldsAlias"), token_stream.clone());
361
362    let struct_name = &ast.ident;
363    let state_struct_visibility = &ast.vis;
364    let state_struct_name = ensure_no_collision(format_ident!("FetchState"), token_stream);
365
366    let mut builder_name = None;
367    for meta in ast
368        .attrs
369        .iter()
370        .filter(|a| a.path().is_ident("system_param"))
371    {
372        meta.parse_nested_meta(|nested| {
373            if nested.path.is_ident("builder") {
374                builder_name = Some(format_ident!("{struct_name}Builder"));
375                Ok(())
376            } else {
377                Err(nested.error("Unsupported attribute"))
378            }
379        })?;
380    }
381
382    let builder = builder_name.map(|builder_name| {
383        let builder_type_parameters: Vec<Ident> = field_members.iter().map(|m| format_ident!("B{}", m)).collect();
384        let builder_doc_comment = format!("A [`SystemParamBuilder`] for a [`{struct_name}`].");
385        let builder_struct = quote! {
386            #[doc = #builder_doc_comment]
387            struct #builder_name<#(#builder_type_parameters,)*> {
388                #(#field_members: #builder_type_parameters,)*
389            }
390        };
391        let lifetimes: Vec<_> = generics.lifetimes().collect();
392        let generic_struct = quote!{ #struct_name <#(#lifetimes,)* #punctuated_generic_idents> };
393        let builder_impl = quote!{
394            // SAFETY: This delegates to the `SystemParamBuilder` for tuples.
395            unsafe impl<
396                #(#lifetimes,)*
397                #(#builder_type_parameters: #path::system::SystemParamBuilder<#field_types>,)*
398                #punctuated_generics
399            > #path::system::SystemParamBuilder<#generic_struct> for #builder_name<#(#builder_type_parameters,)*>
400                #where_clause
401            {
402                fn build(self, world: &mut #path::world::World) -> <#generic_struct as #path::system::SystemParam>::State {
403                    let #builder_name { #(#field_members: #field_locals,)* } = self;
404                    #state_struct_name {
405                        state: #path::system::SystemParamBuilder::build((#(#tuple_patterns,)*), world)
406                    }
407                }
408            }
409        };
410        (builder_struct, builder_impl)
411    });
412    let (builder_struct, builder_impl) = builder.unzip();
413
414    Ok(TokenStream::from(quote! {
415        // We define the FetchState struct in an anonymous scope to avoid polluting the user namespace.
416        // The struct can still be accessed via SystemParam::State, e.g. MessageReaderState can be accessed via
417        // <MessageReader<'static, 'static, T> as SystemParam>::State
418        const _: () = {
419            // Allows rebinding the lifetimes of each field type.
420            type #fields_alias <'w, 's, #punctuated_generics_no_bounds> = (#(#tuple_types,)*);
421
422            #[doc(hidden)]
423            #state_struct_visibility struct #state_struct_name <#(#lifetimeless_generics,)*>
424            #where_clause {
425                state: <#fields_alias::<'static, 'static, #punctuated_generic_idents> as #path::system::SystemParam>::State,
426            }
427
428            unsafe impl<#punctuated_generics> #path::system::SystemParam for
429                #struct_name <#(#shadowed_lifetimes,)* #punctuated_generic_idents> #where_clause
430            {
431                type State = #state_struct_name<#punctuated_generic_idents>;
432                type Item<'w, 's> = #struct_name #ty_generics;
433
434                fn init_state(world: &mut #path::world::World) -> Self::State {
435                    #state_struct_name {
436                        state: <#fields_alias::<'_, '_, #punctuated_generic_idents> as #path::system::SystemParam>::init_state(world),
437                    }
438                }
439
440                fn init_access(state: &Self::State, system_meta: &mut #path::system::SystemMeta, component_access_set: &mut #path::query::FilteredAccessSet, world: &mut #path::world::World) {
441                    <#fields_alias::<'_, '_, #punctuated_generic_idents> as #path::system::SystemParam>::init_access(&state.state, system_meta, component_access_set, world);
442                }
443
444                fn apply(state: &mut Self::State, system_meta: &#path::system::SystemMeta, world: &mut #path::world::World) {
445                    <#fields_alias::<'_, '_, #punctuated_generic_idents> as #path::system::SystemParam>::apply(&mut state.state, system_meta, world);
446                }
447
448                fn queue(state: &mut Self::State, system_meta: &#path::system::SystemMeta, world: #path::world::DeferredWorld) {
449                    <#fields_alias::<'_, '_, #punctuated_generic_idents> as #path::system::SystemParam>::queue(&mut state.state, system_meta, world);
450                }
451
452                #[inline]
453                unsafe fn validate_param<'w, 's>(
454                    state: &'s mut Self::State,
455                    _system_meta: &#path::system::SystemMeta,
456                    _world: #path::world::unsafe_world_cell::UnsafeWorldCell<'w>,
457                ) -> Result<(), #path::system::SystemParamValidationError> {
458                    let #state_struct_name { state: (#(#tuple_patterns,)*) } = state;
459                    #(
460                        <#field_types as #path::system::SystemParam>::validate_param(#field_locals, _system_meta, _world)
461                            .map_err(|err| #path::system::SystemParamValidationError::new::<Self>(err.skipped, #field_validation_messages, #field_validation_names))?;
462                    )*
463                    Result::Ok(())
464                }
465
466                #[inline]
467                unsafe fn get_param<'w, 's>(
468                    state: &'s mut Self::State,
469                    system_meta: &#path::system::SystemMeta,
470                    world: #path::world::unsafe_world_cell::UnsafeWorldCell<'w>,
471                    change_tick: #path::change_detection::Tick,
472                ) -> Self::Item<'w, 's> {
473                    let (#(#tuple_patterns,)*) = <
474                        (#(#tuple_types,)*) as #path::system::SystemParam
475                    >::get_param(&mut state.state, system_meta, world, change_tick);
476                    #struct_name {
477                        #(#field_members: #field_locals,)*
478                    }
479                }
480            }
481
482            // Safety: Each field is `ReadOnlySystemParam`, so this can only read from the `World`
483            unsafe impl<'w, 's, #punctuated_generics> #path::system::ReadOnlySystemParam for #struct_name #ty_generics #read_only_where_clause {}
484
485            #builder_impl
486        };
487
488        #builder_struct
489    }))
490}
491
492/// Implement `QueryData` to use a struct as a data parameter in a query
493#[proc_macro_derive(QueryData, attributes(query_data))]
494pub fn derive_query_data(input: TokenStream) -> TokenStream {
495    derive_query_data_impl(input)
496}
497
498/// Implement `QueryFilter` to use a struct as a filter parameter in a query
499#[proc_macro_derive(QueryFilter, attributes(query_filter))]
500pub fn derive_query_filter(input: TokenStream) -> TokenStream {
501    derive_query_filter_impl(input)
502}
503
504/// Derive macro generating an impl of the trait `ScheduleLabel`.
505///
506/// This does not work for unions.
507#[proc_macro_derive(ScheduleLabel)]
508pub fn derive_schedule_label(input: TokenStream) -> TokenStream {
509    let input = parse_macro_input!(input as DeriveInput);
510    let mut trait_path = bevy_ecs_path();
511    trait_path.segments.push(format_ident!("schedule").into());
512    trait_path
513        .segments
514        .push(format_ident!("ScheduleLabel").into());
515    derive_label(input, "ScheduleLabel", &trait_path)
516}
517
518/// Derive macro generating an impl of the trait `SystemSet`.
519///
520/// This does not work for unions.
521#[proc_macro_derive(SystemSet)]
522pub fn derive_system_set(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.segments.push(format_ident!("SystemSet").into());
527    derive_label(input, "SystemSet", &trait_path)
528}
529
530pub(crate) fn bevy_ecs_path() -> syn::Path {
531    BevyManifest::shared(|manifest| manifest.get_path("bevy_ecs"))
532}
533
534/// Implement the `Event` trait.
535#[proc_macro_derive(Event, attributes(event))]
536pub fn derive_event(input: TokenStream) -> TokenStream {
537    event::derive_event(input)
538}
539
540/// Cheat sheet for derive syntax,
541/// see full explanation on `EntityEvent` trait docs.
542///
543/// ```ignore
544/// #[derive(EntityEvent)]
545/// /// Enable propagation, which defaults to using the ChildOf component
546/// #[entity_event(propagate)]
547/// /// Enable propagation using the given Traversal implementation
548/// #[entity_event(propagate = &'static ChildOf)]
549/// /// Always propagate
550/// #[entity_event(auto_propagate)]
551/// struct MyEvent;
552/// ```
553#[proc_macro_derive(EntityEvent, attributes(entity_event, event_target))]
554pub fn derive_entity_event(input: TokenStream) -> TokenStream {
555    event::derive_entity_event(input)
556}
557
558/// Implement the `Message` trait.
559#[proc_macro_derive(Message)]
560pub fn derive_message(input: TokenStream) -> TokenStream {
561    message::derive_message(input)
562}
563
564/// Implement the `Resource` trait.
565#[proc_macro_derive(Resource)]
566pub fn derive_resource(input: TokenStream) -> TokenStream {
567    component::derive_resource(input)
568}
569
570/// Cheat sheet for derive syntax,
571/// see full explanation and examples on the `Component` trait doc.
572///
573/// ## Immutability
574/// ```ignore
575/// #[derive(Component)]
576/// #[component(immutable)]
577/// struct MyComponent;
578/// ```
579///
580/// ## Sparse instead of table-based storage
581/// ```ignore
582/// #[derive(Component)]
583/// #[component(storage = "SparseSet")]
584/// struct MyComponent;
585/// ```
586///
587/// ## Required Components
588///
589/// ```ignore
590/// #[derive(Component)]
591/// #[require(
592///     // `Default::default()`
593///     A,
594///     // tuple structs
595///     B(1),
596///     // named-field structs
597///     C {
598///         x: 1,
599///         ..default()
600///     },
601///     // unit structs/variants
602///     D::One,
603///     // associated consts
604///     E::ONE,
605///     // constructors
606///     F::new(1),
607///     // arbitrary expressions
608///     G = make(1, 2, 3)
609/// )]
610/// struct MyComponent;
611/// ```
612///
613/// ## Relationships
614/// ```ignore
615/// #[derive(Component)]
616/// #[relationship(relationship_target = Children)]
617/// pub struct ChildOf {
618///     // Marking the field is not necessary if there is only one.
619///     #[relationship]
620///     pub parent: Entity,
621///     internal: u8,
622/// };
623///
624/// #[derive(Component)]
625/// #[relationship_target(relationship = ChildOf)]
626/// pub struct Children(Vec<Entity>);
627/// ```
628///
629/// On despawn, also despawn all related entities:
630/// ```ignore
631/// #[derive(Component)]
632/// #[relationship_target(relationship_target = Children, linked_spawn)]
633/// pub struct Children(Vec<Entity>);
634/// ```
635///
636/// ## Hooks
637/// ```ignore
638/// #[derive(Component)]
639/// #[component(hook_name = function)]
640/// struct MyComponent;
641/// ```
642/// where `hook_name` is `on_add`, `on_insert`, `on_replace` or `on_remove`;  
643/// `function` can be either a path, e.g. `some_function::<Self>`,
644/// or a function call that returns a function that can be turned into
645/// a `ComponentHook`, e.g. `get_closure("Hi!")`.
646/// `function` can be elided if the path is `Self::on_add`, `Self::on_insert` etc.
647///
648/// ## Ignore this component when cloning an entity
649/// ```ignore
650/// #[derive(Component)]
651/// #[component(clone_behavior = Ignore)]
652/// struct MyComponent;
653/// ```
654#[proc_macro_derive(
655    Component,
656    attributes(component, require, relationship, relationship_target, entities)
657)]
658pub fn derive_component(input: TokenStream) -> TokenStream {
659    component::derive_component(input)
660}
661
662/// Implement the `FromWorld` trait.
663#[proc_macro_derive(FromWorld, attributes(from_world))]
664pub fn derive_from_world(input: TokenStream) -> TokenStream {
665    let bevy_ecs_path = bevy_ecs_path();
666    let ast = parse_macro_input!(input as DeriveInput);
667    let name = ast.ident;
668    let (impl_generics, ty_generics, where_clauses) = ast.generics.split_for_impl();
669
670    let (fields, variant_ident) = match &ast.data {
671        Data::Struct(data) => (&data.fields, None),
672        Data::Enum(data) => {
673            match data.variants.iter().find(|variant| {
674                variant
675                    .attrs
676                    .iter()
677                    .any(|attr| attr.path().is_ident("from_world"))
678            }) {
679                Some(variant) => (&variant.fields, Some(&variant.ident)),
680                None => {
681                    return syn::Error::new(
682                        Span::call_site(),
683                        "No variant found with the `#[from_world]` attribute",
684                    )
685                    .into_compile_error()
686                    .into();
687                }
688            }
689        }
690        Data::Union(_) => {
691            return syn::Error::new(
692                Span::call_site(),
693                "#[derive(FromWorld)]` does not support unions",
694            )
695            .into_compile_error()
696            .into();
697        }
698    };
699
700    let field_init_expr = quote!(#bevy_ecs_path::world::FromWorld::from_world(world));
701    let members = fields.members();
702
703    let field_initializers = match variant_ident {
704        Some(variant_ident) => quote!( Self::#variant_ident {
705            #(#members: #field_init_expr),*
706        }),
707        None => quote!( Self {
708            #(#members: #field_init_expr),*
709        }),
710    };
711
712    TokenStream::from(quote! {
713            impl #impl_generics #bevy_ecs_path::world::FromWorld for #name #ty_generics #where_clauses {
714                fn from_world(world: &mut #bevy_ecs_path::world::World) -> Self {
715                    #field_initializers
716                }
717            }
718    })
719}