use std::marker::PhantomData; use std::any::{TypeId, Any}; use std::fmt::Debug; use std::collections::BTreeMap; use std::cmp::Ordering; use std::mem; use minidom::Element; /// A marker trait which marks all events. pub trait Event: Any + Debug {} /// A trait which is implemented for all event handlers. trait EventHandler: Any { /// Handle an event, returns whether to propagate the event to the remaining handlers. fn handle(&self, event: &AbstractEvent) -> Propagation; } /// An abstract event. pub struct AbstractEvent { inner: Box, } impl AbstractEvent { /// Creates an abstract event from a concrete event. pub fn new(event: E) -> AbstractEvent { AbstractEvent { inner: Box::new(event), } } /// Downcasts this abstract event into a concrete event. pub fn downcast(&self) -> Option<&E> { self.inner.downcast_ref::() } /// Checks whether this abstract event is a specific concrete event. pub fn is(&self) -> bool { self.inner.is::() } } struct Record(P, T); impl PartialEq for Record { fn eq(&self, other: &Record) -> bool { self.0 == other.0 } } impl Eq for Record {} impl PartialOrd for Record { fn partial_cmp(&self, other: &Record) -> Option { self.0.partial_cmp(&other.0) } } impl Ord for Record { fn cmp(&self, other: &Record) -> Ordering { self.0.cmp(&other.0) } } /// 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, } /// An event dispatcher, this takes care of dispatching events to their respective handlers. pub struct Dispatcher { handlers: BTreeMap>>>, queue: Vec<(TypeId, AbstractEvent)>, } impl Dispatcher { /// Create a new `Dispatcher`. pub fn new() -> Dispatcher { Dispatcher { handlers: BTreeMap::new(), queue: Vec::new(), } } /// Register an event handler. pub fn register(&mut self, priority: Priority, func: F) where E: Event, F: Fn(&E) -> Propagation + 'static { struct Handler where E: Event, F: Fn(&E) -> Propagation { func: F, _marker: PhantomData, } impl Propagation + 'static> EventHandler for Handler { fn handle(&self, evt: &AbstractEvent) -> Propagation { if let Some(e) = evt.downcast::() { (self.func)(e) } else { Propagation::Continue } } } let handler: Box = Box::new(Handler { func: func, _marker: PhantomData, }) as Box; let ent = self.handlers.entry(TypeId::of::()) .or_insert_with(|| Vec::new()); ent.push(Record(priority, handler)); ent.sort(); } /// Append an event to the queue. pub fn dispatch(&mut self, event: E) where E: Event { self.queue.push((TypeId::of::(), AbstractEvent::new(event))); } /// 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 { match handler.handle(&evt) { 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() {} } } #[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(); #[derive(Debug)] struct MyEvent { should_be_42: u32, } impl Event for MyEvent {} disp.register(Priority::Max, |evt: &MyEvent| { if evt.should_be_42 == 42 { Propagation::Continue } else { Propagation::Stop } }); disp.register(Priority::Min, |_: &MyEvent| { panic!("should not be called"); }); disp.register(Priority::Default, |evt: &MyEvent| { if evt.should_be_42 == 42 { panic!("success"); } else { panic!("not 42"); } }); disp.register(Priority::Min, |_: &MyEvent| { panic!("should not be called"); }); disp.dispatch(MyEvent { should_be_42: 39, }); disp.dispatch(MyEvent { should_be_42: 42, }); disp.flush(); } }