use crate::reactive_command::ICommand;
use ferroui_reactive::disposables::CompositeDisposable;
use ferroui_system::{IDisposable, IObservable, IObserver, ObservableError};
use std::any::Any;
use std::cell::RefCell;
use std::rc::Rc;
pub struct CommandInvoker;
impl CommandInvoker {
pub fn invoke<T: 'static>(source: &Rc<dyn IObservable<T>>, command: Rc<dyn ICommand>) -> Rc<dyn IDisposable> {
source.subscribe(Rc::new(FixedCommandObserver { command }))
}
pub fn invoke_observed<T: 'static>(
source: &Rc<dyn IObservable<T>>,
commands: &Rc<dyn IObservable<Option<Rc<dyn ICommand>>>>,
) -> Rc<dyn IDisposable> {
let latch = Rc::new(CommandLatch { command: RefCell::new(None) });
let command_subscription = commands.subscribe(latch.clone());
let source_subscription = source.subscribe(Rc::new(LatchedCommandObserver { latch }));
CompositeDisposable::from_disposables([command_subscription, source_subscription])
}
}
fn rethrow(error: ObservableError) -> ! {
panic!("{error}")
}
struct FixedCommandObserver {
command: Rc<dyn ICommand>,
}
impl<T: 'static> IObserver<T> for FixedCommandObserver {
fn on_next(&self, value: T) {
let parameter: &dyn Any = &value;
if !self.command.can_execute(Some(parameter)) {
return;
}
self.command.execute(Some(parameter));
}
fn on_error(&self, error: ObservableError) {
rethrow(error);
}
fn on_completed(&self) {}
}
struct CommandLatch {
command: RefCell<Option<Rc<dyn ICommand>>>,
}
impl IObserver<Option<Rc<dyn ICommand>>> for CommandLatch {
fn on_next(&self, value: Option<Rc<dyn ICommand>>) {
*self.command.borrow_mut() = value;
}
fn on_error(&self, error: ObservableError) {
rethrow(error);
}
fn on_completed(&self) {}
}
struct LatchedCommandObserver {
latch: Rc<CommandLatch>,
}
impl<T: 'static> IObserver<T> for LatchedCommandObserver {
fn on_next(&self, value: T) {
let Some(command) = self.latch.command.borrow().clone() else { return };
let parameter: &dyn Any = &value;
if !command.can_execute(Some(parameter)) {
return;
}
command.execute(Some(parameter));
}
fn on_error(&self, error: ObservableError) {
rethrow(error);
}
fn on_completed(&self) {}
}