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