Skip to content

Commit

Permalink
plugin_group! macro (adopted) (#14339)
Browse files Browse the repository at this point in the history
# Objective

- Adopted from #11460.
- Closes #7332.
- The documentation for `DefaultPlugins` and `MinimalPlugins` frequently
goes out of date because it is not .

## Solution

- Create a macro, `plugin_group!`, to automatically create
`PluginGroup`s and document them.

## Testing

- Run `cargo-expand` on the generated code for `DefaultPlugins` and
`MinimalPlugins`.
- Try creating a custom plugin group with the macro.

---

## Showcase

- You can now define custom `PluginGroup`s using the `plugin_group!`
macro.

```rust
plugin_group! {
    /// My really cool plugic group!
    pub struct MyPluginGroup {
        physics:::PhysicsPlugin,
        rendering:::RenderingPlugin,
        ui:::UiPlugin,
    }
}
```

<details>
  <summary>Expanded output</summary>

```rust
/// My really cool plugic group!
///
/// - [`PhysicsPlugin`](physics::PhysicsPlugin)
/// - [`RenderingPlugin`](rendering::RenderingPlugin)
/// - [`UiPlugin`](ui::UiPlugin)
pub struct MyPluginGroup;
impl ::bevy_app::PluginGroup for MyPluginGroup {
    fn build(self) -> ::bevy_app::PluginGroupBuilder {
        let mut group = ::bevy_app::PluginGroupBuilder::start::<Self>();
        {
            const _: () = {
                const fn check_default<T: Default>() {}
                check_default::<physics::PhysicsPlugin>();
            };
            group = group.add(<physics::PhysicsPlugin>::default());
        }
        {
            const _: () = {
                const fn check_default<T: Default>() {}
                check_default::<rendering::RenderingPlugin>();
            };
            group = group.add(<rendering::RenderingPlugin>::default());
        }
        {
            const _: () = {
                const fn check_default<T: Default>() {}
                check_default::<ui::UiPlugin>();
            };
            group = group.add(<ui::UiPlugin>::default());
        }
        group
    }
}
```

</details>

---------

Co-authored-by: Doonv <[email protected]>
Co-authored-by: Mateusz Wachowiak <[email protected]>
  • Loading branch information
3 people authored Jul 16, 2024
1 parent 9a1a84d commit c3057d4
Show file tree
Hide file tree
Showing 7 changed files with 228 additions and 180 deletions.
1 change: 1 addition & 0 deletions crates/bevy_a11y/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ pub enum AccessibilitySystem {
}

/// Plugin managing non-GUI aspects of integrating with accessibility APIs.
#[derive(Default)]
pub struct AccessibilityPlugin;

impl Plugin for AccessibilityPlugin {
Expand Down
152 changes: 152 additions & 0 deletions crates/bevy_app/src/plugin_group.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,159 @@ use crate::{App, AppError, Plugin};
use bevy_utils::{tracing::debug, tracing::warn, TypeIdMap};
use std::any::TypeId;

/// A macro for generating a well-documented [`PluginGroup`] from a list of [`Plugin`] paths.
///
/// Every plugin must implement the [`Default`] trait.
///
/// # Example
///
/// ```
/// # use bevy_app::*;
/// #
/// # mod velocity {
/// # use bevy_app::*;
/// # #[derive(Default)]
/// # pub struct VelocityPlugin;
/// # impl Plugin for VelocityPlugin { fn build(&self, _: &mut App) {} }
/// # }
/// #
/// # mod collision {
/// # pub mod capsule {
/// # use bevy_app::*;
/// # #[derive(Default)]
/// # pub struct CapsuleCollisionPlugin;
/// # impl Plugin for CapsuleCollisionPlugin { fn build(&self, _: &mut App) {} }
/// # }
/// # }
/// #
/// # #[derive(Default)]
/// # pub struct TickratePlugin;
/// # impl Plugin for TickratePlugin { fn build(&self, _: &mut App) {} }
/// #
/// # mod features {
/// # use bevy_app::*;
/// # #[derive(Default)]
/// # pub struct ForcePlugin;
/// # impl Plugin for ForcePlugin { fn build(&self, _: &mut App) {} }
/// # }
/// #
/// # mod web {
/// # use bevy_app::*;
/// # #[derive(Default)]
/// # pub struct WebCompatibilityPlugin;
/// # impl Plugin for WebCompatibilityPlugin { fn build(&self, _: &mut App) {} }
/// # }
/// #
/// # mod internal {
/// # use bevy_app::*;
/// # #[derive(Default)]
/// # pub struct InternalPlugin;
/// # impl Plugin for InternalPlugin { fn build(&self, _: &mut App) {} }
/// # }
/// #
/// plugin_group! {
/// /// Doc comments and annotations are supported: they will be added to the generated plugin
/// /// group.
/// #[derive(Debug)]
/// pub struct PhysicsPlugins {
/// // If referencing a plugin within the same module, you must prefix it with a colon `:`.
/// :TickratePlugin,
/// // If referencing a plugin within a different module, there must be three colons `:::`
/// // between the final module and the plugin name.
/// collision::capsule:::CapsuleCollisionPlugin,
/// velocity:::VelocityPlugin,
/// // If you feature-flag a plugin, it will be automatically documented. There can only be
/// // one automatically documented feature flag, and it must be first. All other
/// // `#[cfg()]` attributes must be wrapped by `#[custom()]`.
/// #[cfg(feature = "external_forces")]
/// features:::ForcePlugin,
/// // More complicated `#[cfg()]`s and annotations are not supported by automatic doc
/// // generation, in which case you must wrap it in `#[custom()]`.
/// #[custom(cfg(target_arch = "wasm32"))]
/// web:::WebCompatibilityPlugin,
/// // You can hide plugins from documentation. Due to macro limitations, hidden plugins
/// // must be last.
/// #[doc(hidden)]
/// internal:::InternalPlugin
/// }
/// /// You may add doc comments after the plugin group as well. They will be appended after
/// /// the documented list of plugins.
/// }
/// ```
#[macro_export]
macro_rules! plugin_group {
{
$(#[$group_meta:meta])*
$vis:vis struct $group:ident {
$(
$(#[cfg(feature = $plugin_feature:literal)])?
$(#[custom($plugin_meta:meta)])*
$($plugin_path:ident::)* : $plugin_name:ident
),*
$(
$(,)?$(
#[doc(hidden)]
$(#[cfg(feature = $hidden_plugin_feature:literal)])?
$(#[custom($hidden_plugin_meta:meta)])*
$($hidden_plugin_path:ident::)* : $hidden_plugin_name:ident
),+
)?

$(,)?
}
$($(#[doc = $post_doc:literal])+)?
} => {
$(#[$group_meta])*
///
$(#[doc = concat!(
" - [`", stringify!($plugin_name), "`](" $(, stringify!($plugin_path), "::")*, stringify!($plugin_name), ")"
$(, " - with feature `", $plugin_feature, "`")?
)])*
$(
///
$(#[doc = $post_doc])+
)?
$vis struct $group;

impl $crate::PluginGroup for $group {
fn build(self) -> $crate::PluginGroupBuilder {
let mut group = $crate::PluginGroupBuilder::start::<Self>();

$(
$(#[cfg(feature = $plugin_feature)])?
$(#[$plugin_meta])*
{
const _: () = {
const fn check_default<T: Default>() {}
check_default::<$($plugin_path::)*$plugin_name>();
};

group = group.add(<$($plugin_path::)*$plugin_name>::default());
}
)*
$($(
$(#[cfg(feature = $hidden_plugin_feature)])?
$(#[$hidden_plugin_meta])*
{
const _: () = {
const fn check_default<T: Default>() {}
check_default::<$($hidden_plugin_path::)*$hidden_plugin_name>();
};

group = group.add(<$($hidden_plugin_path::)*$hidden_plugin_name>::default());
}
)+)?

group
}
}
};
}

/// Combines multiple [`Plugin`]s into a single unit.
///
/// If you want an easier, but slightly more restrictive, method of implementing this trait, you
/// may be interested in the [`plugin_group!`] macro.
pub trait PluginGroup: Sized {
/// Configures the [`Plugin`]s that are to be added.
fn build(self) -> PluginGroupBuilder;
Expand Down
1 change: 1 addition & 0 deletions crates/bevy_dev_tools/src/ci_testing/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ use std::time::Duration;
/// This plugin is included within `DefaultPlugins` and `MinimalPlugins` when the `bevy_ci_testing`
/// feature is enabled. It is recommended to only used this plugin during testing (manual or
/// automatic), and disable it during regular development and for production builds.
#[derive(Default)]
pub struct CiTestingPlugin;

impl Plugin for CiTestingPlugin {
Expand Down
1 change: 1 addition & 0 deletions crates/bevy_dev_tools/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ pub mod states;
///
/// Note: The third method is not recommended, as it requires you to remove the feature before
/// creating a build for release to the public.
#[derive(Default)]
pub struct DevToolsPlugin;

impl Plugin for DevToolsPlugin {
Expand Down
1 change: 1 addition & 0 deletions crates/bevy_gizmos/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ const LINE_JOINT_SHADER_HANDLE: Handle<Shader> = Handle::weak_from_u128(11627807
/// A [`Plugin`] that provides an immediate mode drawing api for visual debugging.
///
/// Requires to be loaded after [`PbrPlugin`](bevy_pbr::PbrPlugin) or [`SpritePlugin`](bevy_sprite::SpritePlugin).
#[derive(Default)]
pub struct GizmoPlugin;

impl Plugin for GizmoPlugin {
Expand Down
Loading

0 comments on commit c3057d4

Please sign in to comment.