plugin.rs

  1//! Provides the plugin infrastructure.
  2
  3use event::{Event, Dispatcher, SendElement, Priority, Propagation};
  4
  5use std::any::{Any, TypeId};
  6
  7use std::collections::HashMap;
  8
  9use std::sync::{RwLock, Arc};
 10
 11use std::marker::PhantomData;
 12
 13use std::ops::Deref;
 14
 15use std::convert::AsRef;
 16
 17use std::mem;
 18
 19use minidom::Element;
 20
 21pub struct PluginContainer {
 22    plugins: RwLock<HashMap<TypeId, Arc<Plugin>>>,
 23}
 24
 25impl PluginContainer {
 26    pub fn new() -> PluginContainer {
 27        PluginContainer {
 28            plugins: RwLock::new(HashMap::new()),
 29        }
 30    }
 31
 32    pub fn register<P: Plugin + 'static>(&self, plugin: Arc<P>) {
 33        let mut guard = self.plugins.write().unwrap();
 34        if guard.insert(TypeId::of::<P>(), plugin as Arc<Plugin>).is_some() {
 35            panic!("registering a plugin that's already registered");
 36        }
 37    }
 38
 39    pub fn get<P: Plugin>(&self) -> Option<PluginRef<P>> {
 40        let guard = self.plugins.read().unwrap();
 41        let arc = guard.get(&TypeId::of::<P>());
 42        arc.map(|arc| PluginRef {
 43            inner: arc.clone(),
 44            _marker: PhantomData
 45        })
 46    }
 47}
 48
 49#[derive(Clone)]
 50pub struct PluginRef<P: Plugin> {
 51    inner: Arc<Plugin>,
 52    _marker: PhantomData<P>,
 53}
 54
 55impl<P: Plugin> Deref for PluginRef<P> {
 56    type Target = P;
 57
 58    fn deref(&self) -> &P {
 59        self.inner.as_any().downcast_ref::<P>().expect("plugin downcast failure")
 60    }
 61}
 62
 63impl<P: Plugin> AsRef<P> for PluginRef<P> {
 64    fn as_ref(&self) -> &P {
 65        self.inner.as_any().downcast_ref::<P>().expect("plugin downcast failure")
 66    }
 67}
 68
 69#[derive(Clone)]
 70pub struct PluginProxyBinding {
 71    dispatcher: Arc<Dispatcher>,
 72    plugin_container: Arc<PluginContainer>,
 73}
 74
 75impl PluginProxyBinding {
 76    pub fn new(dispatcher: Arc<Dispatcher>, plugin_container: Arc<PluginContainer>) -> PluginProxyBinding {
 77        PluginProxyBinding {
 78            dispatcher: dispatcher,
 79            plugin_container: plugin_container,
 80        }
 81    }
 82}
 83
 84pub enum PluginProxy {
 85    Unbound,
 86    BoundTo(PluginProxyBinding),
 87}
 88
 89impl PluginProxy {
 90    /// Returns a new `PluginProxy`.
 91    pub fn new() -> PluginProxy {
 92        PluginProxy::Unbound
 93    }
 94
 95    /// Binds the `PluginProxy` to a `PluginProxyBinding`.
 96    pub fn bind(&mut self, inner: PluginProxyBinding) {
 97        if let PluginProxy::BoundTo(_) = *self {
 98            panic!("trying to bind an already bound plugin proxy!");
 99        }
100        mem::replace(self, PluginProxy::BoundTo(inner));
101    }
102
103    fn with_binding<R, F: FnOnce(&PluginProxyBinding) -> R>(&self, f: F) -> R {
104        match *self {
105            PluginProxy::Unbound => {
106                panic!("trying to use an unbound plugin proxy!");
107            },
108            PluginProxy::BoundTo(ref binding) => {
109                f(binding)
110            },
111        }
112    }
113
114    /// Dispatches an event.
115    pub fn dispatch<E: Event>(&self, event: E) {
116        self.with_binding(move |binding| {
117            // TODO: proper error handling
118            binding.dispatcher.dispatch(event);
119        });
120    }
121
122    /// Registers an event handler.
123    pub fn register_handler<E, F>(&self, priority: Priority, func: F)
124        where
125            E: Event,
126            F: Fn(&E) -> Propagation + 'static {
127        self.with_binding(move |binding| {
128            // TODO: proper error handling
129            binding.dispatcher.register(priority, func);
130        });
131    }
132
133    /// Tries to get another plugin.
134    pub fn plugin<P: Plugin>(&self) -> Option<PluginRef<P>> {
135        self.with_binding(|binding| {
136            binding.plugin_container.get::<P>()
137        })
138    }
139
140    /// Sends a stanza.
141    pub fn send(&self, elem: Element) {
142        self.dispatch(SendElement(elem));
143    }
144}
145
146/// A trait whch all plugins should implement.
147pub trait Plugin: Any + PluginAny {
148    /// Gets a mutable reference to the inner `PluginProxy`.
149    fn get_proxy(&mut self) -> &mut PluginProxy;
150
151    #[doc(hidden)]
152    fn bind(&mut self, inner: PluginProxyBinding) {
153        self.get_proxy().bind(inner);
154    }
155}
156
157pub trait PluginInit {
158    fn init(dispatcher: &Dispatcher, me: Arc<Plugin>);
159}
160
161pub trait PluginAny {
162    fn as_any(&self) -> &Any;
163}
164
165impl<T: Any + Sized + Plugin> PluginAny for T {
166    fn as_any(&self) -> &Any { self }
167}