implement the new event system, things are still really messy

lumi created

Change summary

.gitlab-ci.yml           |   2 
examples/client.rs       |   5 
src/client.rs            | 122 ++++++++++-------
src/event.rs             | 292 +++++++++++++++++++++++++++++++++++------
src/lib.rs               |   5 
src/plugin.rs            |  42 ++---
src/plugin_macro.rs      |  26 +++
src/plugins/messaging.rs |  17 +-
src/plugins/ping.rs      |  17 +-
src/plugins/presence.rs  |  12 -
src/plugins/stanza.rs    |  19 +-
11 files changed, 403 insertions(+), 156 deletions(-)

Detailed changes

.gitlab-ci.yml πŸ”—

@@ -1,4 +1,4 @@
-image: "scorpil/rust:stable"
+image: "scorpil/rust:nightly"
 
 before_script:
 - apt-get update -yqq

examples/client.rs πŸ”—

@@ -19,7 +19,8 @@ fn main() {
     client.register_plugin(PresencePlugin::new());
     client.register_plugin(PingPlugin::new());
     client.plugin::<PresencePlugin>().set_presence(Show::Available, None).unwrap();
-    loop {
+    client.main().unwrap();
+    /*loop {
         let event = client.next_event().unwrap();
         if let Some(evt) = event.downcast::<MessageEvent>() {
             println!("{:?}", evt);
@@ -28,5 +29,5 @@ fn main() {
             println!("{:?}", evt);
             client.plugin::<PingPlugin>().reply_ping(evt);
         }
-    }
+    }*/
 }

src/client.rs πŸ”—

@@ -1,9 +1,9 @@
+use xml;
 use jid::Jid;
 use transport::{Transport, SslTransport};
 use error::Error;
 use ns;
-use plugin::{Plugin, PluginProxyBinding};
-use event::AbstractEvent;
+use plugin::{Plugin, PluginInit, PluginProxyBinding};
 use connection::{Connection, C2S};
 use sasl::client::Mechanism as SaslMechanism;
 use sasl::client::mechanisms::{Plain, Scram};
@@ -11,6 +11,7 @@ use sasl::common::{Credentials as SaslCredentials, Identity, Secret, ChannelBind
 use sasl::common::scram::{Sha1, Sha256};
 use components::sasl_error::SaslError;
 use util::FromElement;
+use event::{Dispatcher, Propagation, SendElement, ReceiveElement, Priority};
 
 use base64;
 
@@ -18,9 +19,11 @@ use minidom::Element;
 
 use xml::reader::XmlEvent as ReaderEvent;
 
-use std::sync::mpsc::{Receiver, channel};
+use std::sync::{Mutex, Arc};
 
-use std::collections::HashSet;
+use std::collections::{HashSet, HashMap};
+
+use std::any::TypeId;
 
 /// Struct that should be moved somewhere else and cleaned up.
 #[derive(Debug)]
@@ -74,18 +77,22 @@ impl ClientBuilder {
         let host = &self.host.unwrap_or(self.jid.domain.clone());
         let mut transport = SslTransport::connect(host, self.port)?;
         C2S::init(&mut transport, &self.jid.domain, "before_sasl")?;
-        let (sender_out, sender_in) = channel();
-        let (dispatcher_out, dispatcher_in) = channel();
+        let dispatcher = Arc::new(Mutex::new(Dispatcher::new()));
         let mut credentials = self.credentials;
         credentials.channel_binding = transport.channel_bind();
+        let transport = Arc::new(Mutex::new(transport));
         let mut client = Client {
             jid: self.jid,
-            transport: transport,
-            plugins: Vec::new(),
-            binding: PluginProxyBinding::new(sender_out, dispatcher_out),
-            sender_in: sender_in,
-            dispatcher_in: dispatcher_in,
+            transport: transport.clone(),
+            plugins: HashMap::new(),
+            binding: PluginProxyBinding::new(dispatcher.clone()),
+            dispatcher: dispatcher,
         };
+        client.dispatcher.lock().unwrap().register(Priority::Default, Box::new(move |evt: &SendElement| {
+            let mut t = transport.lock().unwrap();
+            t.write_element(&evt.0).unwrap();
+            Propagation::Continue
+        }));
         client.connect(credentials)?;
         client.bind()?;
         Ok(client)
@@ -95,11 +102,10 @@ impl ClientBuilder {
 /// An XMPP client.
 pub struct Client {
     jid: Jid,
-    transport: SslTransport,
-    plugins: Vec<Box<Plugin>>,
+    transport: Arc<Mutex<SslTransport>>,
+    plugins: HashMap<TypeId, Arc<Box<Plugin>>>,
     binding: PluginProxyBinding,
-    sender_in: Receiver<Element>,
-    dispatcher_in: Receiver<AbstractEvent>,
+    dispatcher: Arc<Mutex<Dispatcher>>,
 }
 
 impl Client {
@@ -109,46 +115,55 @@ impl Client {
     }
 
     /// Registers a plugin.
-    pub fn register_plugin<P: Plugin + 'static>(&mut self, mut plugin: P) {
-        plugin.bind(self.binding.clone());
-        self.plugins.push(Box::new(plugin));
+    pub fn register_plugin<P: Plugin + PluginInit + 'static>(&mut self, mut plugin: P) {
+        let binding = self.binding.clone();
+        plugin.bind(binding);
+        let p = Arc::new(Box::new(plugin) as Box<Plugin>);
+        {
+            let mut disp = self.dispatcher.lock().unwrap();
+            P::init(&mut disp, p.clone());
+        }
+        if self.plugins.insert(TypeId::of::<P>(), p).is_some() {
+            panic!("registering a plugin that's already registered");
+        }
     }
 
     /// Returns the plugin given by the type parameter, if it exists, else panics.
     pub fn plugin<P: Plugin>(&self) -> &P {
-        for plugin in &self.plugins {
-            let any = plugin.as_any();
-            if let Some(ret) = any.downcast_ref::<P>() {
-                return ret;
-            }
-        }
-        panic!("plugin does not exist!");
+        self.plugins.get(&TypeId::of::<P>())
+                    .expect("the requested plugin was not registered")
+                    .as_any()
+                    .downcast_ref::<P>()
+                    .expect("plugin downcast failure (should not happen!!)")
     }
 
     /// Returns the next event and flush the send queue.
-    pub fn next_event(&mut self) -> Result<AbstractEvent, Error> {
-        self.flush_send_queue()?;
+    pub fn main(&mut self) -> Result<(), Error> {
+        self.dispatcher.lock().unwrap().flush_all();
         loop {
-            if let Ok(evt) = self.dispatcher_in.try_recv() {
-                return Ok(evt);
-            }
-            let elem = self.transport.read_element()?;
-            for plugin in self.plugins.iter_mut() {
-                plugin.handle(&elem);
-                // TODO: handle plugin return
+            let elem = self.read_element()?;
+            {
+                let mut disp = self.dispatcher.lock().unwrap();
+                disp.dispatch(ReceiveElement(elem));
+                disp.flush_all();
             }
-            self.flush_send_queue()?;
         }
     }
 
-    /// Flushes the send queue, sending all queued up stanzas.
-    pub fn flush_send_queue(&mut self) -> Result<(), Error> { // TODO: not sure how great of an
-                                                              //       idea it is to flush in this
-                                                              //       manner…
-        while let Ok(elem) = self.sender_in.try_recv() {
-            self.transport.write_element(&elem)?;
-        }
-        Ok(())
+    fn reset_stream(&self) {
+        self.transport.lock().unwrap().reset_stream()
+    }
+
+    fn read_element(&self) -> Result<Element, Error> {
+        self.transport.lock().unwrap().read_element()
+    }
+
+    fn write_element(&self, elem: &Element) -> Result<(), Error> {
+        self.transport.lock().unwrap().write_element(elem)
+    }
+
+    fn read_event(&self) -> Result<xml::reader::XmlEvent, Error> {
+        self.transport.lock().unwrap().read_event()
     }
 
     fn connect(&mut self, mut credentials: SaslCredentials) -> Result<(), Error> {
@@ -188,9 +203,9 @@ impl Client {
         if !auth.is_empty() {
             elem.append_text_node(base64::encode(&auth));
         }
-        self.transport.write_element(&elem)?;
+        self.write_element(&elem)?;
         loop {
-            let n = self.transport.read_element()?;
+            let n = self.read_element()?;
             if n.is("challenge", ns::SASL) {
                 let text = n.text();
                 let challenge = if text == "" {
@@ -206,7 +221,7 @@ impl Client {
                 if !response.is_empty() {
                     elem.append_text_node(base64::encode(&response));
                 }
-                self.transport.write_element(&elem)?;
+                self.write_element(&elem)?;
             }
             else if n.is("success", ns::SASL) {
                 let text = n.text();
@@ -217,8 +232,11 @@ impl Client {
                     base64::decode(&text)?
                 };
                 mechanism.success(&data).map_err(|x| Error::SaslError(Some(x)))?;
-                self.transport.reset_stream();
-                C2S::init(&mut self.transport, &self.jid.domain, "after_sasl")?;
+                self.reset_stream();
+                {
+                    let mut g = self.transport.lock().unwrap();
+                    C2S::init(&mut *g, &self.jid.domain, "after_sasl")?;
+                }
                 self.wait_for_features()?;
                 return Ok(());
             }
@@ -245,9 +263,9 @@ impl Client {
             bind.append_child(res);
         }
         elem.append_child(bind);
-        self.transport.write_element(&elem)?;
+        self.write_element(&elem)?;
         loop {
-            let n = self.transport.read_element()?;
+            let n = self.read_element()?;
             if n.is("iq", ns::CLIENT) && n.has_child("bind", ns::BIND) {
                 return Ok(());
             }
@@ -257,7 +275,7 @@ impl Client {
     fn wait_for_features(&mut self) -> Result<StreamFeatures, Error> {
         // TODO: this is very ugly
         loop {
-            let e = self.transport.read_event()?;
+            let e = self.read_event()?;
             match e {
                 ReaderEvent::StartElement { .. } => {
                     break;
@@ -266,7 +284,7 @@ impl Client {
             }
         }
         loop {
-            let n = self.transport.read_element()?;
+            let n = self.read_element()?;
             if n.is("features", ns::STREAM) {
                 let mut features = StreamFeatures {
                     sasl_mechanisms: None,

src/event.rs πŸ”—

@@ -1,56 +1,262 @@
-//! Provides an abstract event type which can be downcasted into a more specific one.
-//!
-//! # Examples
-//!
-//! ```
-//! use xmpp::event::{Event, AbstractEvent};
-//!
-//! #[derive(Debug, PartialEq, Eq)]
-//! struct EventA;
-//!
-//! impl Event for EventA {}
-//!
-//! #[derive(Debug, PartialEq, Eq)]
-//! struct EventB;
-//!
-//! impl Event for EventB {}
-//!
-//! let event_a = AbstractEvent::new(EventA);
-//!
-//! assert_eq!(event_a.is::<EventA>(), true);
-//! assert_eq!(event_a.is::<EventB>(), false);
-//!
-//! assert_eq!(event_a.downcast::<EventA>(), Some(&EventA));
-//! assert_eq!(event_a.downcast::<EventB>(), None);
-//! ```
-
+use std::marker::PhantomData;
+use std::any::{TypeId, Any};
 use std::fmt::Debug;
+use std::collections::BTreeMap;
+use std::cmp::Ordering;
+use std::sync::Arc;
+use std::mem;
+use std::ptr;
+use std::raw::TraitObject;
+
+use minidom::Element;
+
+/// A marker trait which marks all events.
+pub trait Event: Any + Debug {}
+
+/// A trait which can be implemented when something can handle a specific kind of event.
+pub trait EventHandler<E: Event>: Any {
+    /// Handle an event, returns whether to propagate the event to the remaining handlers.
+    fn handle(&self, event: &E) -> Propagation;
+}
+
+struct Record<P, T>(P, T);
+
+impl<P: PartialEq, T> PartialEq for Record<P, T> {
+    fn eq(&self, other: &Record<P, T>) -> bool {
+        self.0 == other.0
+    }
+}
+
+impl<P: Eq, T> Eq for Record<P, T> {}
+
+impl<P: PartialOrd, T> PartialOrd for Record<P, T> {
+    fn partial_cmp(&self, other: &Record<P, T>) -> Option<Ordering> {
+        self.0.partial_cmp(&other.0)
+    }
+}
+
+impl<P: Ord, T> Ord for Record<P, T> {
+    fn cmp(&self, other: &Record<P, T>) -> Ordering {
+        self.0.cmp(&other.0)
+    }
+}
 
-use std::any::Any;
+/// An enum representing whether to keep propagating an event or to stop the propagation.
+pub enum Propagation {
+    /// Stop the propagation of the event, the remaining handlers will not get invoked.
+    Stop,
+    /// Continue propagating the event.
+    Continue,
+}
+
+#[derive(Debug)]
+struct GarbageEvent;
+
+impl Event for GarbageEvent {}
 
-/// An abstract event.
-pub struct AbstractEvent {
-    inner: Box<Any>,
+impl<E, F> EventHandler<E> for Box<F> where E: Event, F: 'static + Fn(&E) -> Propagation {
+    fn handle(&self, evt: &E) -> Propagation {
+        self(evt)
+    }
 }
 
-impl AbstractEvent {
-    /// Creates an abstract event from a concrete event.
-    pub fn new<E: Event>(event: E) -> AbstractEvent {
-        AbstractEvent {
-            inner: Box::new(event),
+/// An event dispatcher, this takes care of dispatching events to their respective handlers.
+pub struct Dispatcher {
+    handlers: BTreeMap<TypeId, Vec<Record<Priority, Box<Any>>>>,
+    queue: Vec<(TypeId, Box<Any>)>,
+}
+
+impl Dispatcher {
+    /// Create a new `Dispatcher`.
+    pub fn new() -> Dispatcher {
+        Dispatcher {
+            handlers: BTreeMap::new(),
+            queue: Vec::new(),
         }
     }
 
-    /// Downcasts this abstract event into a concrete event.
-    pub fn downcast<E: Event + 'static>(&self) -> Option<&E> {
-        self.inner.downcast_ref::<E>()
+    /// Register an event handler.
+    pub fn register<E, H>(&mut self, priority: Priority, handler: H) where E: Event + 'static, H: EventHandler<E> {
+        let handler: Box<EventHandler<E>> = Box::new(handler) as Box<EventHandler<E>>;
+        let ent = self.handlers.entry(TypeId::of::<E>())
+                               .or_insert_with(|| Vec::new());
+        ent.push(Record(priority, Box::new(handler) as Box<Any>));
+        ent.sort();
+    }
+
+    /// Append an event to the queue.
+    pub fn dispatch<E>(&mut self, event: E) where E: Event {
+        self.queue.push((TypeId::of::<E>(), Box::new(event) as Box<Any>));
     }
 
-    /// Checks whether this abstract event is a specific concrete event.
-    pub fn is<E: Event + 'static>(&self) -> bool {
-        self.inner.is::<E>()
+    /// Flush all events in the queue so they can be handled by their respective handlers.
+    /// Returns whether there are still pending events.
+    pub fn flush(&mut self) -> bool {
+        let mut q = Vec::new();
+        mem::swap(&mut self.queue, &mut q);
+        'evts: for (t, evt) in q {
+            if let Some(handlers) = self.handlers.get_mut(&t) {
+                for &mut Record(_, ref mut handler) in handlers {
+                    // GarbageEvent is a garbage type.
+                    // The actual passed type is NEVER of this type.
+                    let h: &mut EventHandler<GarbageEvent> = unsafe {
+                        let handler_obj: &mut TraitObject = mem::transmute(handler);
+                        let handler_inner: *mut TraitObject = mem::transmute(handler_obj.data);
+                        mem::transmute(*handler_inner)
+                    };
+                    let e: &&GarbageEvent = unsafe {
+                        let evt_ref: &Any = &evt;
+                        let evt_obj: TraitObject = mem::transmute(evt_ref);
+                        mem::transmute(evt_obj.data)
+                    };
+                    match h.handle(e) {
+                        Propagation::Stop => { continue 'evts; },
+                        Propagation::Continue => (),
+                    }
+                }
+            }
+        }
+        !self.queue.is_empty()
+    }
+
+    /// Flushes all events, like `flush`, but keeps doing this until there is nothing left in the
+    /// queue.
+    pub fn flush_all(&mut self) {
+        while self.flush() {}
+    }
+
+    /// Dispatch an event to the handlers right now, without going through the queue.
+    pub fn dispatch_now<E>(&mut self, event: E) where E: Event {
+        if let Some(handlers) = self.handlers.get_mut(&TypeId::of::<E>()) {
+            for &mut Record(_, ref mut handler) in handlers {
+                let h = handler.downcast_mut::<Box<EventHandler<E>>>().unwrap();
+                match h.handle(&event) {
+                    Propagation::Stop => { return; },
+                    Propagation::Continue => (),
+                }
+            }
+        }
     }
 }
 
-/// A marker trait which all events must implement.
-pub trait Event: Any + Debug {}
+pub struct EventProxy<T: ?Sized, E: Event> {
+    inner: Arc<Box<T>>,
+    vtable: *mut (),
+    _event_type: PhantomData<E>,
+}
+
+impl<T: ?Sized, E: Event> EventProxy<T, E> {
+    /// Unsafe because T is assumed to be a TraitObject or at least have its shape.
+    /// If it is not, things will break. In a fascinatingly horrible manner.
+    /// Some people, such as myself, find it hilarious. Most people do not.
+    /// T is also assumed to actually support EventHandler<E>, if it does not, refer to above
+    /// statement.
+    pub unsafe fn new<H: EventHandler<E>>(inner: Arc<Box<T>>) -> EventProxy<T, E> {
+        let box_with_vtable = &*ptr::null::<H>() as &EventHandler<E>;
+        let obj: TraitObject = mem::transmute(box_with_vtable);
+        EventProxy {
+            inner: inner,
+            vtable: obj.vtable,
+            _event_type: PhantomData,
+        }
+    }
+}
+
+impl<T: ?Sized, E: Event> EventHandler<E> for EventProxy<T, E> where Box<T>: 'static {
+    fn handle(&self, evt: &E) -> Propagation {
+        let inner = Arc::into_raw(self.inner.clone());
+        let obj = TraitObject { data: unsafe { mem::transmute(inner) }, vtable: self.vtable };
+        let handler: &EventHandler<E> = unsafe { mem::transmute(obj) };
+        let prop = handler.handle(evt);
+        unsafe { Arc::<Box<T>>::from_raw(mem::transmute(inner)); }
+        prop
+    }
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
+pub enum Priority {
+    Max,
+    Default,
+    Min,
+}
+
+impl Default for Priority {
+    fn default() -> Priority {
+        Priority::Default
+    }
+}
+
+#[derive(Debug)]
+pub struct SendElement(pub Element);
+
+impl Event for SendElement {}
+
+#[derive(Debug)]
+pub struct ReceiveElement(pub Element);
+
+impl Event for ReceiveElement {}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    #[should_panic(expected = "success")]
+    fn test() {
+        let mut disp = Dispatcher::new();
+
+        struct MyHandler;
+        struct EvilHandler;
+        struct EventFilter;
+
+        #[derive(Debug)]
+        struct MyEvent {
+            should_be_42: u32,
+        }
+
+        impl Event for MyEvent {}
+
+        impl EventHandler<MyEvent> for MyHandler {
+            fn handle(&self, evt: &MyEvent) -> Propagation {
+                if evt.should_be_42 == 42 {
+                    panic!("success");
+                }
+                else {
+                    panic!("not 42");
+                }
+            }
+        }
+
+        impl EventHandler<MyEvent> for EvilHandler {
+            fn handle(&self, _: &MyEvent) -> Propagation {
+                panic!("should not be called");
+            }
+        }
+
+        impl EventHandler<MyEvent> for EventFilter {
+            fn handle(&self, evt: &MyEvent) -> Propagation {
+                if evt.should_be_42 == 42 {
+                    Propagation::Continue
+                }
+                else {
+                    Propagation::Stop
+                }
+            }
+        }
+
+        disp.register(Priority::Max, EventFilter);
+        disp.register(Priority::Min, EvilHandler);
+        disp.register(Priority::Default, MyHandler);
+        disp.register(Priority::Min, EvilHandler);
+
+        disp.dispatch(MyEvent {
+            should_be_42: 39,
+        });
+
+        disp.dispatch(MyEvent {
+            should_be_42: 42,
+        });
+
+        disp.flush();
+    }
+}

src/lib.rs πŸ”—

@@ -1,3 +1,5 @@
+#![feature(raw)]
+
 extern crate xml;
 extern crate openssl;
 extern crate minidom;
@@ -10,8 +12,9 @@ pub mod ns;
 pub mod transport;
 pub mod error;
 pub mod client;
-pub mod component;
+//pub mod component;
 pub mod plugin;
+#[macro_use] pub mod plugin_macro;
 pub mod event;
 pub mod plugins;
 pub mod connection;

src/plugin.rs πŸ”—

@@ -1,10 +1,10 @@
 //! Provides the plugin infrastructure.
 
-use event::{Event, AbstractEvent};
+use event::{Event, EventHandler, Dispatcher, SendElement, Priority};
 
 use std::any::Any;
 
-use std::sync::mpsc::Sender;
+use std::sync::{Arc, Mutex};
 
 use std::mem;
 
@@ -12,14 +12,12 @@ use minidom::Element;
 
 #[derive(Clone)]
 pub struct PluginProxyBinding {
-    sender: Sender<Element>,
-    dispatcher: Sender<AbstractEvent>,
+    dispatcher: Arc<Mutex<Dispatcher>>,
 }
 
 impl PluginProxyBinding {
-    pub fn new(sender: Sender<Element>, dispatcher: Sender<AbstractEvent>) -> PluginProxyBinding {
+    pub fn new(dispatcher: Arc<Mutex<Dispatcher>>) -> PluginProxyBinding {
         PluginProxyBinding {
-            sender: sender,
             dispatcher: dispatcher,
         }
     }
@@ -58,26 +56,23 @@ impl PluginProxy {
     /// Dispatches an event.
     pub fn dispatch<E: Event>(&self, event: E) {
         self.with_binding(move |binding| {
-            binding.dispatcher.send(AbstractEvent::new(event))
-                              .unwrap(); // TODO: may want to return the error
+            // TODO: proper error handling
+            binding.dispatcher.lock().unwrap().dispatch(event);
         });
     }
 
-    /// Sends a stanza.
-    pub fn send(&self, elem: Element) {
+    /// Registers an event handler.
+    pub fn register_handler<E, H>(&self, priority: Priority, handler: H) where E: Event, H: EventHandler<E> {
         self.with_binding(move |binding| {
-            binding.sender.send(elem).unwrap(); // TODO: as above, may want to return the error
+            // TODO: proper error handling
+            binding.dispatcher.lock().unwrap().register(priority, handler);
         });
     }
-}
 
-/// A plugin handler return value.
-///
-/// The `Continue` variant means to do nothing, the `Unload` variant means to unload the plugin.
-#[derive(Copy, Clone, Debug, PartialEq, Eq)]
-pub enum PluginReturn {
-    Continue,
-    Unload,
+    /// Sends a stanza.
+    pub fn send(&self, elem: Element) {
+        self.dispatch(SendElement(elem));
+    }
 }
 
 /// A trait whch all plugins should implement.
@@ -85,19 +80,20 @@ pub trait Plugin: Any + PluginAny {
     /// Gets a mutable reference to the inner `PluginProxy`.
     fn get_proxy(&mut self) -> &mut PluginProxy;
 
-    /// Handles a received stanza.
-    fn handle(&mut self, elem: &Element) -> PluginReturn;
-
     #[doc(hidden)]
     fn bind(&mut self, inner: PluginProxyBinding) {
         self.get_proxy().bind(inner);
     }
 }
 
+pub trait PluginInit {
+    fn init(dispatcher: &mut Dispatcher, me: Arc<Box<Plugin>>);
+}
+
 pub trait PluginAny {
     fn as_any(&self) -> &Any;
 }
 
-impl<T: Any + Sized> PluginAny for T {
+impl<T: Any + Sized + Plugin> PluginAny for T {
     fn as_any(&self) -> &Any { self }
 }

src/plugin_macro.rs πŸ”—

@@ -0,0 +1,26 @@
+#[macro_export]
+macro_rules! impl_plugin {
+    ($plugin:ty, $proxy:ident, [$($evt:ty => $pri:expr),*]) => {
+        impl $crate::plugin::Plugin for $plugin {
+            fn get_proxy(&mut self) -> &mut $crate::plugin::PluginProxy {
+                &mut self.$proxy
+            }
+        }
+
+        #[allow(unused_variables)]
+        impl $crate::plugin::PluginInit for $plugin {
+            fn init( dispatcher: &mut $crate::event::Dispatcher
+                   , me: ::std::sync::Arc<Box<$crate::plugin::Plugin>>) {
+                $(
+                    dispatcher.register($pri, unsafe {
+                        $crate::event::EventProxy::new::<$plugin>(me.clone())
+                    });
+                )*
+            }
+        }
+    };
+
+    ($plugin:ty, $proxy:ident, [$($evt:ty => $pri:expr),*,]) => {
+        impl_plugin!($plugin, $proxy, [$($evt => $pri),*]);
+    };
+}

src/plugins/messaging.rs πŸ”—

@@ -1,5 +1,5 @@
-use plugin::{Plugin, PluginReturn, PluginProxy};
-use event::Event;
+use plugin::{PluginProxy};
+use event::{Event, EventHandler, ReceiveElement, Priority, Propagation};
 use minidom::Element;
 use error::Error;
 use jid::Jid;
@@ -36,12 +36,13 @@ impl MessagingPlugin {
     }
 }
 
-impl Plugin for MessagingPlugin {
-    fn get_proxy(&mut self) -> &mut PluginProxy {
-        &mut self.proxy
-    }
+impl_plugin!(MessagingPlugin, proxy, [
+    ReceiveElement => Priority::Default,
+]);
 
-    fn handle(&mut self, elem: &Element) -> PluginReturn {
+impl EventHandler<ReceiveElement> for MessagingPlugin {
+    fn handle(&self, evt: &ReceiveElement) -> Propagation {
+        let elem = &evt.0;
         if elem.is("message", ns::CLIENT) && elem.attr("type") == Some("chat") {
             if let Some(body) = elem.get_child("body", ns::CLIENT) {
                 self.proxy.dispatch(MessageEvent { // TODO: safety!!!
@@ -51,6 +52,6 @@ impl Plugin for MessagingPlugin {
                 });
             }
         }
-        PluginReturn::Continue
+        Propagation::Continue
     }
 }

src/plugins/ping.rs πŸ”—

@@ -1,5 +1,5 @@
-use plugin::{Plugin, PluginReturn, PluginProxy};
-use event::Event;
+use plugin::PluginProxy;
+use event::{Event, EventHandler, Priority, Propagation, ReceiveElement};
 use minidom::Element;
 use error::Error;
 use jid::Jid;
@@ -45,12 +45,13 @@ impl PingPlugin {
     }
 }
 
-impl Plugin for PingPlugin {
-    fn get_proxy(&mut self) -> &mut PluginProxy {
-        &mut self.proxy
-    }
+impl_plugin!(PingPlugin, proxy, [
+    ReceiveElement => Priority::Default,
+]);
 
-    fn handle(&mut self, elem: &Element) -> PluginReturn {
+impl EventHandler<ReceiveElement> for PingPlugin {
+    fn handle(&self, evt: &ReceiveElement) -> Propagation {
+        let elem = &evt.0;
         if elem.is("iq", ns::CLIENT) && elem.attr("type") == Some("get") {
             if elem.has_child("ping", ns::PING) {
                 self.proxy.dispatch(PingEvent { // TODO: safety!!!
@@ -60,6 +61,6 @@ impl Plugin for PingPlugin {
                 });
             }
         }
-        PluginReturn::Continue
+        Propagation::Continue
     }
 }

src/plugins/presence.rs πŸ”—

@@ -1,5 +1,5 @@
 use error::Error;
-use plugin::{Plugin, PluginProxy, PluginReturn};
+use plugin::PluginProxy;
 
 use minidom::Element;
 
@@ -94,12 +94,4 @@ impl PresencePlugin {
     }
 }
 
-impl Plugin for PresencePlugin {
-    fn get_proxy(&mut self) -> &mut PluginProxy {
-        &mut self.proxy
-    }
-
-    fn handle(&mut self, _elem: &Element) -> PluginReturn {
-        PluginReturn::Continue
-    }
-}
+impl_plugin!(PresencePlugin, proxy, []);

src/plugins/stanza.rs πŸ”—

@@ -1,8 +1,8 @@
 use std::fmt::Debug;
 use std::any::Any;
 
-use plugin::{Plugin, PluginReturn, PluginProxy};
-use event::Event;
+use plugin::PluginProxy;
+use event::{Event, EventHandler, ReceiveElement, Propagation, Priority};
 use minidom::Element;
 use jid::Jid;
 use ns;
@@ -52,12 +52,14 @@ impl StanzaPlugin {
     }
 }
 
-impl Plugin for StanzaPlugin {
-    fn get_proxy(&mut self) -> &mut PluginProxy {
-        &mut self.proxy
-    }
+impl_plugin!(StanzaPlugin, proxy, [
+    ReceiveElement => Priority::Default,
+]);
+
+impl EventHandler<ReceiveElement> for StanzaPlugin {
+    fn handle(&self, evt: &ReceiveElement) -> Propagation {
+        let elem = &evt.0;
 
-    fn handle(&mut self, elem: &Element) -> PluginReturn {
         let from = match elem.attr("from") { Some(from) => Some(from.parse().unwrap()), None => None };
         let to = match elem.attr("to") { Some(to) => Some(to.parse().unwrap()), None => None };
         let id = match elem.attr("id") { Some(id) => Some(id.parse().unwrap()), None => None };
@@ -89,6 +91,7 @@ impl Plugin for StanzaPlugin {
                 payloads: payloads,
             });
         }
-        PluginReturn::Continue
+
+        Propagation::Continue
     }
 }