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