ferroui_base/reactive/
disposable.rs1use std::cell::{Cell, RefCell};
2use std::rc::Rc;
3
4use super::IDisposable;
5
6pub struct Disposable;
8
9struct EmptyDisposable;
10
11impl IDisposable for EmptyDisposable {
12 fn dispose(&self) {}
13}
14
15struct AnonymousDisposable<F: FnOnce()> {
16 action: Cell<Option<F>>,
17}
18
19impl<F: FnOnce()> IDisposable for AnonymousDisposable<F> {
20 fn dispose(&self) {
21 if let Some(action) = self.action.take() {
22 action();
23 }
24 }
25}
26
27impl Disposable {
28 pub fn empty() -> Rc<dyn IDisposable> {
30 crate::ferro_thread_local! {
31 static EMPTY: Rc<dyn IDisposable> = Rc::new(EmptyDisposable);
32 }
33 EMPTY.with(Rc::clone)
34 }
35
36 pub fn create(action: impl FnOnce() + 'static) -> Rc<dyn IDisposable> {
38 Rc::new(AnonymousDisposable { action: Cell::new(Some(action)) })
39 }
40
41 pub fn create_with_state<TState: 'static>(
44 state: TState,
45 dispose: impl FnOnce(TState) + 'static,
46 ) -> Rc<dyn IDisposable> {
47 Rc::new(AnonymousDisposable { action: Cell::new(Some(move || dispose(state))) })
48 }
49}
50
51#[derive(Default)]
54pub struct SerialDisposable {
55 current: RefCell<Option<Rc<dyn IDisposable>>>,
56 disposed: Cell<bool>,
57}
58
59impl SerialDisposable {
60 pub fn new() -> Self {
61 Self::default()
62 }
63
64 pub fn set(&self, value: Option<Rc<dyn IDisposable>>) {
65 if self.disposed.get() {
66 if let Some(v) = value {
67 v.dispose();
68 }
69 return;
70 }
71 let old = self.current.replace(value);
72 if let Some(old) = old {
73 old.dispose();
74 }
75 }
76}
77
78impl IDisposable for SerialDisposable {
79 fn dispose(&self) {
80 if !self.disposed.replace(true) {
81 if let Some(old) = self.current.take() {
82 old.dispose();
83 }
84 }
85 }
86}
87
88#[cfg(test)]
89mod tests {
90 use super::*;
92
93 #[test]
94 fn a_disposable_with_state_gives_it_to_the_action_once() {
95 let seen = Rc::new(RefCell::new(Vec::new()));
96 let s = seen.clone();
97 let target = Disposable::create_with_state(5, move |state: i32| s.borrow_mut().push(state));
98
99 assert!(seen.borrow().is_empty());
100 target.dispose();
101 target.dispose();
102 assert_eq!(vec![5], *seen.borrow());
103 }
104}