use ferroui_system::{IDisposable, IObserver, ObservableError};
use std::cell::Cell;
pub trait ObserverCore<T> {
fn on_next_core(&self, value: T);
fn on_error_core(&self, error: ObservableError);
fn on_completed_core(&self);
}
struct FnCore<T> {
on_next: Box<dyn Fn(T)>,
on_error: Box<dyn Fn(ObservableError)>,
on_completed: Box<dyn Fn()>,
}
impl<T> ObserverCore<T> for FnCore<T> {
fn on_next_core(&self, value: T) {
(self.on_next)(value);
}
fn on_error_core(&self, error: ObservableError) {
(self.on_error)(error);
}
fn on_completed_core(&self) {
(self.on_completed)();
}
}
pub struct ObserverBase<T> {
core: Box<dyn ObserverCore<T>>,
is_stopped: Cell<bool>,
}
impl<T: 'static> ObserverBase<T> {
pub fn new(core: impl ObserverCore<T> + 'static) -> Self {
Self { core: Box::new(core), is_stopped: Cell::new(false) }
}
pub fn from_fns(
on_next_core: impl Fn(T) + 'static,
on_error_core: impl Fn(ObservableError) + 'static,
on_completed_core: impl Fn() + 'static,
) -> Self {
Self::new(FnCore { on_next: Box::new(on_next_core), on_error: Box::new(on_error_core), on_completed: Box::new(on_completed_core) })
}
pub(crate) fn fail(&self, error: ObservableError) -> bool {
if !self.is_stopped.replace(true) {
self.core.on_error_core(error);
return true;
}
false
}
}
impl<T: 'static> IObserver<T> for ObserverBase<T> {
fn on_next(&self, value: T) {
if !self.is_stopped.get() {
self.core.on_next_core(value);
}
}
fn on_error(&self, error: ObservableError) {
self.fail(error);
}
fn on_completed(&self) {
if !self.is_stopped.replace(true) {
self.core.on_completed_core();
}
}
}
impl<T> IDisposable for ObserverBase<T> {
fn dispose(&self) {
self.is_stopped.set(true);
}
}