parent
ce30e674b8
commit
8c720a77c7
@ -1,7 +1,2 @@ |
|||||||
#[cfg(test)] |
pub mod listener; |
||||||
mod tests { |
pub mod listener_container; |
||||||
#[test] |
|
||||||
fn it_works() { |
|
||||||
assert_eq!(2 + 2, 4); |
|
||||||
} |
|
||||||
} |
|
@ -0,0 +1,41 @@ |
|||||||
|
#[derive(Debug)] |
||||||
|
pub struct OnEventError(String); |
||||||
|
|
||||||
|
pub type ListenerResult = Result<(), OnEventError>; |
||||||
|
|
||||||
|
pub trait Listener<E: 'static, S: 'static> { |
||||||
|
fn on_event(&self, event: &E, state: &mut S) -> ListenerResult; |
||||||
|
} |
||||||
|
|
||||||
|
impl<E: 'static, S: 'static, F: Fn(&E, &mut S) -> ListenerResult> Listener<E, S> 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); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -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<OnEventError>>; |
||||||
|
|
||||||
|
pub struct ListenerContainer<S: 'static> { |
||||||
|
map: HashMap<TypeId, Box<dyn Any>>, |
||||||
|
marker: PhantomData<S>, |
||||||
|
} |
||||||
|
|
||||||
|
type ListenerList<E, S> = Vec<Box<dyn Listener<E, S>>>; |
||||||
|
|
||||||
|
impl<S: 'static> ListenerContainer<S> { |
||||||
|
pub fn new() -> Self { |
||||||
|
ListenerContainer { |
||||||
|
map: HashMap::new(), |
||||||
|
marker: PhantomData, |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
fn get_listener_list<E: 'static>(&self) -> Option<&ListenerList<E, S>> { |
||||||
|
self.map |
||||||
|
.get(&TypeId::of::<E>()) |
||||||
|
.and_then(|boxed| boxed.downcast_ref::<ListenerList<E, S>>()) |
||||||
|
} |
||||||
|
|
||||||
|
fn get_mut_listener_list<E: 'static>(&mut self) -> Option<&mut ListenerList<E, S>> { |
||||||
|
self.map |
||||||
|
.get_mut(&TypeId::of::<E>()) |
||||||
|
.and_then(|boxed| boxed.downcast_mut::<ListenerList<E, S>>()) |
||||||
|
} |
||||||
|
|
||||||
|
pub fn register_listener_box<E: 'static>(&mut self, listener: Box<dyn Listener<E, S>>) { |
||||||
|
match self.get_mut_listener_list() { |
||||||
|
None => { |
||||||
|
let mut list = Vec::new(); |
||||||
|
list.push(listener); |
||||||
|
self.map.insert(TypeId::of::<E>(), Box::new(list)); |
||||||
|
} |
||||||
|
Some(list) => { |
||||||
|
list.push(listener); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
pub fn register_listener<E: 'static, L: Listener<E, S> + 'static>(&mut self, listener: L) { |
||||||
|
self.register_listener_box(Box::new(listener)) |
||||||
|
} |
||||||
|
|
||||||
|
pub fn on_event<E: 'static>(&self, event: &E, state: &mut S) -> Result<(), Vec<OnEventError>> { |
||||||
|
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<OnEventError>> { |
||||||
|
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::<State>::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); |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue