aboutsummaryrefslogtreecommitdiff
path: root/azalea-client/src/plugins.rs
diff options
context:
space:
mode:
Diffstat (limited to 'azalea-client/src/plugins.rs')
-rw-r--r--azalea-client/src/plugins.rs106
1 files changed, 85 insertions, 21 deletions
diff --git a/azalea-client/src/plugins.rs b/azalea-client/src/plugins.rs
index 150d5960..93641906 100644
--- a/azalea-client/src/plugins.rs
+++ b/azalea-client/src/plugins.rs
@@ -10,42 +10,65 @@ use std::{
type U64Hasher = BuildHasherDefault<NoHashHasher<u64>>;
// 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.
+#[derive(Clone, Default)]
+pub struct PluginStates {
+ map: Option<HashMap<TypeId, Box<dyn PluginState>, U64Hasher>>,
+}
+
+/// A map of PluginState TypeIds to AnyPlugin objects. This can then be built
+/// into a [`PluginStates`] object to get a fresh new state based on this
+/// plugin.
///
-/// If you're using azalea, you should generate this from the `plugins!` macro.
+/// If you're using the azalea crate, you should generate this from the
+/// `plugins!` macro.
#[derive(Clone, Default)]
pub struct Plugins {
- map: Option<HashMap<TypeId, Box<dyn Plugin>, U64Hasher>>,
+ map: Option<HashMap<TypeId, Box<dyn AnyPlugin>, U64Hasher>>,
+}
+
+impl PluginStates {
+ pub fn get<T: PluginState>(&self) -> Option<&T> {
+ self.map
+ .as_ref()
+ .and_then(|map| map.get(&TypeId::of::<T>()))
+ .and_then(|boxed| (boxed.as_ref() as &dyn Any).downcast_ref::<T>())
+ }
}
impl Plugins {
+ /// Create a new empty set of plugins.
pub fn new() -> Self {
Self::default()
}
- pub fn add<T: Plugin>(&mut self, plugin: T) {
+ /// Add a new plugin to this set.
+ pub fn add<T: Plugin + Clone>(&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::<T>(), Box::new(plugin));
+ .insert(TypeId::of::<T::State>(), Box::new(plugin));
}
- pub fn get<T: Plugin>(&self) -> Option<&T> {
- self.map
- .as_ref()
- .and_then(|map| map.get(&TypeId::of::<T>()))
- .and_then(|boxed| (boxed.as_ref() as &dyn Any).downcast_ref::<T>())
+ /// 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) -> PluginStates {
+ let mut map = HashMap::with_hasher(BuildHasherDefault::default());
+ for (id, plugin) in self.map.unwrap().into_iter() {
+ map.insert(id, plugin.build());
+ }
+ PluginStates { map: Some(map) }
}
}
-impl IntoIterator for Plugins {
- type Item = Box<dyn Plugin>;
+impl IntoIterator for PluginStates {
+ type Item = Box<dyn PluginState>;
type IntoIter = std::vec::IntoIter<Self::Item>;
+ /// Iterate over the plugin states.
fn into_iter(self) -> Self::IntoIter {
self.map
.map(|map| map.into_values().collect::<Vec<_>>())
@@ -54,26 +77,67 @@ impl IntoIterator for Plugins {
}
}
-/// Plugins can keep their own personal state, listen to events, and add new functions to Client.
+/// A `PluginState` keeps the current state of a plugin for a client. All the
+/// fields must be atomic. Unique `PluginState`s are built from [`Plugin`]s.
#[async_trait]
-pub trait Plugin: Send + Sync + PluginClone + Any + 'static {
+pub trait PluginState: Send + Sync + PluginStateClone + Any + 'static {
async fn handle(self: Box<Self>, event: Event, bot: Client);
}
-/// An internal trait that allows Plugin to be cloned.
+/// Plugins can keep their own personal state, listen to [`Event`]s, and add
+/// new functions to [`Client`].
+pub trait Plugin: Send + Sync + Any + 'static {
+ type State: PluginState;
+
+ fn build(&self) -> Self::State;
+}
+
+/// AnyPlugin is basically a Plugin but without the State associated type
+/// it has to exist so we can do a hashmap with Box<dyn AnyPlugin>
+#[doc(hidden)]
+pub trait AnyPlugin: Send + Sync + Any + AnyPluginClone + 'static {
+ fn build(&self) -> Box<dyn PluginState>;
+}
+
+impl<S: PluginState, B: Plugin<State = S> + Clone> AnyPlugin for B {
+ fn build(&self) -> Box<dyn PluginState> {
+ Box::new(self.build())
+ }
+}
+
+/// An internal trait that allows PluginState to be cloned.
+#[doc(hidden)]
+pub trait PluginStateClone {
+ fn clone_box(&self) -> Box<dyn PluginState>;
+}
+impl<T> PluginStateClone for T
+where
+ T: 'static + PluginState + Clone,
+{
+ fn clone_box(&self) -> Box<dyn PluginState> {
+ Box::new(self.clone())
+ }
+}
+impl Clone for Box<dyn PluginState> {
+ fn clone(&self) -> Self {
+ self.clone_box()
+ }
+}
+
+/// An internal trait that allows AnyPlugin to be cloned.
#[doc(hidden)]
-pub trait PluginClone {
- fn clone_box(&self) -> Box<dyn Plugin>;
+pub trait AnyPluginClone {
+ fn clone_box(&self) -> Box<dyn AnyPlugin>;
}
-impl<T> PluginClone for T
+impl<T> AnyPluginClone for T
where
T: 'static + Plugin + Clone,
{
- fn clone_box(&self) -> Box<dyn Plugin> {
+ fn clone_box(&self) -> Box<dyn AnyPlugin> {
Box::new(self.clone())
}
}
-impl Clone for Box<dyn Plugin> {
+impl Clone for Box<dyn AnyPlugin> {
fn clone(&self) -> Self {
self.clone_box()
}