From 8c720a77c7e18a79ee3171f066a7b82629d61562 Mon Sep 17 00:00:00 2001 From: brnrs Date: Sun, 26 Apr 2020 15:38:51 +0200 Subject: [PATCH] basic functionality --- src/lib.rs | 9 +-- src/listener.rs | 41 ++++++++++++++ src/listener_container.rs | 115 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 158 insertions(+), 7 deletions(-) create mode 100644 src/listener.rs create mode 100644 src/listener_container.rs diff --git a/src/lib.rs b/src/lib.rs index 31e1bb2..5d7fb5c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,2 @@ -#[cfg(test)] -mod tests { - #[test] - fn it_works() { - assert_eq!(2 + 2, 4); - } -} +pub mod listener; +pub mod listener_container; \ No newline at end of file diff --git a/src/listener.rs b/src/listener.rs new file mode 100644 index 0000000..a1fdd5d --- /dev/null +++ b/src/listener.rs @@ -0,0 +1,41 @@ +#[derive(Debug)] +pub struct OnEventError(String); + +pub type ListenerResult = Result<(), OnEventError>; + +pub trait Listener { + fn on_event(&self, event: &E, state: &mut S) -> ListenerResult; +} + +impl ListenerResult> Listener for F { + fn on_event(&self, event: &E, state: &mut S) -> ListenerResult { + self(event, state) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_closure_as_listener() { + // Given structs + struct State(u32); + struct Increment; + // And closure as Listener + let listener = |_: &Increment, state: &mut State| { + state.0 += 1; + Ok(()) + }; + // And values + let event = Increment; + let mut state = State(0); + + // When + listener.on_event(&event, &mut state).unwrap(); + + // Then + assert_eq!(state.0, 1); + } + +} \ No newline at end of file diff --git a/src/listener_container.rs b/src/listener_container.rs new file mode 100644 index 0000000..5985295 --- /dev/null +++ b/src/listener_container.rs @@ -0,0 +1,115 @@ +use std::any::{Any, TypeId}; +use std::collections::HashMap; +use std::marker::PhantomData; + +use super::listener::{Listener, ListenerResult, OnEventError}; + +pub type ListenerResults = Result<(), Vec>; + +pub struct ListenerContainer { + map: HashMap>, + marker: PhantomData, +} + +type ListenerList = Vec>>; + +impl ListenerContainer { + pub fn new() -> Self { + ListenerContainer { + map: HashMap::new(), + marker: PhantomData, + } + } + + fn get_listener_list(&self) -> Option<&ListenerList> { + self.map + .get(&TypeId::of::()) + .and_then(|boxed| boxed.downcast_ref::>()) + } + + fn get_mut_listener_list(&mut self) -> Option<&mut ListenerList> { + self.map + .get_mut(&TypeId::of::()) + .and_then(|boxed| boxed.downcast_mut::>()) + } + + pub fn register_listener_box(&mut self, listener: Box>) { + match self.get_mut_listener_list() { + None => { + let mut list = Vec::new(); + list.push(listener); + self.map.insert(TypeId::of::(), Box::new(list)); + } + Some(list) => { + list.push(listener); + } + } + } + + pub fn register_listener + 'static>(&mut self, listener: L) { + self.register_listener_box(Box::new(listener)) + } + + pub fn on_event(&self, event: &E, state: &mut S) -> Result<(), Vec> { + if let Some(list) = self.get_listener_list() { + list.iter() + .map(|listener| listener.on_event(event, state)) + .fold(Ok(()), accumulate_errors) + } else { + Ok(()) + } + } +} + +pub fn accumulate_errors( + acc: ListenerResults, + result: ListenerResult, +) -> Result<(), Vec> { + if let Err(error) = result { + match acc { + Ok(()) => Err(vec![error]), + Err(mut error_list) => { + error_list.push(error); + Err(error_list) + } + } + } else { + acc + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_listener_container() { + // Given structs + struct SubState(u32, u32); + struct State(u32, SubState); + struct Increment; + // And listener container + let mut container = ListenerContainer::::new(); + // And listeners + container.register_listener(|_: &Increment, state: &mut State| { + state.0 += 1; + Ok(()) + }); + container.register_listener(|_: &Increment, state: &mut State| { + (state.1).0 += 1; + (state.1).1 += 1; + Ok(()) + }); + // And values + let event = Increment; + let mut state = State(0, SubState(0, 0)); + + // When + container.on_event(&event, &mut state).unwrap(); + + // Then + assert_eq!(state.0, 1); + assert_eq!((state.1).0, 1); + assert_eq!((state.1).1, 1); + } +}