From 63c1e2b13880d798622d7a4dcfc0c755a9fcb5a8 Mon Sep 17 00:00:00 2001 From: bstriker Date: Thu, 5 Feb 2026 20:59:26 -0500 Subject: [PATCH] feat: add `default_plugin` feature for simplifying `auto_plugin` macros by omitting `plugin = ...` --- .github/workflows/ci.yaml | 4 +-- CHANGELOG.md | 3 +- Cargo.toml | 4 +++ README.md | 1 + .../bevy_auto_plugin_proc_macros/Cargo.toml | 1 + crates/bevy_auto_plugin_shared/Cargo.toml | 1 + .../__private/expand/derive/auto_plugin.rs | 19 ++++++++++++ .../src/macro_api/attributes/auto_plugin.rs | 2 ++ .../src/macro_api/composed.rs | 14 +++++++++ docs/derives/AutoPlugin.md | 3 +- tests/e2e/actions/auto_default_plugin.rs | 31 +++++++++++++++++++ tests/e2e/actions/mod.rs | 2 ++ 12 files changed, 81 insertions(+), 4 deletions(-) create mode 100644 tests/e2e/actions/auto_default_plugin.rs diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 9948aeaa..ca17b626 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -20,7 +20,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - feature-set: [ default, inventory, compat_generics_angles ] + feature-set: [ default, inventory, compat_generics_angles, default_plugin ] steps: - name: Checkout code uses: actions/checkout@v3 @@ -85,7 +85,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - feature-set: [ default, inventory ] + feature-set: [ default, inventory, default_plugin ] steps: - name: Checkout code uses: actions/checkout@v3 diff --git a/CHANGELOG.md b/CHANGELOG.md index 845ee89e..961b4899 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -165,4 +165,5 @@ - Allow action macros on `use` statements; each imported name becomes its own entry (no `*`, `self`, or `_`). - Add `pipe_in` for `auto_add_system`/`auto_system` to build system pipelines. - Accept `generics = <...>` as shorthand for `generics(...)` in attributes (feature: `compat_generics_angles`). - - This feature will eventually be removed unless a petition is opened. \ No newline at end of file + - This feature will eventually be removed unless a petition is opened. +- Add optional `default_plugin` feature to allow omitting `plugin = ...` when a default plugin is set via `#[auto_plugin(default_plugin)]`. diff --git a/Cargo.toml b/Cargo.toml index fe135e0b..30a76320 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,6 +20,10 @@ compat_generics_angles = [ "bevy_auto_plugin_proc_macros/compat_generics_angles", "bevy_auto_plugin_shared/compat_generics_angles", ] +default_plugin = [ + "bevy_auto_plugin_proc_macros/default_plugin", + "bevy_auto_plugin_shared/default_plugin", +] web = [ "bevy_auto_plugin_shared/web", ] diff --git a/README.md b/README.md index 6a32e212..1c217d2a 100644 --- a/README.md +++ b/README.md @@ -201,6 +201,7 @@ fn plugin(app: &mut App) { ``` There is `auto_plugin` arguments if your plugin has generics. +Optional: enable feature `default_plugin` and add `#[auto_plugin(default_plugin)]` to allow `auto_*` macros to omit `plugin = ...`. See [tests](tests/e2e) for other examples diff --git a/crates/bevy_auto_plugin_proc_macros/Cargo.toml b/crates/bevy_auto_plugin_proc_macros/Cargo.toml index cd95f14f..c4ddd5b8 100644 --- a/crates/bevy_auto_plugin_proc_macros/Cargo.toml +++ b/crates/bevy_auto_plugin_proc_macros/Cargo.toml @@ -18,6 +18,7 @@ inventory = ["bevy_auto_plugin_shared/inventory"] debug_log_plugin_registry = ["bevy_auto_plugin_shared/debug_log_plugin_registry"] log_plugin_build = ["bevy_auto_plugin_shared/log_plugin_build"] compat_generics_angles = ["bevy_auto_plugin_shared/compat_generics_angles"] +default_plugin = ["bevy_auto_plugin_shared/default_plugin"] [dependencies] bevy_auto_plugin_shared = { workspace = true } diff --git a/crates/bevy_auto_plugin_shared/Cargo.toml b/crates/bevy_auto_plugin_shared/Cargo.toml index 472e6264..ec8f68fe 100644 --- a/crates/bevy_auto_plugin_shared/Cargo.toml +++ b/crates/bevy_auto_plugin_shared/Cargo.toml @@ -15,6 +15,7 @@ inventory = [] debug_log_plugin_registry = ["log"] log_plugin_build = ["log"] compat_generics_angles = [] +default_plugin = [] web = [ "bevy_app/web" ] diff --git a/crates/bevy_auto_plugin_shared/src/__private/expand/derive/auto_plugin.rs b/crates/bevy_auto_plugin_shared/src/__private/expand/derive/auto_plugin.rs index 005ed361..5e731636 100644 --- a/crates/bevy_auto_plugin_shared/src/__private/expand/derive/auto_plugin.rs +++ b/crates/bevy_auto_plugin_shared/src/__private/expand/derive/auto_plugin.rs @@ -76,6 +76,25 @@ pub fn expand_derive_auto_plugin(input: MacroStream) -> MacroStream { }); } + #[cfg(feature = "default_plugin")] + if params.auto_plugin.default_plugin.is_present() { + if !params.generics.params.is_empty() { + compile_warnings.extend( + syn::Error::new( + params.auto_plugin.default_plugin.span(), + "`default_plugin` is not supported for generic plugins; use a concrete wrapper type", + ) + .to_compile_error(), + ); + } else { + output.extend(quote! { + #[doc(hidden)] + #[allow(dead_code)] + type __bevy_auto_plugin_default_plugin = #ident; + }); + } + } + quote! { #compile_warnings #output diff --git a/crates/bevy_auto_plugin_shared/src/macro_api/attributes/auto_plugin.rs b/crates/bevy_auto_plugin_shared/src/macro_api/attributes/auto_plugin.rs index f587461c..090fc67f 100644 --- a/crates/bevy_auto_plugin_shared/src/macro_api/attributes/auto_plugin.rs +++ b/crates/bevy_auto_plugin_shared/src/macro_api/attributes/auto_plugin.rs @@ -22,6 +22,8 @@ pub struct AutoPluginStructOrEnumArgs { #[darling(multiple)] pub generics: Vec, pub impl_plugin_trait: Flag, + #[cfg(feature = "default_plugin")] + pub default_plugin: Flag, #[deprecated( since = "0.8.0", note = "always implemented - remove `impl_generic_auto_plugin_trait`" diff --git a/crates/bevy_auto_plugin_shared/src/macro_api/composed.rs b/crates/bevy_auto_plugin_shared/src/macro_api/composed.rs index f56477bd..bf9b90f8 100644 --- a/crates/bevy_auto_plugin_shared/src/macro_api/composed.rs +++ b/crates/bevy_auto_plugin_shared/src/macro_api/composed.rs @@ -55,6 +55,8 @@ where let generics_keys: HashSet<&str> = MGenerics::keys().iter().copied().collect(); let mut plugin_bucket = Vec::::new(); + #[cfg(feature = "default_plugin")] + let mut has_plugin_key = false; let mut generics_bucket = Vec::::new(); let mut base_bucket = Vec::::new(); @@ -73,6 +75,10 @@ where }; let routed = if let Some(ref k) = key_opt { + #[cfg(feature = "default_plugin")] + if k == "plugin" { + has_plugin_key = true; + } if plugin_keys.contains(k.as_str()) { plugin_bucket.push(nm.clone()); true @@ -91,6 +97,14 @@ where } } + #[cfg(feature = "default_plugin")] + if !has_plugin_key && plugin_keys.contains("plugin") { + plugin_bucket.insert( + 0, + NestedMeta::Meta(parse_quote!(plugin = __bevy_auto_plugin_default_plugin)), + ); + } + // Parse each bucket let base = CBase::from_list(&base_bucket)?; let plugin = MPlugin::from_list(&plugin_bucket)?; diff --git a/docs/derives/AutoPlugin.md b/docs/derives/AutoPlugin.md index d6947171..fd6b540d 100644 --- a/docs/derives/AutoPlugin.md +++ b/docs/derives/AutoPlugin.md @@ -3,6 +3,7 @@ events, resources, and systems. # Parameters - `impl_plugin_trait` - Optional. When present, it automatically implements the Plugin trait. +- `default_plugin` - Optional (feature: `default_plugin`). Emits a default plugin alias so `auto_*` macros can omit `plugin = ...`. # Example ```rust @@ -15,4 +16,4 @@ struct MyPlugin; // Plugin will automatically implement the Plugin trait // and include all registered components, events, resources, etc. -``` \ No newline at end of file +``` diff --git a/tests/e2e/actions/auto_default_plugin.rs b/tests/e2e/actions/auto_default_plugin.rs new file mode 100644 index 00000000..b230d07c --- /dev/null +++ b/tests/e2e/actions/auto_default_plugin.rs @@ -0,0 +1,31 @@ +use bevy_app::prelude::*; +use bevy_auto_plugin::prelude::*; +use bevy_ecs::prelude::*; +use bevy_reflect::prelude::*; +use internal_test_proc_macro::xtest; +use internal_test_util::type_id_of; + +#[derive(AutoPlugin)] +#[auto_plugin(impl_plugin_trait, default_plugin)] +struct DefaultPlugin; + +#[auto_register_type] +#[derive(Reflect)] +struct DefaultedType; + +fn app() -> App { + let mut app = internal_test_util::create_minimal_app(); + app.add_plugins(DefaultPlugin); + app +} + +#[xtest] +fn test_auto_default_plugin_register_type() { + let app = app(); + let type_registry = app.world().resource::().0.clone(); + let type_registry = type_registry.read(); + assert!( + type_registry.contains(type_id_of::()), + "did not auto register DefaultedType" + ); +} diff --git a/tests/e2e/actions/mod.rs b/tests/e2e/actions/mod.rs index b83d3323..180bc3a6 100644 --- a/tests/e2e/actions/mod.rs +++ b/tests/e2e/actions/mod.rs @@ -16,6 +16,8 @@ mod auto_bind_plugin_after_build; mod auto_configure_system_set; 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_resource; mod auto_init_resource_generic; mod auto_init_state;