use crate::concurrency::{IScheduler, SchedulerExt};
use crate::linq::Observable;
use ferroui_system::{IObservable, IObserver, ObservableError};
use std::fmt;
use std::hash::{Hash, Hasher};
use std::rc::Rc;
pub trait IObserverWithResult<T, R> {
fn on_next(&self, value: T) -> R;
fn on_error(&self, error: ObservableError) -> R;
fn on_completed(&self) -> R;
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum NotificationKind {
OnNext,
OnError,
OnCompleted,
}
#[derive(Clone)]
pub enum Notification<T> {
OnNext(T),
OnError(ObservableError),
OnCompleted,
}
impl<T> Notification<T> {
pub fn kind(&self) -> NotificationKind {
match self {
Self::OnNext(_) => NotificationKind::OnNext,
Self::OnError(_) => NotificationKind::OnError,
Self::OnCompleted => NotificationKind::OnCompleted,
}
}
pub fn has_value(&self) -> bool {
matches!(self, Self::OnNext(_))
}
pub fn value(&self) -> Option<&T> {
match self {
Self::OnNext(value) => Some(value),
_ => None,
}
}
pub fn exception(&self) -> Option<&ObservableError> {
match self {
Self::OnError(error) => Some(error),
_ => None,
}
}
pub fn accept(self, observer: &dyn IObserver<T>) {
match self {
Self::OnNext(value) => observer.on_next(value),
Self::OnError(error) => observer.on_error(error),
Self::OnCompleted => observer.on_completed(),
}
}
pub fn accept_observer_with_result<R>(self, observer: &dyn IObserverWithResult<T, R>) -> R {
match self {
Self::OnNext(value) => observer.on_next(value),
Self::OnError(error) => observer.on_error(error),
Self::OnCompleted => observer.on_completed(),
}
}
pub fn accept_fns<R>(
self,
on_next: impl FnOnce(T) -> R,
on_error: impl FnOnce(ObservableError) -> R,
on_completed: impl FnOnce() -> R,
) -> R {
match self {
Self::OnNext(value) => on_next(value),
Self::OnError(error) => on_error(error),
Self::OnCompleted => on_completed(),
}
}
}
impl<T: Clone + 'static> Notification<T> {
pub fn to_observable(&self) -> Rc<dyn IObservable<T>> {
self.to_observable_on(crate::concurrency::Scheduler::immediate())
}
pub fn to_observable_on(&self, scheduler: Rc<dyn IScheduler>) -> Rc<dyn IObservable<T>> {
let notification = self.clone();
Observable::create(move |observer: Rc<dyn IObserver<T>>| {
let notification = notification.clone();
scheduler.schedule_action(move || {
let is_next = notification.has_value();
notification.accept(&*observer);
if is_next {
observer.on_completed();
}
})
})
}
}
impl<T: PartialEq> PartialEq for Notification<T> {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::OnNext(a), Self::OnNext(b)) => a == b,
(Self::OnError(a), Self::OnError(b)) => Rc::ptr_eq(a, b),
(Self::OnCompleted, Self::OnCompleted) => true,
_ => false,
}
}
}
impl<T: Hash> Hash for Notification<T> {
fn hash<H: Hasher>(&self, state: &mut H) {
match self {
Self::OnNext(value) => value.hash(state),
Self::OnError(error) => (Rc::as_ptr(error) as *const () as usize).hash(state),
Self::OnCompleted => {
std::any::type_name::<T>().hash(state);
8510u32.hash(state);
}
}
}
}
impl<T: fmt::Debug> fmt::Debug for Notification<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::OnNext(value) => write!(f, "OnNext({value:?})"),
Self::OnError(error) => write!(f, "OnError({error})"),
Self::OnCompleted => f.write_str("OnCompleted()"),
}
}
}