use std::sync::{Arc, Weak};
pub trait Observer {
type State;
type Error;
fn update(&self, state: &Self::State) -> Result<(), Self::Error>;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum NotifyStrategy {
StopOnError,
IgnoreError,
}
pub struct Subject<T, E> {
observers: Vec<Weak<dyn Observer<State = T, Error = E>>>,
}
impl<T, E> Subject<T, E> {
pub fn new() -> Self {
Self {
observers: Vec::new(),
}
}
pub fn with_capacity(capacity: usize) -> Self {
Self {
observers: Vec::with_capacity(capacity),
}
}
pub fn attach(&mut self, observer: Arc<dyn Observer<State = T, Error = E>>) {
let weak = Arc::downgrade(&observer);
if !self.observers.iter().any(|item| item.ptr_eq(&weak)) {
self.observers.push(weak);
}
}
pub fn detach(&mut self, observer: Arc<dyn Observer<State = T, Error = E>>) {
let weak = Arc::downgrade(&observer);
self.observers.retain(|item| !item.ptr_eq(&weak));
}
pub fn notify(&self, state: &T, strategy: NotifyStrategy) -> Result<(), E> {
self.observers
.iter()
.flat_map(Weak::upgrade)
.try_for_each(|observer| match observer.update(state) {
Ok(()) => Ok(()),
Err(e) => match strategy {
NotifyStrategy::StopOnError => Err(e),
NotifyStrategy::IgnoreError => Ok(()),
},
})
}
}
impl<T, E> Default for Subject<T, E> {
fn default() -> Self {
Self::new()
}
}