use crate::{Client, Event}; use async_trait::async_trait; use nohash_hasher::NoHashHasher; use std::{ any::{Any, TypeId}, collections::HashMap, hash::BuildHasherDefault, }; // kind of based on https://docs.rs/http/latest/src/http/extensions.rs.html /// A map of plugin ids to Plugin trait objects. The client stores this so we /// can keep the state for our plugins. /// /// If you're using azalea, you should generate this from the `plugins!` macro. #[derive(Clone)] pub struct Plugins { map: Option, BuildHasherDefault>>>, } impl Plugins { pub fn new() -> Self { Self { map: None } } 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)); } 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 IntoIterator for Plugins { type Item = Box; type IntoIter = std::vec::IntoIter; fn into_iter(self) -> Self::IntoIter { self.map .map(|map| map.into_iter().map(|(_, v)| v).collect::>()) .unwrap_or_default() .into_iter() } } /// Plugins can keep their own personal state, listen to events, and add new functions to Client. #[async_trait] pub trait Plugin: Send + Sync + PluginClone + Any + 'static { async fn handle(self: Box, event: Event, bot: Client); } /// An internal trait that allows Plugin to be cloned. #[doc(hidden)] pub trait PluginClone { fn clone_box(&self) -> Box; } impl PluginClone for T where T: 'static + Plugin + Clone, { fn clone_box(&self) -> Box { Box::new(self.clone()) } } impl Clone for Box { fn clone(&self) -> Self { self.clone_box() } }