use crate::{Swarm, SwarmEvent}; use async_trait::async_trait; use nohash_hasher::NoHashHasher; use std::{ any::{Any, TypeId}, collections::HashMap, hash::BuildHasherDefault, }; type U64Hasher = BuildHasherDefault>; // kind of based on https://docs.rs/http/latest/src/http/extensions.rs.html /// A map of plugin ids to [`SwarmPlugin`] trait objects. The client stores /// this so we can keep the state for our [`Swarm`] plugins. /// /// If you're using azalea, you should generate this from the `swarm_plugins!` macro. #[derive(Clone, Default)] pub struct SwarmPlugins { map: Option>, U64Hasher>>, } #[derive(Clone)] pub struct SwarmPluginStates { map: Option>, U64Hasher>>, } impl SwarmPluginStates { pub fn get>(&self) -> Option<&T> { self.map .as_ref() .and_then(|map| map.get(&TypeId::of::())) .and_then(|boxed| (boxed.as_ref() as &dyn Any).downcast_ref::()) } } impl SwarmPlugins where S: 'static, { /// Create a new empty set of plugins. pub fn new() -> Self { Self { map: None } } /// Add a new plugin to this set. pub fn add>(&mut self, plugin: T) { if self.map.is_none() { self.map = Some(HashMap::with_hasher(BuildHasherDefault::default())); } self.map .as_mut() .unwrap() .insert(TypeId::of::(), Box::new(plugin)); } /// Build our plugin states from this set of plugins. Note that if you're /// using `azalea` you'll probably never need to use this as it's called /// for you. pub fn build(self) -> SwarmPluginStates { if self.map.is_none() { return SwarmPluginStates { map: None }; } let mut map = HashMap::with_hasher(BuildHasherDefault::default()); for (id, plugin) in self.map.unwrap().into_iter() { map.insert(id, plugin.build()); } SwarmPluginStates { map: Some(map) } } } impl IntoIterator for SwarmPluginStates { type Item = Box>; type IntoIter = std::vec::IntoIter; /// Iterate over the plugin states. fn into_iter(self) -> Self::IntoIter { self.map .map(|map| map.into_values().collect::>()) .unwrap_or_default() .into_iter() } } /// A `SwarmPluginState` keeps the current state of a plugin for a client. All /// the fields must be atomic. Unique `SwarmPluginState`s are built from /// [`SwarmPlugin`]s. #[async_trait] pub trait SwarmPluginState: Send + Sync + SwarmPluginStateClone + Any + 'static { async fn handle(self: Box, event: SwarmEvent, swarm: Swarm); } /// Swarm plugins can keep their own personal state ([`SwarmPluginState`]), /// listen to [`SwarmEvent`]s, and add new functions to [`Swarm`]. pub trait SwarmPlugin: Send + Sync + SwarmPluginClone + Any + 'static { fn build(&self) -> Box>; } /// An internal trait that allows SwarmPluginState to be cloned. #[doc(hidden)] pub trait SwarmPluginStateClone { fn clone_box(&self) -> Box>; } impl SwarmPluginStateClone for T where T: 'static + SwarmPluginState + Clone, { fn clone_box(&self) -> Box> { Box::new(self.clone()) } } impl Clone for Box> { fn clone(&self) -> Self { self.clone_box() } } /// An internal trait that allows SwarmPlugin to be cloned. #[doc(hidden)] pub trait SwarmPluginClone { fn clone_box(&self) -> Box>; } impl SwarmPluginClone for T where T: 'static + SwarmPlugin + Clone, { fn clone_box(&self) -> Box> { Box::new(self.clone()) } } impl Clone for Box> { fn clone(&self) -> Self { self.clone_box() } }