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}