use crate::concurrency::AsyncLock;
use crate::notification::Notification;
use crate::observer_base::{ObserverBase, ObserverCore};
use ferroui_system::{IObserver, IProgress, ObservableError};
use std::cell::Cell;
use std::rc::Rc;
type OnError = Box<dyn Fn(ObservableError)>;
pub struct AnonymousObserver<T> {
on_next: Box<dyn Fn(T)>,
on_error: OnError,
on_completed: Box<dyn Fn()>,
is_stopped: Cell<bool>,
}
impl<T> AnonymousObserver<T> {
pub fn new(
on_next: impl Fn(T) + 'static,
on_error: impl Fn(ObservableError) + 'static,
on_completed: impl Fn() + 'static,
) -> Self {
Self { on_next: Box::new(on_next), on_error: Box::new(on_error), on_completed: Box::new(on_completed), is_stopped: Cell::new(false) }
}
}
pub(crate) fn throw(error: ObservableError) {
panic!("unhandled error of an observable sequence: {error}");
}
impl<T> IObserver<T> for AnonymousObserver<T> {
fn on_next(&self, value: T) {
if !self.is_stopped.get() {
(self.on_next)(value);
}
}
fn on_error(&self, error: ObservableError) {
if !self.is_stopped.replace(true) {
(self.on_error)(error);
}
}
fn on_completed(&self) {
if !self.is_stopped.replace(true) {
(self.on_completed)();
}
}
}
pub struct Observer;
impl Observer {
pub fn create<T: 'static>(on_next: impl Fn(T) + 'static) -> Rc<dyn IObserver<T>> {
Rc::new(AnonymousObserver::new(on_next, throw, || {}))
}
pub fn create_with_error<T: 'static>(
on_next: impl Fn(T) + 'static,
on_error: impl Fn(ObservableError) + 'static,
) -> Rc<dyn IObserver<T>> {
Rc::new(AnonymousObserver::new(on_next, on_error, || {}))
}
pub fn create_with_completed<T: 'static>(on_next: impl Fn(T) + 'static, on_completed: impl Fn() + 'static) -> Rc<dyn IObserver<T>> {
Rc::new(AnonymousObserver::new(on_next, throw, on_completed))
}
pub fn create_all<T: 'static>(
on_next: impl Fn(T) + 'static,
on_error: impl Fn(ObservableError) + 'static,
on_completed: impl Fn() + 'static,
) -> Rc<dyn IObserver<T>> {
Rc::new(AnonymousObserver::new(on_next, on_error, on_completed))
}
pub fn to_observer<T: 'static>(handler: impl Fn(Notification<T>) + 'static) -> Rc<dyn IObserver<T>> {
let handler = Rc::new(handler);
let (on_error, on_completed) = (handler.clone(), handler.clone());
Rc::new(AnonymousObserver::new(
move |value| handler(Notification::OnNext(value)),
move |error| on_error(Notification::OnError(error)),
move || on_completed(Notification::OnCompleted),
))
}
}
pub trait ObserverExt<T> {
fn to_notifier(&self) -> Box<dyn Fn(Notification<T>)>;
fn as_observer(&self) -> Rc<dyn IObserver<T>>;
fn checked(&self) -> Rc<dyn IObserver<T>>;
fn synchronize(&self) -> Rc<dyn IObserver<T>>;
fn synchronize_prevent_reentrancy(&self, prevent_reentrancy: bool) -> Rc<dyn IObserver<T>>;
fn synchronize_with_gate(&self, gate: Rc<dyn std::any::Any>) -> Rc<dyn IObserver<T>>;
fn synchronize_with_lock(&self, async_lock: Rc<AsyncLock>) -> Rc<dyn IObserver<T>>;
fn notify_on(&self, scheduler: Rc<dyn crate::concurrency::IScheduler>) -> Rc<dyn IObserver<T>>;
fn notify_on_context(&self, context: std::sync::Arc<dyn ferroui_system::threading::SynchronizationContext>) -> Rc<dyn IObserver<T>>;
fn to_progress(&self) -> Rc<dyn IProgress<T>>;
fn to_progress_on(&self, scheduler: Rc<dyn crate::concurrency::IScheduler>) -> Rc<dyn IProgress<T>>;
}
impl<T: 'static> ObserverExt<T> for Rc<dyn IObserver<T>> {
fn to_notifier(&self) -> Box<dyn Fn(Notification<T>)> {
let observer = self.clone();
Box::new(move |notification| notification.accept(&*observer))
}
fn as_observer(&self) -> Rc<dyn IObserver<T>> {
let (next, error, completed) = (self.clone(), self.clone(), self.clone());
Rc::new(AnonymousObserver::new(move |v| next.on_next(v), move |e| error.on_error(e), move || completed.on_completed()))
}
fn checked(&self) -> Rc<dyn IObserver<T>> {
Rc::new(CheckedObserver { observer: self.clone(), state: Cell::new(CheckedState::Idle) })
}
fn synchronize(&self) -> Rc<dyn IObserver<T>> {
synchronized(self.clone(), Rc::new(()))
}
fn synchronize_prevent_reentrancy(&self, prevent_reentrancy: bool) -> Rc<dyn IObserver<T>> {
if prevent_reentrancy {
return Rc::new(ObserverBase::new(AsyncLockCore { observer: self.clone(), gate: Rc::new(AsyncLock::new()) }));
}
synchronized(self.clone(), Rc::new(()))
}
fn synchronize_with_gate(&self, gate: Rc<dyn std::any::Any>) -> Rc<dyn IObserver<T>> {
synchronized(self.clone(), gate)
}
fn synchronize_with_lock(&self, async_lock: Rc<AsyncLock>) -> Rc<dyn IObserver<T>> {
Rc::new(ObserverBase::new(AsyncLockCore { observer: self.clone(), gate: async_lock }))
}
fn notify_on(&self, scheduler: Rc<dyn crate::concurrency::IScheduler>) -> Rc<dyn IObserver<T>> {
Rc::new(NotifyOnObserver { inner: crate::internal::ScheduledObserver::new(scheduler, self.clone()) })
}
fn notify_on_context(&self, context: std::sync::Arc<dyn ferroui_system::threading::SynchronizationContext>) -> Rc<dyn IObserver<T>> {
let scheduler: Rc<dyn crate::concurrency::IScheduler> = crate::concurrency::SynchronizationContextScheduler::new(context);
self.notify_on(scheduler)
}
fn to_progress(&self) -> Rc<dyn IProgress<T>> {
let observer = self.clone();
Rc::new(AnonymousProgress { progress: Box::new(move |value| observer.on_next(value)) })
}
fn to_progress_on(&self, scheduler: Rc<dyn crate::concurrency::IScheduler>) -> Rc<dyn IProgress<T>> {
let observer = self.notify_on(scheduler);
Rc::new(AnonymousProgress { progress: Box::new(move |value| observer.on_next(value)) })
}
}
struct AnonymousProgress<T> {
progress: Box<dyn Fn(T)>,
}
impl<T> IProgress<T> for AnonymousProgress<T> {
fn report(&self, value: T) {
(self.progress)(value);
}
}
pub trait ProgressExt<T> {
fn to_observer(&self) -> Rc<dyn IObserver<T>>;
}
impl<T: 'static> ProgressExt<T> for Rc<dyn IProgress<T>> {
fn to_observer(&self) -> Rc<dyn IObserver<T>> {
let progress = self.clone();
Observer::create(move |value| progress.report(value))
}
}
fn synchronized<T: 'static>(observer: Rc<dyn IObserver<T>>, gate: Rc<dyn std::any::Any>) -> Rc<dyn IObserver<T>> {
Rc::new(ObserverBase::new(SynchronizedCore { observer, _gate: gate }))
}
struct SynchronizedCore<T> {
observer: Rc<dyn IObserver<T>>,
_gate: Rc<dyn std::any::Any>,
}
impl<T> ObserverCore<T> for SynchronizedCore<T> {
fn on_next_core(&self, value: T) {
self.observer.on_next(value);
}
fn on_error_core(&self, error: ObservableError) {
self.observer.on_error(error);
}
fn on_completed_core(&self) {
self.observer.on_completed();
}
}
struct AsyncLockCore<T> {
observer: Rc<dyn IObserver<T>>,
gate: Rc<AsyncLock>,
}
impl<T: 'static> ObserverCore<T> for AsyncLockCore<T> {
fn on_next_core(&self, value: T) {
let observer = self.observer.clone();
self.gate.wait(move || observer.on_next(value));
}
fn on_error_core(&self, error: ObservableError) {
let observer = self.observer.clone();
self.gate.wait(move || observer.on_error(error));
}
fn on_completed_core(&self) {
let observer = self.observer.clone();
self.gate.wait(move || observer.on_completed());
}
}
struct NotifyOnObserver<T: 'static> {
inner: Rc<crate::internal::ScheduledObserver<T>>,
}
impl<T: 'static> IObserver<T> for NotifyOnObserver<T> {
fn on_next(&self, value: T) {
use crate::internal::IScheduledObserver;
self.inner.on_next(value);
self.inner.ensure_active();
}
fn on_error(&self, error: ObservableError) {
use crate::internal::IScheduledObserver;
self.inner.on_error(error);
self.inner.ensure_active();
}
fn on_completed(&self) {
use crate::internal::IScheduledObserver;
self.inner.on_completed();
self.inner.ensure_active();
}
}
#[derive(Clone, Copy, PartialEq, Eq)]
enum CheckedState {
Idle,
Busy,
Done,
}
struct CheckedObserver<T> {
observer: Rc<dyn IObserver<T>>,
state: Cell<CheckedState>,
}
impl<T> CheckedObserver<T> {
fn check_access(&self) {
match self.state.get() {
CheckedState::Busy => panic!("This observer is currently busy with a notification."),
CheckedState::Done => panic!("This observer has already terminated."),
CheckedState::Idle => self.state.set(CheckedState::Busy),
}
}
}
impl<T> IObserver<T> for CheckedObserver<T> {
fn on_next(&self, value: T) {
self.check_access();
self.observer.on_next(value);
self.state.set(CheckedState::Idle);
}
fn on_error(&self, error: ObservableError) {
self.check_access();
self.observer.on_error(error);
self.state.set(CheckedState::Done);
}
fn on_completed(&self) {
self.check_access();
self.observer.on_completed();
self.state.set(CheckedState::Done);
}
}