//! Provides the plugin infrastructure. use event::{Event, Dispatcher, SendElement, Priority, Propagation}; use std::any::{Any, TypeId}; 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; use jid::Jid; pub struct PluginContainer { plugins: RwLock>>, } impl PluginContainer { pub fn new() -> PluginContainer { PluginContainer { plugins: RwLock::new(HashMap::new()), } } pub fn register(&self, plugin: Arc

) { let mut guard = self.plugins.write().unwrap(); if guard.insert(TypeId::of::

(), plugin as Arc).is_some() { panic!("registering a plugin that's already registered"); } } pub fn get(&self) -> Option> { let guard = self.plugins.read().unwrap(); let arc = guard.get(&TypeId::of::

()); arc.map(|arc| PluginRef { inner: arc.clone(), _marker: PhantomData }) } } #[derive(Clone)] pub struct PluginRef { inner: Arc, _marker: PhantomData

, } impl Deref for PluginRef

{ type Target = P; fn deref(&self) -> &P { self.inner.as_any().downcast_ref::

().expect("plugin downcast failure") } } impl AsRef

for PluginRef

{ fn as_ref(&self) -> &P { self.inner.as_any().downcast_ref::

().expect("plugin downcast failure") } } #[derive(Clone)] pub struct PluginProxyBinding { dispatcher: Arc, plugin_container: Arc, jid: Jid, } impl PluginProxyBinding { pub fn new(dispatcher: Arc, plugin_container: Arc, jid: Jid) -> PluginProxyBinding { PluginProxyBinding { dispatcher: dispatcher, plugin_container: plugin_container, jid: jid, } } } pub enum PluginProxy { Unbound, BoundTo(PluginProxyBinding), } impl PluginProxy { /// Returns a new `PluginProxy`. pub fn new() -> PluginProxy { PluginProxy::Unbound } /// Binds the `PluginProxy` to a `PluginProxyBinding`. pub fn bind(&mut self, inner: PluginProxyBinding) { if let PluginProxy::BoundTo(_) = *self { panic!("trying to bind an already bound plugin proxy!"); } mem::replace(self, PluginProxy::BoundTo(inner)); } fn with_binding R>(&self, f: F) -> R { match *self { PluginProxy::Unbound => { panic!("trying to use an unbound plugin proxy!"); }, PluginProxy::BoundTo(ref binding) => { f(binding) }, } } /// Dispatches an event. pub fn dispatch(&self, event: E) { self.with_binding(move |binding| { // TODO: proper error handling binding.dispatcher.dispatch(event); }); } /// Registers an event handler. pub fn register_handler(&self, priority: Priority, func: F) where E: Event, F: Fn(&E) -> Propagation + 'static { self.with_binding(move |binding| { // TODO: proper error handling binding.dispatcher.register(priority, func); }); } /// Tries to get another plugin. pub fn plugin(&self) -> Option> { self.with_binding(|binding| { binding.plugin_container.get::

() }) } /// Sends a stanza. pub fn send(&self, elem: Element) { self.dispatch(SendElement(elem)); } /// Get our own JID. pub fn get_own_jid(&self) -> Jid { self.with_binding(|binding| { binding.jid.clone() }) } } /// A trait whch all plugins should implement. pub trait Plugin: Any + PluginAny { /// Gets a mutable reference to the inner `PluginProxy`. fn get_proxy(&mut self) -> &mut PluginProxy; #[doc(hidden)] fn bind(&mut self, inner: PluginProxyBinding) { self.get_proxy().bind(inner); } } pub trait PluginInit { fn init(dispatcher: &Dispatcher, me: Arc); } pub trait PluginAny { fn as_any(&self) -> &Any; } impl PluginAny for T { fn as_any(&self) -> &Any { self } }