diff --git a/Cargo.toml b/Cargo.toml index a367b149..6f4b3ad9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,6 +48,7 @@ internal_test_util = { path = "crates/internal_test_util" } internal_test_proc_macro = { path = "crates/internal_test_proc_macro" } bevy = { version = "0.19", default-features = false, features = ["bevy_state"] } bevy_app = { version = "0.19", default-features = false } +bevy_asset = { version = "0.19", default-features = false } bevy_state = { version = "0.19", default-features = false } bevy_reflect = { version = "0.19", default-features = false } bevy_reflect_derive = { version = "0.19", default-features = false } @@ -76,6 +77,7 @@ bevy_auto_plugin_shared = { workspace = true } # required for doc tests bevy = { workspace = true } bevy_app = { workspace = true } +bevy_asset = { workspace = true } bevy_reflect = { workspace = true } bevy_reflect_derive = { workspace = true } bevy_ecs = { workspace = true } diff --git a/crates/bevy_auto_plugin_proc_macros/Cargo.toml b/crates/bevy_auto_plugin_proc_macros/Cargo.toml index 4fb27782..c28132f1 100644 --- a/crates/bevy_auto_plugin_proc_macros/Cargo.toml +++ b/crates/bevy_auto_plugin_proc_macros/Cargo.toml @@ -29,4 +29,6 @@ proc-macro2 = { workspace = true } # needed for doc-tests bevy = { workspace = true } bevy_app = { workspace = true } +bevy_asset = { workspace = true } bevy_auto_plugin = { path = "../../.", default-features = false } +bevy_reflect = { workspace = true } diff --git a/crates/bevy_auto_plugin_proc_macros/src/lib.rs b/crates/bevy_auto_plugin_proc_macros/src/lib.rs index 4d8b9eae..e3db754b 100644 --- a/crates/bevy_auto_plugin_proc_macros/src/lib.rs +++ b/crates/bevy_auto_plugin_proc_macros/src/lib.rs @@ -30,6 +30,12 @@ pub fn auto_register_type(attr: CompilerStream, input: CompilerStream) -> Compil handle_attribute(expand::attr::auto_register_type, attr, input) } +#[doc = include_str!(concat!(env!("OUT_DIR"), "/docs/proc_attributes/actions/auto_register_asset_reflect.md"))] +#[proc_macro_attribute] +pub fn auto_register_asset_reflect(attr: CompilerStream, input: CompilerStream) -> CompilerStream { + handle_attribute(expand::attr::auto_register_asset_reflect, attr, input) +} + #[doc = include_str!(concat!(env!("OUT_DIR"), "/docs/proc_attributes/actions/auto_add_message.md"))] #[proc_macro_attribute] pub fn auto_add_message(attr: CompilerStream, input: CompilerStream) -> CompilerStream { @@ -42,6 +48,12 @@ pub fn auto_configure_system_set(attr: CompilerStream, input: CompilerStream) -> handle_attribute(expand::attr::auto_configure_system_set, attr, input) } +#[doc = include_str!(concat!(env!("OUT_DIR"), "/docs/proc_attributes/actions/auto_init_asset.md"))] +#[proc_macro_attribute] +pub fn auto_init_asset(attr: CompilerStream, input: CompilerStream) -> CompilerStream { + handle_attribute(expand::attr::auto_init_asset, attr, input) +} + #[doc = include_str!(concat!(env!("OUT_DIR"), "/docs/proc_attributes/actions/auto_init_resource.md"))] #[proc_macro_attribute] pub fn auto_init_resource(attr: CompilerStream, input: CompilerStream) -> CompilerStream { @@ -96,6 +108,12 @@ pub fn auto_add_plugin(attr: CompilerStream, input: CompilerStream) -> CompilerS handle_attribute(expand::attr::auto_add_plugin, attr, input) } +#[doc = include_str!(concat!(env!("OUT_DIR"), "/docs/proc_attributes/rewrites/auto_asset.md"))] +#[proc_macro_attribute] +pub fn auto_asset(attr: CompilerStream, input: CompilerStream) -> CompilerStream { + handle_attribute(expand::attr::auto_asset, attr, input) +} + #[doc = include_str!(concat!(env!("OUT_DIR"), "/docs/proc_attributes/rewrites/auto_component.md"))] #[proc_macro_attribute] pub fn auto_component(attr: CompilerStream, input: CompilerStream) -> CompilerStream { diff --git a/crates/bevy_auto_plugin_shared/Cargo.toml b/crates/bevy_auto_plugin_shared/Cargo.toml index 15f99a81..5fe63df8 100644 --- a/crates/bevy_auto_plugin_shared/Cargo.toml +++ b/crates/bevy_auto_plugin_shared/Cargo.toml @@ -47,6 +47,7 @@ wasm-bindgen-test = { workspace = true } anyhow = { workspace = true } # used in crate resolve tests bevy_ecs = { workspace = true } +bevy_asset = { workspace = true } bevy_reflect = { workspace = true } bevy_state = { workspace = true } bevy_auto_plugin = { path = "../../.", default-features = false } diff --git a/crates/bevy_auto_plugin_shared/src/__private/expand/attr/mod.rs b/crates/bevy_auto_plugin_shared/src/__private/expand/attr/mod.rs index 528c5ce0..1b8fd60e 100644 --- a/crates/bevy_auto_plugin_shared/src/__private/expand/attr/mod.rs +++ b/crates/bevy_auto_plugin_shared/src/__private/expand/attr/mod.rs @@ -31,7 +31,9 @@ macro_rules! gen_rewrite_outers { gen_action_outers! { auto_run_on_build => IaRunOnBuild, auto_register_type => IaRegisterType, + auto_register_asset_reflect => IaRegisterAssetReflect, auto_add_message => IaAddMessage, + auto_init_asset => IaInitAsset, auto_init_resource => IaInitResource, auto_insert_resource => IaInsertResource, auto_init_state => IaInitState, @@ -46,6 +48,7 @@ gen_action_outers! { } gen_rewrite_outers! { + auto_asset => IaAsset, auto_component => IaComponent, auto_resource => IaResource, auto_system => IaSystem, diff --git a/crates/bevy_auto_plugin_shared/src/__private/mod.rs b/crates/bevy_auto_plugin_shared/src/__private/mod.rs index 6dbb24cc..2681fe53 100644 --- a/crates/bevy_auto_plugin_shared/src/__private/mod.rs +++ b/crates/bevy_auto_plugin_shared/src/__private/mod.rs @@ -31,6 +31,18 @@ pub(crate) mod paths { } } + pub mod asset { + use super::*; + + pub fn resolve() -> Result { + bevy_crate_path!(asset) + } + + pub fn asset_root_path() -> syn::Path { + resolve().expect(bevy_crate_err_message!(asset)) + } + } + pub mod reflect { use super::*; @@ -119,6 +131,14 @@ mod tests { ); } + #[xtest] + fn test_crate_resolve_bevy_asset() { + assert_eq!( + map_resolve_crate(super::paths::asset::resolve()), + ok_bevy_crate_path_string!(asset) + ); + } + #[xtest] fn test_crate_resolve_bevy_state() { assert_eq!( diff --git a/crates/bevy_auto_plugin_shared/src/codegen/tokens.rs b/crates/bevy_auto_plugin_shared/src/codegen/tokens.rs index ee5aece8..4c427d15 100644 --- a/crates/bevy_auto_plugin_shared/src/codegen/tokens.rs +++ b/crates/bevy_auto_plugin_shared/src/codegen/tokens.rs @@ -61,6 +61,16 @@ pub fn derive_component_path() -> NonEmptyPath { ecs_import!(prelude::Component) } +pub fn derive_asset_path() -> NonEmptyPath { + let root = crate::__private::paths::asset::asset_root_path(); + parse_quote!(#root::Asset) +} + +pub fn derive_type_path_path() -> NonEmptyPath { + let root = crate::__private::paths::reflect::reflect_root_path(); + parse_quote!(#root::TypePath) +} + pub fn derive_resource_path() -> NonEmptyPath { ecs_import!(prelude::Resource) } @@ -98,6 +108,26 @@ pub fn derive_component<'a>( .concat(), ) } +pub fn derive_asset<'a>(extra_items: impl IntoIterator) -> TokenStream { + derive_from( + [ + vec![&derive_asset_path(), &derive_type_path_path()], + extra_items.into_iter().collect::>(), + ] + .concat(), + ) +} +pub fn derive_asset_without_type_path<'a>( + extra_items: impl IntoIterator, +) -> TokenStream { + derive_from( + [ + vec![&derive_asset_path()], + extra_items.into_iter().collect::>(), + ] + .concat(), + ) +} pub fn derive_resource<'a>(extra_items: impl IntoIterator) -> TokenStream { derive_from( [ @@ -193,6 +223,9 @@ pub fn use_bevy_state_app_ext_states() -> syn::ItemUse { pub fn auto_register_type(args: RegisterTypeAttrEmitter) -> TokenStream { args.to_token_stream() } +pub fn auto_register_asset_reflect(args: RegisterAssetReflectAttrEmitter) -> TokenStream { + args.to_token_stream() +} pub fn auto_register_state_type(args: RegisterStateTypeAttrEmitter) -> TokenStream { args.to_token_stream() } @@ -202,6 +235,9 @@ pub fn auto_name(args: NameAttrEmitter) -> TokenStream { pub fn auto_init_resource(args: InitResourceAttrEmitter) -> TokenStream { args.to_token_stream() } +pub fn auto_init_asset(args: InitAssetAttrEmitter) -> TokenStream { + args.to_token_stream() +} pub fn auto_insert_resource(args: InsertResourceAttrEmitter) -> TokenStream { args.to_token_stream() } diff --git a/crates/bevy_auto_plugin_shared/src/macro_api/attributes/actions/auto_init_asset.rs b/crates/bevy_auto_plugin_shared/src/macro_api/attributes/actions/auto_init_asset.rs new file mode 100644 index 00000000..2a938680 --- /dev/null +++ b/crates/bevy_auto_plugin_shared/src/macro_api/attributes/actions/auto_init_asset.rs @@ -0,0 +1,49 @@ +use crate::macro_api::prelude::*; +use darling::FromMeta; +use proc_macro2::TokenStream; +use quote::{ + ToTokens, + quote, +}; + +#[derive(FromMeta, Debug, Default, Clone, PartialEq, Hash)] +#[darling(derive_syn_parse, default)] +pub struct InitAssetArgs {} + +impl AttributeIdent for InitAssetArgs { + const IDENT: &'static str = "auto_init_asset"; +} + +pub type IaInitAsset = ItemAttribute< + Composed, + AllowStructOrEnumOrUse, +>; +pub type InitAssetAppMutEmitter = AppMutationEmitter; +pub type InitAssetAttrEmitter = AttrEmitter; + +impl EmitAppMutationTokens for InitAssetAppMutEmitter { + fn to_app_mutation_tokens( + &self, + tokens: &mut TokenStream, + app_param: &syn::Ident, + ) -> syn::Result<()> { + let bevy_asset = crate::__private::paths::asset::asset_root_path(); + let concrete_paths = self.args.concrete_paths()?; + for concrete_path in concrete_paths { + tokens.extend(quote! { + #bevy_asset::AssetApp::init_asset::<#concrete_path>(#app_param); + }); + } + Ok(()) + } +} + +impl ToTokens for InitAssetAttrEmitter { + fn to_tokens(&self, tokens: &mut TokenStream) { + let args = self.args.args.extra_args(); + tokens.extend(quote! { + #(#args),* + }); + *tokens = self.wrap_as_attr(tokens); + } +} diff --git a/crates/bevy_auto_plugin_shared/src/macro_api/attributes/actions/auto_register_asset_reflect.rs b/crates/bevy_auto_plugin_shared/src/macro_api/attributes/actions/auto_register_asset_reflect.rs new file mode 100644 index 00000000..8f937a60 --- /dev/null +++ b/crates/bevy_auto_plugin_shared/src/macro_api/attributes/actions/auto_register_asset_reflect.rs @@ -0,0 +1,49 @@ +use crate::macro_api::prelude::*; +use darling::FromMeta; +use proc_macro2::TokenStream; +use quote::{ + ToTokens, + quote, +}; + +#[derive(FromMeta, Debug, Default, Clone, PartialEq, Hash)] +#[darling(derive_syn_parse, default)] +pub struct RegisterAssetReflectArgs {} + +impl AttributeIdent for RegisterAssetReflectArgs { + const IDENT: &'static str = "auto_register_asset_reflect"; +} + +pub type IaRegisterAssetReflect = ItemAttribute< + Composed, + AllowStructOrEnumOrUse, +>; +pub type RegisterAssetReflectAppMutEmitter = AppMutationEmitter; +pub type RegisterAssetReflectAttrEmitter = AttrEmitter; + +impl EmitAppMutationTokens for RegisterAssetReflectAppMutEmitter { + fn to_app_mutation_tokens( + &self, + tokens: &mut TokenStream, + app_param: &syn::Ident, + ) -> syn::Result<()> { + let bevy_asset = crate::__private::paths::asset::asset_root_path(); + let concrete_paths = self.args.concrete_paths()?; + for concrete_path in concrete_paths { + tokens.extend(quote! { + #bevy_asset::AssetApp::register_asset_reflect::<#concrete_path>(#app_param); + }); + } + Ok(()) + } +} + +impl ToTokens for RegisterAssetReflectAttrEmitter { + fn to_tokens(&self, tokens: &mut TokenStream) { + let args = self.args.args.extra_args(); + tokens.extend(quote! { + #(#args),* + }); + *tokens = self.wrap_as_attr(tokens); + } +} diff --git a/crates/bevy_auto_plugin_shared/src/macro_api/attributes/actions/mod.rs b/crates/bevy_auto_plugin_shared/src/macro_api/attributes/actions/mod.rs index bbd3a320..16e3a1c2 100644 --- a/crates/bevy_auto_plugin_shared/src/macro_api/attributes/actions/mod.rs +++ b/crates/bevy_auto_plugin_shared/src/macro_api/attributes/actions/mod.rs @@ -3,12 +3,14 @@ mod auto_add_observer; mod auto_add_plugin; mod auto_add_system; mod auto_configure_system_set; +mod auto_init_asset; mod auto_init_resource; mod auto_init_state; mod auto_init_sub_state; mod auto_insert_resource; mod auto_name; mod auto_plugin_build_hook; +mod auto_register_asset_reflect; mod auto_register_state_type; mod auto_register_type; mod auto_run_on_build; @@ -20,12 +22,14 @@ pub mod prelude { pub use auto_add_plugin::*; pub use auto_add_system::*; pub use auto_configure_system_set::*; + pub use auto_init_asset::*; pub use auto_init_resource::*; pub use auto_init_state::*; pub use auto_init_sub_state::*; pub use auto_insert_resource::*; pub use auto_name::*; pub use auto_plugin_build_hook::*; + pub use auto_register_asset_reflect::*; pub use auto_register_state_type::*; pub use auto_register_type::*; pub use auto_run_on_build::*; diff --git a/crates/bevy_auto_plugin_shared/src/macro_api/attributes/rewrites/auto_asset.rs b/crates/bevy_auto_plugin_shared/src/macro_api/attributes/rewrites/auto_asset.rs new file mode 100644 index 00000000..5e4c2839 --- /dev/null +++ b/crates/bevy_auto_plugin_shared/src/macro_api/attributes/rewrites/auto_asset.rs @@ -0,0 +1,70 @@ +use crate::{ + codegen::{ + ExpandAttrs, + tokens, + }, + macro_api::prelude::*, + syntax::{ + ast::flag_or_list::FlagOrList, + validated::non_empty_path::NonEmptyPath, + }, + util::macros::impl_from_default, +}; +use darling::FromMeta; +use proc_macro2::Ident; + +#[derive(FromMeta, Debug, Default, Clone, PartialEq, Hash)] +#[darling(derive_syn_parse, default)] +pub struct AssetArgs { + pub derive: FlagOrList, + pub reflect: FlagOrList, + pub register: bool, + pub init: bool, +} + +impl AttributeIdent for AssetArgs { + const IDENT: &'static str = "auto_asset"; +} + +impl<'a> From<&'a AssetArgs> for InitAssetArgs { + fn from(_: &'a AssetArgs) -> Self { + Self::default() + } +} + +impl<'a> From<&'a AssetArgs> for RegisterAssetReflectArgs { + fn from(_: &'a AssetArgs) -> Self { + Self::default() + } +} + +pub type IaAsset = + ItemAttribute, AllowStructOrEnum>; +pub type AssetAttrExpandEmitter = AttrExpansionEmitter; + +impl AttrExpansionEmitterToExpandAttr for AssetAttrExpandEmitter { + fn to_expand_attrs(&self, expand_attrs: &mut ExpandAttrs) { + if self.args.args.base.derive.present { + let derive_tokens = if self.args.args.base.reflect.present { + tokens::derive_asset_without_type_path(&self.args.args.base.derive.items) + } else { + tokens::derive_asset(&self.args.args.base.derive.items) + }; + expand_attrs.attrs.push(derive_tokens); + } + if self.args.args.base.reflect.present { + if self.args.args.base.derive.present { + expand_attrs.attrs.push(tokens::derive_reflect()); + } + expand_attrs.append(tokens::reflect(&self.args.args.base.reflect.items)) + } + if self.args.args.base.register { + expand_attrs.attrs.push(tokens::auto_register_asset_reflect(self.into())); + } + if self.args.args.base.init { + expand_attrs.attrs.push(tokens::auto_init_asset(self.into())); + } + } +} + +impl_from_default!(AssetArgs => (InitAssetArgs, RegisterAssetReflectArgs)); diff --git a/crates/bevy_auto_plugin_shared/src/macro_api/attributes/rewrites/mod.rs b/crates/bevy_auto_plugin_shared/src/macro_api/attributes/rewrites/mod.rs index ca9210b1..8d107e81 100644 --- a/crates/bevy_auto_plugin_shared/src/macro_api/attributes/rewrites/mod.rs +++ b/crates/bevy_auto_plugin_shared/src/macro_api/attributes/rewrites/mod.rs @@ -1,3 +1,4 @@ +mod auto_asset; mod auto_component; mod auto_event; mod auto_message; @@ -9,6 +10,7 @@ mod auto_system; pub mod prelude { pub use super::*; + pub use auto_asset::*; pub use auto_component::*; pub use auto_event::*; pub use auto_message::*; diff --git a/crates/bevy_auto_plugin_shared/src/macro_api/context/macro_paths.rs b/crates/bevy_auto_plugin_shared/src/macro_api/context/macro_paths.rs index 758d5868..4121c2e8 100644 --- a/crates/bevy_auto_plugin_shared/src/macro_api/context/macro_paths.rs +++ b/crates/bevy_auto_plugin_shared/src/macro_api/context/macro_paths.rs @@ -14,6 +14,8 @@ pub struct MacroPaths { pub emit_add_observer_macro: syn::Path, /// resolved absolute path to `auto_add_plugin` pub emit_add_plugin_macro: syn::Path, + /// resolved absolute path to `auto_init_asset` + pub emit_init_asset_macro: syn::Path, /// resolved absolute path to `auto_init_resource` pub emit_init_resource_macro: syn::Path, /// resolved absolute path to `auto_init_state` @@ -22,6 +24,8 @@ pub struct MacroPaths { pub emit_init_sub_state_macro: syn::Path, /// resolved absolute path to `auto_insert_resource` pub emit_insert_resource_macro: syn::Path, + /// resolved absolute path to `auto_register_asset_reflect` + pub emit_register_asset_reflect_macro: syn::Path, /// resolved absolute path to `auto_register_state_type` pub emit_register_state_type_macro: syn::Path, /// resolved absolute path to `auto_register_type` @@ -43,10 +47,12 @@ impl Default for MacroPaths { emit_add_message_macro: parse_quote!( ::bevy_auto_plugin::prelude::auto_add_message ), emit_add_observer_macro: parse_quote!( ::bevy_auto_plugin::prelude::auto_add_observer ), emit_add_plugin_macro: parse_quote!( ::bevy_auto_plugin::prelude::auto_add_plugin ), + emit_init_asset_macro: parse_quote!( ::bevy_auto_plugin::prelude::auto_init_asset ), emit_init_resource_macro: parse_quote!( ::bevy_auto_plugin::prelude::auto_init_resource ), emit_init_state_macro: parse_quote!( ::bevy_auto_plugin::prelude::auto_init_state ), emit_init_sub_state_macro: parse_quote!( ::bevy_auto_plugin::prelude::auto_init_sub_state ), emit_insert_resource_macro: parse_quote!( ::bevy_auto_plugin::prelude::auto_insert_resource ), + emit_register_asset_reflect_macro: parse_quote!( ::bevy_auto_plugin::prelude::auto_register_asset_reflect), emit_register_state_type_macro: parse_quote!( ::bevy_auto_plugin::prelude::auto_register_state_type ), emit_register_type_macro: parse_quote!( ::bevy_auto_plugin::prelude::auto_register_type ), emit_run_on_build_macro: parse_quote!( ::bevy_auto_plugin::prelude::auto_run_on_build ), @@ -85,6 +91,12 @@ impl MacroPathProvider for AddPluginArgs { } } +impl MacroPathProvider for InitAssetArgs { + fn macro_path(context: &Context) -> &syn::Path { + &context.macros.emit_init_asset_macro + } +} + impl MacroPathProvider for InitResourceArgs { fn macro_path(context: &Context) -> &syn::Path { &context.macros.emit_init_resource_macro @@ -109,6 +121,12 @@ impl MacroPathProvider for InsertResourceArgs { } } +impl MacroPathProvider for RegisterAssetReflectArgs { + fn macro_path(context: &Context) -> &syn::Path { + &context.macros.emit_register_asset_reflect_macro + } +} + impl MacroPathProvider for RegisterStateTypeArgs { fn macro_path(context: &Context) -> &syn::Path { &context.macros.emit_register_state_type_macro diff --git a/docs/proc_attributes/actions/auto_init_asset.md b/docs/proc_attributes/actions/auto_init_asset.md new file mode 100644 index 00000000..38cb4660 --- /dev/null +++ b/docs/proc_attributes/actions/auto_init_asset.md @@ -0,0 +1,43 @@ +Automatically registers an asset to be initialized in the app. + +# Parameters +- `plugin = PluginType` - Required unless the `default_plugin` feature is enabled and `#[auto_plugin(default_plugin)]` is in scope. Specifies which plugin should initialize this asset. +- `after_build` - Optional. Injects this macro's tokens at the end of the plugin build instead of the start. +- `generics(T1, T2, ...)` - Optional. Specifies concrete types for generic parameters. + When provided, the asset will be initialized with these specific generic parameters. + Note: Clippy will complain if you have duplicate generic type names. For those you can use named generics: `generics(T1 = ..., T2 = ...)`. + +# Notes +- This attribute can be applied to a `use` item; each imported name becomes its own target. +- `use ...::*`, `use ...::self`, and `_` imports are not supported. +- Renames (`as`) are supported and use the local name. + +# Example +```rust +use bevy::prelude::*; +use bevy_asset::Asset; +use bevy_auto_plugin::prelude::*; + +#[derive(AutoPlugin)] +#[auto_plugin(impl_plugin_trait)] +struct MyPlugin; + +#[derive(Asset, TypePath)] +#[auto_init_asset(plugin = MyPlugin)] +struct FooAsset; +``` + +# Example (with generics) +```rust +use bevy::prelude::*; +use bevy_asset::Asset; +use bevy_auto_plugin::prelude::*; + +#[derive(AutoPlugin)] +#[auto_plugin(impl_plugin_trait)] +struct MyPlugin; + +#[derive(Asset, TypePath)] +#[auto_init_asset(plugin = MyPlugin, generics(usize))] +struct FooAssetWithGeneric(T); +``` diff --git a/docs/proc_attributes/actions/auto_register_asset_reflect.md b/docs/proc_attributes/actions/auto_register_asset_reflect.md new file mode 100644 index 00000000..920ebfc9 --- /dev/null +++ b/docs/proc_attributes/actions/auto_register_asset_reflect.md @@ -0,0 +1,45 @@ +Automatically registers an asset for Bevy asset reflection. + +# Parameters +- `plugin = PluginType` - Required unless the `default_plugin` feature is enabled and `#[auto_plugin(default_plugin)]` is in scope. Specifies which plugin should register this asset reflection. +- `after_build` - Optional. Injects this macro's tokens at the end of the plugin build instead of the start. +- `generics(T1, T2, ...)` - Optional. Specifies concrete types for generic parameters. + When provided, asset reflection will be registered with these specific generic parameters. + Note: Clippy will complain if you have duplicate generic type names. For those you can use named generics: `generics(T1 = ..., T2 = ...)`. + +# Notes +- This attribute can be applied to a `use` item; each imported name becomes its own target. +- `use ...::*`, `use ...::self`, and `_` imports are not supported. +- Renames (`as`) are supported and use the local name. +- This calls Bevy's `register_asset_reflect`, which registers the asset type and handle type, and adds asset-specific reflection data. +- The target type must satisfy Bevy's `register_asset_reflect` bounds, usually by deriving `Asset` and `Reflect`. + +# Example +```rust +use bevy::prelude::*; +use bevy_asset::Asset; +use bevy_auto_plugin::prelude::*; + +#[derive(AutoPlugin)] +#[auto_plugin(impl_plugin_trait)] +struct MyPlugin; + +#[derive(Asset, Reflect)] +#[auto_register_asset_reflect(plugin = MyPlugin)] +struct FooAsset; +``` + +# Example (with generics) +```rust +use bevy::prelude::*; +use bevy_asset::Asset; +use bevy_auto_plugin::prelude::*; + +#[derive(AutoPlugin)] +#[auto_plugin(impl_plugin_trait)] +struct MyPlugin; + +#[derive(Asset, Reflect)] +#[auto_register_asset_reflect(plugin = MyPlugin, generics(usize))] +struct FooAssetWithGeneric(T); +``` diff --git a/docs/proc_attributes/rewrites/auto_asset.md b/docs/proc_attributes/rewrites/auto_asset.md new file mode 100644 index 00000000..86a45094 --- /dev/null +++ b/docs/proc_attributes/rewrites/auto_asset.md @@ -0,0 +1,61 @@ +Automatically derives `Asset` with `TypePath` or `Reflect`, registers asset reflection, and initializes the asset in the app. + +# Parameters +- `plugin = PluginType` - Required unless the `default_plugin` feature is enabled and `#[auto_plugin(default_plugin)]` is in scope. Specifies which plugin should initialize this asset. +- `after_build` - Optional. Injects this macro's tokens at the end of the plugin build instead of the start. +- `generics(T1, T2, ...)` - Optional. Specifies concrete types for generic parameters. + When provided, the `register` and `init` actions use these specific generic parameters. + Note: Clippy will complain if you have duplicate generic type names. For those you can use named generics: `generics(A = ..., B = ...)`. +- `derive` | `derive(Debug, Default, ..)` - Optional. Specifies that the macro should handle deriving `Asset` and `TypePath`. + When `reflect` is also enabled, this derives `Asset` and `Reflect`; `Reflect` supplies the required `TypePath` implementation. + Passes through any additional derives listed. +- `reflect` | `reflect(Debug, Default, ..)` - Optional. Specifies that the macro should handle emitting the single `#[reflect(...)]`. + Passes through any additional reflects listed. + If enabled in tandem with `derive` it also includes `#[derive(Reflect)]`. +- `register` - Registers the asset for Bevy asset reflection by calling Bevy's `register_asset_reflect`. + Same as having `#[auto_register_asset_reflect]`. + The target type must satisfy Bevy's `register_asset_reflect` bounds, usually by deriving `Asset` and `Reflect`. +- `init` - Initializes the asset in the app. + Same as having `#[auto_init_asset]`. + +# Example +```rust +use bevy::prelude::*; +use bevy_auto_plugin::prelude::*; + +#[derive(AutoPlugin)] +#[auto_plugin(impl_plugin_trait)] +struct MyPlugin; + +#[auto_asset(plugin = MyPlugin, derive(Debug), init)] +struct FooAsset; +``` + +# Example (with reflect) +```rust +use bevy::prelude::*; +use bevy_auto_plugin::prelude::*; + +#[derive(AutoPlugin)] +#[auto_plugin(impl_plugin_trait)] +struct MyPlugin; + +#[auto_asset(plugin = MyPlugin, derive(Debug), reflect(Debug), register, init)] +struct ReflectedAsset { + value: usize, +} +``` + +# Example (with generics) +```rust +use bevy::prelude::*; +use bevy_auto_plugin::prelude::*; +use bevy_reflect::TypePath; + +#[derive(AutoPlugin)] +#[auto_plugin(impl_plugin_trait)] +struct MyPlugin; + +#[auto_asset(plugin = MyPlugin, generics(usize), derive(Debug), init)] +struct FooAssetWithGeneric(T); +``` diff --git a/src/lib.rs b/src/lib.rs index 5f1302ea..da255569 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -197,6 +197,9 @@ pub mod prelude { #[doc = include_str!("../docs/proc_attributes/actions/auto_add_system.md")] pub use bevy_auto_plugin_proc_macros::auto_add_system; + #[doc = include_str!("../docs/proc_attributes/actions/auto_init_asset.md")] + pub use bevy_auto_plugin_proc_macros::auto_init_asset; + #[doc = include_str!("../docs/proc_attributes/actions/auto_init_resource.md")] pub use bevy_auto_plugin_proc_macros::auto_init_resource; @@ -218,12 +221,18 @@ pub mod prelude { #[doc = include_str!("../docs/proc_attributes/actions/auto_register_state_type.md")] pub use bevy_auto_plugin_proc_macros::auto_register_state_type; + #[doc = include_str!("../docs/proc_attributes/actions/auto_register_asset_reflect.md")] + pub use bevy_auto_plugin_proc_macros::auto_register_asset_reflect; + #[doc = include_str!("../docs/proc_attributes/actions/auto_register_type.md")] pub use bevy_auto_plugin_proc_macros::auto_register_type; #[doc = include_str!("../docs/proc_attributes/actions/auto_add_observer.md")] pub use bevy_auto_plugin_proc_macros::auto_add_observer; + #[doc = include_str!("../docs/proc_attributes/rewrites/auto_asset.md")] + pub use bevy_auto_plugin_proc_macros::auto_asset; + #[doc = include_str!("../docs/proc_attributes/rewrites/auto_component.md")] pub use bevy_auto_plugin_proc_macros::auto_component; diff --git a/tests/e2e/actions/auto_init_asset.rs b/tests/e2e/actions/auto_init_asset.rs new file mode 100644 index 00000000..b3694a8e --- /dev/null +++ b/tests/e2e/actions/auto_init_asset.rs @@ -0,0 +1,43 @@ +use bevy_app::prelude::*; +use bevy_asset::{ + AssetPlugin, + Assets, + prelude::*, +}; +use bevy_auto_plugin::prelude::*; +use bevy_reflect::TypePath; +use internal_test_proc_macro::xtest; + +#[derive(AutoPlugin)] +#[auto_plugin(impl_plugin_trait)] +struct TestPlugin; + +#[auto_init_asset(plugin = TestPlugin)] +#[derive(Asset, TypePath)] +struct TestAsset; + +#[auto_init_asset(plugin = TestPlugin, generics(usize))] +#[derive(Asset, TypePath)] +struct GenericAsset(T); + +fn app() -> App { + let mut app = internal_test_util::create_minimal_app(); + app.add_plugins(AssetPlugin::default()); + app.add_plugins(TestPlugin); + app +} + +#[xtest] +fn test_auto_init_asset() { + let app = app(); + assert!(app.world().get_resource::>().is_some(), "did not auto init asset"); +} + +#[xtest] +fn test_auto_init_asset_generic() { + let app = app(); + assert!( + app.world().get_resource::>>().is_some(), + "did not auto init generic asset" + ); +} diff --git a/tests/e2e/actions/auto_register_asset_reflect.rs b/tests/e2e/actions/auto_register_asset_reflect.rs new file mode 100644 index 00000000..513268ef --- /dev/null +++ b/tests/e2e/actions/auto_register_asset_reflect.rs @@ -0,0 +1,85 @@ +use bevy_app::prelude::*; +use bevy_asset::{ + Asset, + ReflectAsset, +}; +use bevy_auto_plugin::prelude::*; +use bevy_ecs::reflect::AppTypeRegistry; +use bevy_reflect::{ + Reflect, + TypePath, +}; +use internal_test_proc_macro::xtest; +use std::any::TypeId; + +#[derive(AutoPlugin)] +#[auto_plugin(impl_plugin_trait)] +struct TestPlugin; + +#[auto_register_asset_reflect(plugin = TestPlugin)] +#[derive(Asset, Reflect)] +struct TestAsset; + +#[auto_register_asset_reflect(plugin = TestPlugin, generics(usize))] +#[derive(Asset, Reflect)] +struct GenericAsset(T); + +mod external { + use bevy_asset::Asset; + use bevy_reflect::Reflect; + + #[derive(Asset, Reflect)] + pub struct ExternalAssetA; + + #[derive(Asset, Reflect)] + pub struct ExternalAssetB; +} + +#[auto_register_asset_reflect(plugin = TestPlugin)] +use external::{ + ExternalAssetA, + ExternalAssetB, +}; + +fn app() -> App { + let mut app = internal_test_util::create_minimal_app(); + app.add_plugins(TestPlugin); + app +} + +#[xtest] +fn test_auto_register_asset_reflect() { + let app = app(); + let type_registry = app.world().resource::().0.clone(); + let type_registry = type_registry.read(); + assert!( + type_registry.get_type_data::(TypeId::of::()).is_some(), + "did not auto register asset reflection" + ); +} + +#[xtest] +fn test_auto_register_asset_reflect_generic() { + let app = app(); + let type_registry = app.world().resource::().0.clone(); + let type_registry = type_registry.read(); + assert!( + type_registry.get_type_data::(TypeId::of::>()).is_some(), + "did not auto register generic asset reflection" + ); +} + +#[xtest] +fn test_auto_register_asset_reflect_use() { + let app = app(); + let type_registry = app.world().resource::().0.clone(); + let type_registry = type_registry.read(); + assert!( + type_registry.get_type_data::(TypeId::of::()).is_some(), + "did not auto register ExternalAssetA asset reflection" + ); + assert!( + type_registry.get_type_data::(TypeId::of::()).is_some(), + "did not auto register ExternalAssetB asset reflection" + ); +} diff --git a/tests/e2e/actions/mod.rs b/tests/e2e/actions/mod.rs index 180bc3a6..943d4e55 100644 --- a/tests/e2e/actions/mod.rs +++ b/tests/e2e/actions/mod.rs @@ -18,6 +18,7 @@ mod auto_configure_system_set_schedule_config; mod auto_configure_system_set_schedule_config_multiple_groups; #[cfg(feature = "default_plugin")] mod auto_default_plugin; +mod auto_init_asset; mod auto_init_resource; mod auto_init_resource_generic; mod auto_init_state; @@ -31,6 +32,7 @@ mod auto_plugin_default_param; mod auto_plugin_default_param_method; mod auto_plugin_param; mod auto_plugin_with_generics; +mod auto_register_asset_reflect; mod auto_register_state_type; mod auto_register_type; mod auto_register_type_generic; diff --git a/tests/e2e/rewrites/auto_asset.rs b/tests/e2e/rewrites/auto_asset.rs new file mode 100644 index 00000000..e83cf12d --- /dev/null +++ b/tests/e2e/rewrites/auto_asset.rs @@ -0,0 +1,101 @@ +use bevy_app::prelude::*; +use bevy_asset::{ + Asset, + AssetPlugin, + Assets, + ReflectAsset, +}; +use bevy_auto_plugin::prelude::*; +use bevy_ecs::reflect::AppTypeRegistry; +use bevy_reflect::{ + Reflect, + TypePath, +}; +use internal_test_proc_macro::xtest; +use std::any::TypeId; + +#[derive(AutoPlugin)] +#[auto_plugin(impl_plugin_trait)] +struct Test; + +#[auto_asset(plugin = Test, derive(Debug, Default), init)] +struct FooAsset; + +#[auto_asset(plugin = Test, generics(usize), derive(Debug, Default), init)] +struct GenericAsset(T); + +#[auto_asset(plugin = Test, init)] +#[derive(Asset, TypePath)] +struct ManuallyDerivedAsset; + +#[auto_asset(plugin = Test, derive(Debug), reflect(Debug), register, init)] +struct ReflectedAsset { + value: usize, +} + +#[auto_asset(plugin = Test, generics(usize), derive, reflect, register)] +struct GenericReflectedAsset(T); + +fn app() -> App { + let mut app = internal_test_util::create_minimal_app(); + app.add_plugins(AssetPlugin::default()); + app.add_plugins(Test); + app +} + +#[xtest] +fn test_auto_asset_derives_type_path() { + assert!(FooAsset::type_path().contains("FooAsset")); +} + +#[xtest] +fn test_auto_init_asset() { + let app = app(); + assert!(app.world().contains_resource::>()); +} + +#[xtest] +fn test_auto_init_generic_asset() { + let app = app(); + assert!(app.world().contains_resource::>>()); +} + +#[xtest] +fn test_auto_asset_can_init_manually_derived_asset() { + let app = app(); + assert!(app.world().contains_resource::>()); +} + +#[xtest] +fn test_auto_asset_reflect_derives_reflect() { + fn assert_reflect() {} + + assert_reflect::(); + assert!(ReflectedAsset::type_path().contains("ReflectedAsset")); +} + +#[xtest] +fn test_auto_init_reflected_asset() { + let app = app(); + assert!(app.world().contains_resource::>()); +} + +#[xtest] +fn test_auto_asset_registers_asset_reflect() { + let app = app(); + let type_registry = app.world().resource::().0.clone(); + let type_registry = type_registry.read(); + assert!(type_registry.get_type_data::(TypeId::of::()).is_some()); +} + +#[xtest] +fn test_auto_asset_registers_generic_asset_reflect() { + let app = app(); + let type_registry = app.world().resource::().0.clone(); + let type_registry = type_registry.read(); + assert!( + type_registry + .get_type_data::(TypeId::of::>()) + .is_some() + ); +} diff --git a/tests/e2e/rewrites/mod.rs b/tests/e2e/rewrites/mod.rs index d69dbb81..4f53758f 100644 --- a/tests/e2e/rewrites/mod.rs +++ b/tests/e2e/rewrites/mod.rs @@ -1,3 +1,4 @@ +mod auto_asset; mod auto_component; mod auto_event; mod auto_message;