use crate::{App, Plugin}; use bevy_utils::{tracing::debug, HashMap}; use std::any::TypeId; /// Combines multiple [`Plugin`]s into a single unit. pub trait PluginGroup { /// Configures the [`Plugin`]s that are to be added. fn build(&mut self, group: &mut PluginGroupBuilder); } struct PluginEntry { plugin: Box, enabled: bool, } /// Facilitates the creation and configuration of a [`PluginGroup`]. /// Provides a build ordering to ensure that [`Plugin`]s which produce/require a [`Resource`](bevy_ecs::system::Resource) /// are built before/after dependent/depending [`Plugin`]s. #[derive(Default)] pub struct PluginGroupBuilder { plugins: HashMap, order: Vec, } impl PluginGroupBuilder { /// Finds the index of a target [`Plugin`]. Panics if the target's [`TypeId`] is not found. fn index_of(&mut self) -> usize { let index = self .order .iter() .position(|&ty| ty == TypeId::of::()); match index { Some(i) => i, None => panic!( "Plugin does not exist in group: {}.", std::any::type_name::() ), } } /// Appends a [`Plugin`] to the [`PluginGroupBuilder`]. pub fn add(&mut self, plugin: T) -> &mut Self { self.order.push(TypeId::of::()); self.plugins.insert( TypeId::of::(), PluginEntry { plugin: Box::new(plugin), enabled: true, }, ); self } /// Configures a [`Plugin`] to be built before another plugin. pub fn add_before(&mut self, plugin: T) -> &mut Self { let target_index = self.index_of::(); self.order.insert(target_index, TypeId::of::()); self.plugins.insert( TypeId::of::(), PluginEntry { plugin: Box::new(plugin), enabled: true, }, ); self } /// Configures a [`Plugin`] to be built after another plugin. pub fn add_after(&mut self, plugin: T) -> &mut Self { let target_index = self.index_of::(); self.order.insert(target_index + 1, TypeId::of::()); self.plugins.insert( TypeId::of::(), PluginEntry { plugin: Box::new(plugin), enabled: true, }, ); self } /// Enables a [`Plugin`]. /// /// [`Plugin`]s within a [`PluginGroup`] are enabled by default. This function is used to /// opt back in to a [`Plugin`] after [disabling](Self::disable) it. pub fn enable(&mut self) -> &mut Self { let mut plugin_entry = self .plugins .get_mut(&TypeId::of::()) .expect("Cannot enable a plugin that does not exist."); plugin_entry.enabled = true; self } /// Disables a [`Plugin`], preventing it from being added to the [`App`] with the rest of the [`PluginGroup`]. pub fn disable(&mut self) -> &mut Self { let mut plugin_entry = self .plugins .get_mut(&TypeId::of::()) .expect("Cannot disable a plugin that does not exist."); plugin_entry.enabled = false; self } /// Consumes the [`PluginGroupBuilder`] and [builds](Plugin::build) the contained [`Plugin`]s. pub fn finish(self, app: &mut App) { for ty in self.order.iter() { if let Some(entry) = self.plugins.get(ty) { if entry.enabled { debug!("added plugin: {}", entry.plugin.name()); entry.plugin.build(app); } } } } }