abstract plugin logic into PluginContainer

lumi created

Change summary

src/client.rs    | 27 +++++++-----------
src/component.rs | 33 ++++++++--------------
src/plugin.rs    | 71 +++++++++++++++++++++++++++++++++++++++++++++++--
3 files changed, 90 insertions(+), 41 deletions(-)

Detailed changes

src/client.rs 🔗

@@ -3,7 +3,7 @@ use jid::Jid;
 use transport::{Transport, SslTransport};
 use error::Error;
 use ns;
-use plugin::{Plugin, PluginInit, PluginProxyBinding};
+use plugin::{Plugin, PluginInit, PluginProxyBinding, PluginContainer, PluginRef};
 use connection::{Connection, C2S};
 use sasl::client::Mechanism as SaslMechanism;
 use sasl::client::mechanisms::{Plain, Scram};
@@ -21,9 +21,7 @@ use xml::reader::XmlEvent as ReaderEvent;
 
 use std::sync::{Mutex, Arc};
 
-use std::collections::{HashSet, HashMap};
-
-use std::any::TypeId;
+use std::collections::HashSet;
 
 /// Struct that should be moved somewhere else and cleaned up.
 #[derive(Debug)]
@@ -81,11 +79,12 @@ impl ClientBuilder {
         let mut credentials = self.credentials;
         credentials.channel_binding = transport.channel_bind();
         let transport = Arc::new(Mutex::new(transport));
+        let plugin_container = Arc::new(PluginContainer::new());
         let mut client = Client {
             jid: self.jid,
             transport: transport.clone(),
-            plugins: HashMap::new(),
-            binding: PluginProxyBinding::new(dispatcher.clone()),
+            binding: PluginProxyBinding::new(dispatcher.clone(), plugin_container.clone()),
+            plugin_container: plugin_container,
             dispatcher: dispatcher,
         };
         client.dispatcher.register(Priority::Default, move |evt: &SendElement| {
@@ -103,7 +102,7 @@ impl ClientBuilder {
 pub struct Client {
     jid: Jid,
     transport: Arc<Mutex<SslTransport>>,
-    plugins: HashMap<TypeId, Arc<Plugin>>,
+    plugin_container: Arc<PluginContainer>,
     binding: PluginProxyBinding,
     dispatcher: Arc<Dispatcher>,
 }
@@ -118,11 +117,9 @@ impl Client {
     pub fn register_plugin<P: Plugin + PluginInit + 'static>(&mut self, mut plugin: P) {
         let binding = self.binding.clone();
         plugin.bind(binding);
-        let p = Arc::new(plugin) as Arc<Plugin>;
+        let p = Arc::new(plugin);
         P::init(&self.dispatcher, p.clone());
-        if self.plugins.insert(TypeId::of::<P>(), p).is_some() {
-            panic!("registering a plugin that's already registered");
-        }
+        self.plugin_container.register(p);
     }
 
     pub fn register_handler<E, F>(&mut self, pri: Priority, func: F)
@@ -133,12 +130,8 @@ impl Client {
     }
 
     /// Returns the plugin given by the type parameter, if it exists, else panics.
-    pub fn plugin<P: Plugin>(&self) -> &P {
-        self.plugins.get(&TypeId::of::<P>())
-                    .expect("the requested plugin was not registered")
-                    .as_any()
-                    .downcast_ref::<P>()
-                    .expect("plugin downcast failure (should not happen!!)")
+    pub fn plugin<P: Plugin>(&self) -> PluginRef<P> {
+        self.plugin_container.get::<P>().unwrap()
     }
 
     /// Returns the next event and flush the send queue.

src/component.rs 🔗

@@ -3,7 +3,7 @@ use jid::Jid;
 use transport::{Transport, PlainTransport};
 use error::Error;
 use ns;
-use plugin::{Plugin, PluginInit, PluginProxyBinding};
+use plugin::{Plugin, PluginInit, PluginProxyBinding, PluginContainer, PluginRef};
 use event::{Dispatcher, ReceiveElement, SendElement, Propagation, Priority, Event};
 use connection::{Connection, Component2S};
 use sha_1::{Sha1, Digest};
@@ -15,10 +15,6 @@ use xml::reader::XmlEvent as ReaderEvent;
 use std::fmt::Write;
 use std::sync::{Mutex, Arc};
 
-use std::collections::HashMap;
-
-use std::any::TypeId;
-
 /// A builder for `Component`s.
 pub struct ComponentBuilder {
     jid: Jid,
@@ -63,11 +59,12 @@ impl ComponentBuilder {
         Component2S::init(&mut transport, &self.jid.domain, "stream_opening")?;
         let dispatcher = Arc::new(Dispatcher::new());
         let transport = Arc::new(Mutex::new(transport));
+        let plugin_container = Arc::new(PluginContainer::new());
         let mut component = Component {
             jid: self.jid,
             transport: transport.clone(),
-            plugins: HashMap::new(),
-            binding: PluginProxyBinding::new(dispatcher.clone()),
+            binding: PluginProxyBinding::new(dispatcher.clone(), plugin_container.clone()),
+            plugin_container: plugin_container,
             dispatcher: dispatcher,
         };
         component.dispatcher.register(Priority::Default, move |evt: &SendElement| {
@@ -84,7 +81,7 @@ impl ComponentBuilder {
 pub struct Component {
     jid: Jid,
     transport: Arc<Mutex<PlainTransport>>,
-    plugins: HashMap<TypeId, Arc<Plugin>>,
+    plugin_container: Arc<PluginContainer>,
     binding: PluginProxyBinding,
     dispatcher: Arc<Dispatcher>,
 }
@@ -99,20 +96,9 @@ impl Component {
     pub fn register_plugin<P: Plugin + PluginInit + 'static>(&mut self, mut plugin: P) {
         let binding = self.binding.clone();
         plugin.bind(binding);
-        let p = Arc::new(plugin) as Arc<Plugin>;
+        let p = Arc::new(plugin);
         P::init(&self.dispatcher, p.clone());
-        if self.plugins.insert(TypeId::of::<P>(), p).is_some() {
-            panic!("registering a plugin that's already registered");
-        }
-    }
-
-    /// Returns the plugin given by the type parameter, if it exists, else panics.
-    pub fn plugin<P: Plugin>(&self) -> &P {
-        self.plugins.get(&TypeId::of::<P>())
-                    .expect("the requested plugin was not registered")
-                    .as_any()
-                    .downcast_ref::<P>()
-                    .expect("plugin downcast failure (should not happen!!)")
+        self.plugin_container.register(p);
     }
 
     pub fn register_handler<E, F>(&mut self, pri: Priority, func: F)
@@ -122,6 +108,11 @@ impl Component {
         self.dispatcher.register(pri, func);
     }
 
+    /// Returns the plugin given by the type parameter, if it exists, else panics.
+    pub fn plugin<P: Plugin>(&self) -> PluginRef<P> {
+        self.plugin_container.get::<P>().unwrap()
+    }
+
     /// Returns the next event and flush the send queue.
     pub fn main(&mut self) -> Result<(), Error> {
         self.dispatcher.flush_all();

src/plugin.rs 🔗

@@ -2,23 +2,81 @@
 
 use event::{Event, Dispatcher, SendElement, Priority, Propagation};
 
-use std::any::Any;
+use std::any::{Any, TypeId};
 
-use std::sync::Arc;
+use std::collections::HashMap;
+
+use std::sync::{RwLock, Arc};
+
+use std::marker::PhantomData;
+
+use std::ops::Deref;
+
+use std::convert::AsRef;
 
 use std::mem;
 
 use minidom::Element;
 
+pub struct PluginContainer {
+    plugins: RwLock<HashMap<TypeId, Arc<Plugin>>>,
+}
+
+impl PluginContainer {
+    pub fn new() -> PluginContainer {
+        PluginContainer {
+            plugins: RwLock::new(HashMap::new()),
+        }
+    }
+
+    pub fn register<P: Plugin + 'static>(&self, plugin: Arc<P>) {
+        let mut guard = self.plugins.write().unwrap();
+        if guard.insert(TypeId::of::<P>(), plugin as Arc<Plugin>).is_some() {
+            panic!("registering a plugin that's already registered");
+        }
+    }
+
+    pub fn get<P: Plugin>(&self) -> Option<PluginRef<P>> {
+        let guard = self.plugins.read().unwrap();
+        let arc = guard.get(&TypeId::of::<P>());
+        arc.map(|arc| PluginRef {
+            inner: arc.clone(),
+            _marker: PhantomData
+        })
+    }
+}
+
+#[derive(Clone)]
+pub struct PluginRef<P: Plugin> {
+    inner: Arc<Plugin>,
+    _marker: PhantomData<P>,
+}
+
+impl<P: Plugin> Deref for PluginRef<P> {
+    type Target = P;
+
+    fn deref(&self) -> &P {
+        self.inner.as_any().downcast_ref::<P>().expect("plugin downcast failure")
+    }
+}
+
+impl<P: Plugin> AsRef<P> for PluginRef<P> {
+    fn as_ref(&self) -> &P {
+        self.inner.as_any().downcast_ref::<P>().expect("plugin downcast failure")
+    }
+}
+
 #[derive(Clone)]
 pub struct PluginProxyBinding {
     dispatcher: Arc<Dispatcher>,
+    plugin_container: Arc<PluginContainer>,
 }
 
 impl PluginProxyBinding {
-    pub fn new(dispatcher: Arc<Dispatcher>) -> PluginProxyBinding {
+    pub fn new(dispatcher: Arc<Dispatcher>, plugin_container: Arc<PluginContainer>) -> PluginProxyBinding {
         PluginProxyBinding {
             dispatcher: dispatcher,
+            plugin_container: plugin_container,
         }
     }
 }
@@ -72,6 +130,13 @@ impl PluginProxy {
         });
     }
 
+    /// Tries to get another plugin.
+    pub fn plugin<P: Plugin>(&self) -> Option<PluginRef<P>> {
+        self.with_binding(|binding| {
+            binding.plugin_container.get::<P>()
+        })
+    }
+
     /// Sends a stanza.
     pub fn send(&self, elem: Element) {
         self.dispatch(SendElement(elem));