implemented #[bundle(ignore)] (#6123)
# Objective Fixes #5559 Replaces #5628 ## Solution Because the generated method from_components() creates an instance of Self my implementation requires any field type that is marked to be ignored to implement Default. --- ## Changelog Added the possibility to ignore fields in a bundle with `#[bundle(ignore)]`. Typically used when `PhantomData` needs to be added to a `Bundle`.
This commit is contained in:
		
							parent
							
								
									1d22634cfb
								
							
						
					
					
						commit
						7a41efa227
					
				@ -12,8 +12,10 @@ use syn::{
 | 
			
		||||
    parse::{Parse, ParseStream},
 | 
			
		||||
    parse_macro_input,
 | 
			
		||||
    punctuated::Punctuated,
 | 
			
		||||
    spanned::Spanned,
 | 
			
		||||
    token::Comma,
 | 
			
		||||
    DeriveInput, Field, GenericParam, Ident, Index, LitInt, Result, Token, TypeParam,
 | 
			
		||||
    DeriveInput, Field, GenericParam, Ident, Index, LitInt, Meta, MetaList, NestedMeta, Result,
 | 
			
		||||
    Token, TypeParam,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct AllTuples {
 | 
			
		||||
@ -80,7 +82,15 @@ pub fn all_tuples(input: TokenStream) -> TokenStream {
 | 
			
		||||
    })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[proc_macro_derive(Bundle)]
 | 
			
		||||
enum BundleFieldKind {
 | 
			
		||||
    Component,
 | 
			
		||||
    Ignore,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const BUNDLE_ATTRIBUTE_NAME: &str = "bundle";
 | 
			
		||||
const BUNDLE_ATTRIBUTE_IGNORE_NAME: &str = "ignore";
 | 
			
		||||
 | 
			
		||||
#[proc_macro_derive(Bundle, attributes(bundle))]
 | 
			
		||||
pub fn derive_bundle(input: TokenStream) -> TokenStream {
 | 
			
		||||
    let ast = parse_macro_input!(input as DeriveInput);
 | 
			
		||||
    let ecs_path = bevy_ecs_path();
 | 
			
		||||
@ -90,6 +100,36 @@ pub fn derive_bundle(input: TokenStream) -> TokenStream {
 | 
			
		||||
        Err(e) => return e.into_compile_error().into(),
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    let mut field_kind = Vec::with_capacity(named_fields.len());
 | 
			
		||||
 | 
			
		||||
    'field_loop: for field in named_fields.iter() {
 | 
			
		||||
        for attr in &field.attrs {
 | 
			
		||||
            if attr.path.is_ident(BUNDLE_ATTRIBUTE_NAME) {
 | 
			
		||||
                if let Ok(Meta::List(MetaList { nested, .. })) = attr.parse_meta() {
 | 
			
		||||
                    if let Some(&NestedMeta::Meta(Meta::Path(ref path))) = nested.first() {
 | 
			
		||||
                        if path.is_ident(BUNDLE_ATTRIBUTE_IGNORE_NAME) {
 | 
			
		||||
                            field_kind.push(BundleFieldKind::Ignore);
 | 
			
		||||
                            continue 'field_loop;
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        return syn::Error::new(
 | 
			
		||||
                            path.span(),
 | 
			
		||||
                            format!(
 | 
			
		||||
                                "Invalid bundle attribute. Use `{BUNDLE_ATTRIBUTE_IGNORE_NAME}`"
 | 
			
		||||
                            ),
 | 
			
		||||
                        )
 | 
			
		||||
                        .into_compile_error()
 | 
			
		||||
                        .into();
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    return syn::Error::new(attr.span(), format!("Invalid bundle attribute. Use `#[{BUNDLE_ATTRIBUTE_NAME}({BUNDLE_ATTRIBUTE_IGNORE_NAME})]`")).into_compile_error().into();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        field_kind.push(BundleFieldKind::Component);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let field = named_fields
 | 
			
		||||
        .iter()
 | 
			
		||||
        .map(|field| field.ident.as_ref().unwrap())
 | 
			
		||||
@ -102,7 +142,11 @@ pub fn derive_bundle(input: TokenStream) -> TokenStream {
 | 
			
		||||
    let mut field_component_ids = Vec::new();
 | 
			
		||||
    let mut field_get_components = Vec::new();
 | 
			
		||||
    let mut field_from_components = Vec::new();
 | 
			
		||||
    for (field_type, field) in field_type.iter().zip(field.iter()) {
 | 
			
		||||
    for ((field_type, field_kind), field) in
 | 
			
		||||
        field_type.iter().zip(field_kind.iter()).zip(field.iter())
 | 
			
		||||
    {
 | 
			
		||||
        match field_kind {
 | 
			
		||||
            BundleFieldKind::Component => {
 | 
			
		||||
                field_component_ids.push(quote! {
 | 
			
		||||
                <#field_type as #ecs_path::bundle::Bundle>::component_ids(components, storages, &mut *ids);
 | 
			
		||||
                });
 | 
			
		||||
@ -113,6 +157,14 @@ pub fn derive_bundle(input: TokenStream) -> TokenStream {
 | 
			
		||||
                    #field: <#field_type as #ecs_path::bundle::Bundle>::from_components(ctx, &mut *func),
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            BundleFieldKind::Ignore => {
 | 
			
		||||
                field_from_components.push(quote! {
 | 
			
		||||
                    #field: ::std::default::Default::default(),
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    let generics = ast.generics;
 | 
			
		||||
    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
 | 
			
		||||
    let struct_name = &ast.ident;
 | 
			
		||||
 | 
			
		||||
@ -82,10 +82,12 @@ use std::{any::TypeId, collections::HashMap};
 | 
			
		||||
/// used instead.
 | 
			
		||||
/// The derived `Bundle` implementation contains the items of its fields, which all must
 | 
			
		||||
/// implement `Bundle`.
 | 
			
		||||
/// As explained above, this includes any [`Component`] type, and other derived bundles:
 | 
			
		||||
/// As explained above, this includes any [`Component`] type, and other derived bundles.
 | 
			
		||||
///
 | 
			
		||||
/// If you want to add `PhantomData` to your `Bundle` you have to mark it with `#[bundle(ignore)]`.
 | 
			
		||||
/// ```
 | 
			
		||||
/// # use bevy_ecs::{component::Component, bundle::Bundle};
 | 
			
		||||
/// # use std::marker::PhantomData;
 | 
			
		||||
/// use bevy_ecs::{component::Component, bundle::Bundle};
 | 
			
		||||
///
 | 
			
		||||
/// #[derive(Component)]
 | 
			
		||||
/// struct XPosition(i32);
 | 
			
		||||
@ -99,12 +101,20 @@ use std::{any::TypeId, collections::HashMap};
 | 
			
		||||
///     y: YPosition,
 | 
			
		||||
/// }
 | 
			
		||||
///
 | 
			
		||||
/// // You have to implement `Default` for ignored field types in bundle structs.
 | 
			
		||||
/// #[derive(Default)]
 | 
			
		||||
/// struct Other(f32);
 | 
			
		||||
///
 | 
			
		||||
/// #[derive(Bundle)]
 | 
			
		||||
/// struct NamedPointBundle {
 | 
			
		||||
/// struct NamedPointBundle<T: Send + Sync + 'static> {
 | 
			
		||||
///     // Or other bundles
 | 
			
		||||
///     a: PositionBundle,
 | 
			
		||||
///     // In addition to more components
 | 
			
		||||
///     z: PointName,
 | 
			
		||||
///
 | 
			
		||||
///     // when you need to use `PhantomData` you have to mark it as ignored
 | 
			
		||||
///     #[bundle(ignore)]
 | 
			
		||||
///     _phantom_data: PhantomData<T>
 | 
			
		||||
/// }
 | 
			
		||||
///
 | 
			
		||||
/// #[derive(Component)]
 | 
			
		||||
 | 
			
		||||
@ -238,6 +238,45 @@ mod tests {
 | 
			
		||||
                b: B(2),
 | 
			
		||||
            }
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        #[derive(Default, Component, PartialEq, Debug)]
 | 
			
		||||
        struct Ignored;
 | 
			
		||||
 | 
			
		||||
        #[derive(Bundle, PartialEq, Debug)]
 | 
			
		||||
        struct BundleWithIgnored {
 | 
			
		||||
            c: C,
 | 
			
		||||
            #[bundle(ignore)]
 | 
			
		||||
            ignored: Ignored,
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let mut ids = Vec::new();
 | 
			
		||||
        <BundleWithIgnored as Bundle>::component_ids(
 | 
			
		||||
            &mut world.components,
 | 
			
		||||
            &mut world.storages,
 | 
			
		||||
            &mut |id| {
 | 
			
		||||
                ids.push(id);
 | 
			
		||||
            },
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        assert_eq!(ids, &[world.init_component::<C>(),]);
 | 
			
		||||
 | 
			
		||||
        let e4 = world
 | 
			
		||||
            .spawn(BundleWithIgnored {
 | 
			
		||||
                c: C,
 | 
			
		||||
                ignored: Ignored,
 | 
			
		||||
            })
 | 
			
		||||
            .id();
 | 
			
		||||
 | 
			
		||||
        assert_eq!(world.get::<C>(e4).unwrap(), &C);
 | 
			
		||||
        assert_eq!(world.get::<Ignored>(e4), None);
 | 
			
		||||
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            world.entity_mut(e4).remove::<BundleWithIgnored>().unwrap(),
 | 
			
		||||
            BundleWithIgnored {
 | 
			
		||||
                c: C,
 | 
			
		||||
                ignored: Ignored,
 | 
			
		||||
            }
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user