ferroui_reactiveui/binding/command_binding/
command_invoker.rs1use crate::reactive_command::ICommand;
5use ferroui_reactive::disposables::CompositeDisposable;
6use ferroui_system::{IDisposable, IObservable, IObserver, ObservableError};
7use std::any::Any;
8use std::cell::RefCell;
9use std::rc::Rc;
10
11pub struct CommandInvoker;
21
22impl CommandInvoker {
23 pub fn invoke<T: 'static>(source: &Rc<dyn IObservable<T>>, command: Rc<dyn ICommand>) -> Rc<dyn IDisposable> {
26 source.subscribe(Rc::new(FixedCommandObserver { command }))
27 }
28
29 pub fn invoke_observed<T: 'static>(
33 source: &Rc<dyn IObservable<T>>,
34 commands: &Rc<dyn IObservable<Option<Rc<dyn ICommand>>>>,
35 ) -> Rc<dyn IDisposable> {
36 let latch = Rc::new(CommandLatch { command: RefCell::new(None) });
37 let command_subscription = commands.subscribe(latch.clone());
38 let source_subscription = source.subscribe(Rc::new(LatchedCommandObserver { latch }));
39 CompositeDisposable::from_disposables([command_subscription, source_subscription])
40 }
41}
42
43fn rethrow(error: ObservableError) -> ! {
45 panic!("{error}")
46}
47
48struct FixedCommandObserver {
50 command: Rc<dyn ICommand>,
51}
52
53impl<T: 'static> IObserver<T> for FixedCommandObserver {
54 fn on_next(&self, value: T) {
55 let parameter: &dyn Any = &value;
56 if !self.command.can_execute(Some(parameter)) {
57 return;
58 }
59 self.command.execute(Some(parameter));
60 }
61
62 fn on_error(&self, error: ObservableError) {
63 rethrow(error);
64 }
65
66 fn on_completed(&self) {}
67}
68
69struct CommandLatch {
71 command: RefCell<Option<Rc<dyn ICommand>>>,
72}
73
74impl IObserver<Option<Rc<dyn ICommand>>> for CommandLatch {
75 fn on_next(&self, value: Option<Rc<dyn ICommand>>) {
76 *self.command.borrow_mut() = value;
77 }
78
79 fn on_error(&self, error: ObservableError) {
80 rethrow(error);
81 }
82
83 fn on_completed(&self) {}
84}
85
86struct LatchedCommandObserver {
88 latch: Rc<CommandLatch>,
89}
90
91impl<T: 'static> IObserver<T> for LatchedCommandObserver {
92 fn on_next(&self, value: T) {
93 let Some(command) = self.latch.command.borrow().clone() else { return };
94 let parameter: &dyn Any = &value;
95 if !command.can_execute(Some(parameter)) {
96 return;
97 }
98 command.execute(Some(parameter));
99 }
100
101 fn on_error(&self, error: ObservableError) {
102 rethrow(error);
103 }
104
105 fn on_completed(&self) {}
106}