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}