ferroui_base/reactive/
lightweight_observable_base.rs1use super::{Disposable, IDisposable, IObserver, ObservableError};
2use std::cell::RefCell;
3use std::rc::Rc;
4
5pub trait LightweightObservable<T>: 'static {
10 fn observable_base(&self) -> &LightweightObservableBase<T>;
12
13 fn initialize(&self);
15
16 fn deinitialize(&self);
19
20 fn subscribed(&self, _observer: &Rc<dyn IObserver<T>>, _first: bool) {}
23}
24
25pub struct LightweightObservableBase<T> {
29 error: RefCell<Option<ObservableError>>,
30 observers: RefCell<Option<Vec<Rc<dyn IObserver<T>>>>>,
31}
32
33impl<T> Default for LightweightObservableBase<T> {
34 fn default() -> Self {
35 Self::new()
36 }
37}
38
39impl<T> LightweightObservableBase<T> {
40 pub fn new() -> Self {
41 Self { error: RefCell::new(None), observers: RefCell::new(Some(Vec::new())) }
42 }
43
44 pub fn has_observers(&self) -> bool {
46 self.observers.borrow().as_ref().is_some_and(|observers| !observers.is_empty())
47 }
48
49 pub fn subscribe<O: LightweightObservable<T>>(owner: &Rc<O>, observer: Rc<dyn IObserver<T>>) -> Rc<dyn IDisposable>
52 where
53 T: 'static,
54 {
55 let base = owner.observable_base();
56 let first = {
57 let mut observers = base.observers.borrow_mut();
58 match observers.as_mut() {
59 Some(observers) => {
60 let first = observers.is_empty();
61 observers.push(observer.clone());
62 first
63 }
64 None => {
65 drop(observers);
66 let error = base.error.borrow().clone();
67 match error {
68 Some(error) => observer.on_error(error),
69 None => observer.on_completed(),
70 }
71 return Disposable::empty();
72 }
73 }
74 };
75
76 if first {
77 owner.initialize();
78 }
79
80 owner.subscribed(&observer, first);
81
82 Rc::new(RemoveObserver { parent: RefCell::new(Some((owner.clone(), observer))) })
83 }
84
85 fn remove<O: LightweightObservable<T>>(owner: &O, observer: &Rc<dyn IObserver<T>>) {
86 let base = owner.observable_base();
87 let now_empty = {
88 let mut observers = base.observers.borrow_mut();
89 let Some(observers) = observers.as_mut() else { return };
90 if let Some(index) = observers.iter().position(|o| std::ptr::addr_eq(Rc::as_ptr(o), Rc::as_ptr(observer))) {
91 observers.remove(index);
92 }
93 if observers.is_empty() {
94 observers.shrink_to_fit();
95 true
96 } else {
97 false
98 }
99 };
100 if now_empty {
101 owner.deinitialize();
102 }
103 }
104
105 fn snapshot(&self) -> Option<Vec<Rc<dyn IObserver<T>>>> {
108 self.observers.borrow().clone()
109 }
110
111 pub fn publish_next(&self, value: T)
113 where
114 T: Clone,
115 {
116 let Some(observers) = self.snapshot() else { return };
117 for observer in &observers {
118 observer.on_next(value.clone());
119 }
120 }
121
122 pub fn publish_completed<O: LightweightObservable<T>>(owner: &O) {
125 let Some(observers) = owner.observable_base().observers.borrow_mut().take() else { return };
126 for observer in &observers {
127 observer.on_completed();
128 }
129 owner.deinitialize();
130 }
131
132 pub fn publish_error<O: LightweightObservable<T>>(owner: &O, error: ObservableError) {
135 let base = owner.observable_base();
136 let Some(observers) = base.observers.borrow_mut().take() else { return };
137 *base.error.borrow_mut() = Some(error.clone());
138 for observer in &observers {
139 observer.on_error(error.clone());
140 }
141 owner.deinitialize();
142 }
143}
144
145struct RemoveObserver<O, T> {
147 parent: RefCell<Option<(Rc<O>, Rc<dyn IObserver<T>>)>>,
148}
149
150impl<O: LightweightObservable<T>, T: 'static> IDisposable for RemoveObserver<O, T> {
151 fn dispose(&self) {
152 let parent = self.parent.borrow_mut().take();
153 if let Some((parent, observer)) = parent {
154 LightweightObservableBase::remove(&*parent, &observer);
155 }
156 }
157}