diff --git a/crates/bevy_app/src/plugin_group.rs b/crates/bevy_app/src/plugin_group.rs index 22d174df9e..b41a78f067 100644 --- a/crates/bevy_app/src/plugin_group.rs +++ b/crates/bevy_app/src/plugin_group.rs @@ -66,13 +66,25 @@ impl PluginGroupBuilder { // Insert the new plugin as enabled, and removes its previous ordering if it was // already present fn upsert_plugin_state(&mut self, plugin: T, added_at_index: usize) { - if let Some(entry) = self.plugins.insert( + self.upsert_plugin_entry_state( TypeId::of::(), PluginEntry { plugin: Box::new(plugin), enabled: true, }, - ) { + added_at_index, + ); + } + + // Insert the new plugin entry as enabled, and removes its previous ordering if it was + // already present + fn upsert_plugin_entry_state( + &mut self, + key: TypeId, + plugin: PluginEntry, + added_at_index: usize, + ) { + if let Some(entry) = self.plugins.insert(key, plugin) { if entry.enabled { warn!( "You are replacing plugin '{}' that was not disabled.", @@ -83,7 +95,7 @@ impl PluginGroupBuilder { .order .iter() .enumerate() - .find(|(i, ty)| *i != added_at_index && **ty == TypeId::of::()) + .find(|(i, ty)| *i != added_at_index && **ty == key) .map(|(i, _)| i) { self.order.remove(to_remove); @@ -118,6 +130,26 @@ impl PluginGroupBuilder { self } + /// Adds a [`PluginGroup`] at the end of this [`PluginGroupBuilder`]. If the plugin was + /// already in the group, it is removed from its previous place. + pub fn add_group(mut self, group: impl PluginGroup) -> Self { + let Self { + mut plugins, order, .. + } = group.build(); + + for plugin_id in order { + self.upsert_plugin_entry_state( + plugin_id, + plugins.remove(&plugin_id).unwrap(), + self.order.len(), + ); + + self.order.push(plugin_id); + } + + self + } + /// Adds a [`Plugin`] in this [`PluginGroupBuilder`] before the plugin of type `Target`. /// If the plugin was already the group, it is removed from its previous place. There must /// be a plugin of type `Target` in the group or it will panic. @@ -335,4 +367,48 @@ mod tests { ] ); } + + #[test] + fn add_basic_subgroup() { + let group_a = PluginGroupBuilder::start::() + .add(PluginA) + .add(PluginB); + + let group_b = PluginGroupBuilder::start::() + .add_group(group_a) + .add(PluginC); + + assert_eq!( + group_b.order, + vec![ + std::any::TypeId::of::(), + std::any::TypeId::of::(), + std::any::TypeId::of::(), + ] + ); + } + + #[test] + fn add_conflicting_subgroup() { + let group_a = PluginGroupBuilder::start::() + .add(PluginA) + .add(PluginC); + + let group_b = PluginGroupBuilder::start::() + .add(PluginB) + .add(PluginC); + + let group = PluginGroupBuilder::start::() + .add_group(group_a) + .add_group(group_b); + + assert_eq!( + group.order, + vec![ + std::any::TypeId::of::(), + std::any::TypeId::of::(), + std::any::TypeId::of::(), + ] + ); + } }