bevy_plugin_builder/
macros.rs

1//! Declarative plugin registration macro implementation
2//!
3//! This module contains the main `define_plugin!` macro that generates
4//! Bevy plugin implementations from declarative syntax.
5
6/// Define a Bevy plugin declaratively, eliminating boilerplate registration code.
7///
8/// This macro takes a plugin name and a configuration block, then generates
9/// a complete `impl Plugin for PluginName` with all the specified registrations.
10///
11/// ## Supported Configuration Options
12///
13/// - `resources: [Type1, Type2]` - Initialize resources with `init_resource`
14/// - `events: [Event1, Event2]` - Register events with `add_event`
15/// - `plugins: [Plugin1, Plugin2]` - Add sub-plugins with `add_plugins`
16/// - `states: [State1]` - Initialize states with `init_state`
17/// - `sub_states: [SubState1]` - Add sub-states with `add_sub_state`
18/// - `reflect: [Type1, Type2]` - Register types for reflection
19/// - `startup: [system1, system2]` - Add startup systems
20/// - `update: [system3, system4]` - Add update systems (supports conditions/ordering)
21/// - `fixed_update: [system5]` - Add fixed update systems
22/// - `on_enter: { State::Variant => [system6] }` - Add state enter systems
23/// - `on_exit: { State::Variant => [system7] }` - Add state exit systems
24/// - `custom_init: |app| { ... }` - Custom initialization logic
25/// - `custom_finish: |app| { ... }` - Custom finish logic
26///
27/// ## Example
28///
29/// ```rust
30/// use bevy_plugin_builder::define_plugin;
31/// use bevy::prelude::*;
32///
33/// #[derive(Resource, Default)]
34/// struct MyResource;
35///
36/// #[derive(Event)]
37/// struct MyEvent;
38///
39/// fn setup_system() {}
40/// fn update_system() {}
41///
42/// define_plugin!(MyPlugin {
43///     resources: [MyResource],
44///     events: [MyEvent],
45///     startup: [setup_system],
46///     update: [update_system]
47/// });
48/// ```
49///
50/// This generates:
51///
52/// ```rust
53/// # use bevy::prelude::*;
54/// # #[derive(Resource, Default)]
55/// # struct MyResource;
56/// # #[derive(Event)]
57/// # struct MyEvent;
58/// # fn setup_system() {}
59/// # fn update_system() {}
60/// pub struct MyPlugin;
61///
62/// impl Plugin for MyPlugin {
63///     fn build(&self, app: &mut App) {
64///         app.init_resource::<MyResource>()
65///            .add_event::<MyEvent>()
66///            .add_systems(Startup, setup_system)
67///            .add_systems(Update, update_system);
68///     }
69/// }
70/// ```
71#[macro_export]
72macro_rules! define_plugin {
73    // Main entry point - plugin with configuration block
74    ($plugin_name:ident { $($config:tt)* }) => {
75        pub struct $plugin_name;
76
77        impl ::bevy::prelude::Plugin for $plugin_name {
78            fn build(&self, app: &mut ::bevy::prelude::App) {
79                $crate::define_plugin_internal!(app, $($config)*);
80            }
81
82            fn finish(&self, app: &mut ::bevy::prelude::App) {
83                $crate::define_plugin_finish!(app, $($config)*);
84            }
85        }
86    };
87}
88
89/// Internal macro for parsing and applying plugin configuration.
90/// This is separate from the main macro to allow for recursive parsing.
91#[macro_export]
92macro_rules! define_plugin_internal {
93    // Empty configuration (base case)
94    ($app:ident,) => {};
95
96    // Resources registration
97    ($app:ident, resources: [$($resource:ty),* $(,)?] $(, $($rest:tt)*)?) => {
98        $(
99            $app.init_resource::<$resource>();
100        )*
101        $crate::define_plugin_internal!($app, $($($rest)*)?);
102    };
103
104    // Events registration
105    ($app:ident, events: [$($event:ty),* $(,)?] $(, $($rest:tt)*)?) => {
106        $(
107            $app.add_event::<$event>();
108        )*
109        $crate::define_plugin_internal!($app, $($($rest)*)?);
110    };
111
112    // Sub-plugins registration
113    ($app:ident, plugins: [$($plugin:expr),* $(,)?] $(, $($rest:tt)*)?) => {
114        $(
115            $app.add_plugins($plugin);
116        )*
117        $crate::define_plugin_internal!($app, $($($rest)*)?);
118    };
119
120    // States registration
121    ($app:ident, states: [$($state:ty),* $(,)?] $(, $($rest:tt)*)?) => {
122        $(
123            $app.init_state::<$state>();
124        )*
125        $crate::define_plugin_internal!($app, $($($rest)*)?);
126    };
127
128    // Sub-states registration
129    ($app:ident, sub_states: [$($state:ty),* $(,)?] $(, $($rest:tt)*)?) => {
130        $(
131            $app.add_sub_state::<$state>();
132        )*
133        $crate::define_plugin_internal!($app, $($($rest)*)?);
134    };
135
136    // Reflection registration
137    ($app:ident, reflect: [$($reflect_type:ty),* $(,)?] $(, $($rest:tt)*)?) => {
138        $(
139            $app.register_type::<$reflect_type>();
140        )*
141        $crate::define_plugin_internal!($app, $($($rest)*)?);
142    };
143
144    // Startup systems
145    ($app:ident, startup: [$($system:expr),* $(,)?] $(, $($rest:tt)*)?) => {
146        $app.add_systems(
147            ::bevy::prelude::Startup,
148            ($($system,)*)
149        );
150        $crate::define_plugin_internal!($app, $($($rest)*)?);
151    };
152
153    // Update systems (supports complex system expressions)
154    ($app:ident, update: [$($system:expr),* $(,)?] $(, $($rest:tt)*)?) => {
155        $app.add_systems(
156            ::bevy::prelude::Update,
157            ($($system,)*)
158        );
159        $crate::define_plugin_internal!($app, $($($rest)*)?);
160    };
161
162    // FixedUpdate systems (supports complex system expressions)
163    ($app:ident, fixed_update: [$($system:expr),* $(,)?] $(, $($rest:tt)*)?) => {
164        $app.add_systems(
165            ::bevy::prelude::FixedUpdate,
166            ($($system,)*)
167        );
168        $crate::define_plugin_internal!($app, $($($rest)*)?);
169    };
170
171    // OnEnter systems with state mapping
172    ($app:ident, on_enter: { $($state:expr => [$($system:expr),* $(,)?]),* $(,)? } $(, $($rest:tt)*)?) => {
173        $(
174            $app.add_systems(
175                ::bevy::prelude::OnEnter($state),
176                ($($system,)*)
177            );
178        )*
179        $crate::define_plugin_internal!($app, $($($rest)*)?);
180    };
181
182    // OnExit systems with state mapping
183    ($app:ident, on_exit: { $($state:expr => [$($system:expr),* $(,)?]),* $(,)? } $(, $($rest:tt)*)?) => {
184        $(
185            $app.add_systems(
186                ::bevy::prelude::OnExit($state),
187                ($($system,)*)
188            );
189        )*
190        $crate::define_plugin_internal!($app, $($($rest)*)?);
191    };
192
193    // Custom initialization (for complex setup)
194    ($app:ident, custom_init: $init_fn:expr $(, $($rest:tt)*)?) => {
195        $init_fn($app);
196        $crate::define_plugin_internal!($app, $($($rest)*)?);
197    };
198
199    // Custom finish handler (skip in build, handled in finish)
200    ($app:ident, custom_finish: $finish_fn:expr $(, $($rest:tt)*)?) => {
201        $crate::define_plugin_internal!($app, $($($rest)*)?);
202    };
203
204    // Error case - unrecognized configuration
205    ($app:ident, $unknown:tt $($rest:tt)*) => {
206        compile_error!(concat!(
207            "Unknown plugin configuration option: ",
208            stringify!($unknown),
209            "\nSupported options: resources, events, plugins, states, sub_states, reflect, startup, update, fixed_update, on_enter, on_exit, custom_init, custom_finish"
210        ));
211    };
212}
213
214/// Macro for handling Plugin finish() method configuration
215#[macro_export]
216macro_rules! define_plugin_finish {
217    // Empty configuration (base case) - default finish does nothing
218    ($app:ident,) => {};
219
220    // Skip all standard configurations (only process custom_finish)
221    ($app:ident, resources: [$($resource:ty),* $(,)?] $(, $($rest:tt)*)?) => {
222        $crate::define_plugin_finish!($app, $($($rest)*)?);
223    };
224    ($app:ident, events: [$($event:ty),* $(,)?] $(, $($rest:tt)*)?) => {
225        $crate::define_plugin_finish!($app, $($($rest)*)?);
226    };
227    ($app:ident, plugins: [$($plugin:expr),* $(,)?] $(, $($rest:tt)*)?) => {
228        $crate::define_plugin_finish!($app, $($($rest)*)?);
229    };
230    ($app:ident, startup: [$($system:expr),* $(,)?] $(, $($rest:tt)*)?) => {
231        $crate::define_plugin_finish!($app, $($($rest)*)?);
232    };
233    ($app:ident, update: [$($system:expr),* $(,)?] $(, $($rest:tt)*)?) => {
234        $crate::define_plugin_finish!($app, $($($rest)*)?);
235    };
236    ($app:ident, fixed_update: [$($system:expr),* $(,)?] $(, $($rest:tt)*)?) => {
237        $crate::define_plugin_finish!($app, $($($rest)*)?);
238    };
239    ($app:ident, on_enter: { $($state:expr => [$($system:expr),* $(,)?]),* $(,)? } $(, $($rest:tt)*)?) => {
240        $crate::define_plugin_finish!($app, $($($rest)*)?);
241    };
242    ($app:ident, on_exit: { $($state:expr => [$($system:expr),* $(,)?]),* $(,)? } $(, $($rest:tt)*)?) => {
243        $crate::define_plugin_finish!($app, $($($rest)*)?);
244    };
245    ($app:ident, custom_init: $init_fn:expr $(, $($rest:tt)*)?) => {
246        $crate::define_plugin_finish!($app, $($($rest)*)?);
247    };
248    ($app:ident, states: [$($state:ty),* $(,)?] $(, $($rest:tt)*)?) => {
249        $crate::define_plugin_finish!($app, $($($rest)*)?);
250    };
251    ($app:ident, sub_states: [$($state:ty),* $(,)?] $(, $($rest:tt)*)?) => {
252        $crate::define_plugin_finish!($app, $($($rest)*)?);
253    };
254    ($app:ident, reflect: [$($reflect_type:ty),* $(,)?] $(, $($rest:tt)*)?) => {
255        $crate::define_plugin_finish!($app, $($($rest)*)?);
256    };
257
258    // Custom finish - this is what we're looking for!
259    ($app:ident, custom_finish: $finish_fn:expr $(, $($rest:tt)*)?) => {
260        $finish_fn($app);
261        $crate::define_plugin_finish!($app, $($($rest)*)?);
262    };
263
264    // Handle all other configurations (catch-all for unknown tokens)
265    ($app:ident, $unknown:tt $($rest:tt)*) => {
266        $crate::define_plugin_finish!($app, $($rest)*);
267    };
268}
269
270// The macro is exported at crate root via #[macro_export]