ferroui_base/reactive/
composite_disposable.rs1use super::IDisposable;
2use std::cell::{Cell, RefCell};
3use std::rc::Rc;
4
5#[derive(Default)]
7pub struct CompositeDisposable {
8 disposables: RefCell<Vec<Rc<dyn IDisposable>>>,
9 disposed: Cell<bool>,
10}
11
12impl CompositeDisposable {
13 pub fn new() -> Self {
14 Self::default()
15 }
16
17 pub fn with_capacity(capacity: usize) -> Self {
18 Self { disposables: RefCell::new(Vec::with_capacity(capacity)), disposed: Cell::new(false) }
19 }
20
21 pub fn from_disposables(disposables: impl IntoIterator<Item = Rc<dyn IDisposable>>) -> Self {
22 Self { disposables: RefCell::new(disposables.into_iter().collect()), disposed: Cell::new(false) }
23 }
24
25 pub fn is_disposed(&self) -> bool {
26 self.disposed.get()
27 }
28
29 pub fn count(&self) -> usize {
30 self.disposables.borrow().len()
31 }
32
33 pub fn add(&self, item: Rc<dyn IDisposable>) {
36 if self.disposed.get() {
37 item.dispose();
38 } else {
39 self.disposables.borrow_mut().push(item);
40 }
41 }
42
43 pub fn remove(&self, item: &Rc<dyn IDisposable>) -> bool {
45 let removed = {
46 let mut list = self.disposables.borrow_mut();
47 list.iter().position(|d| Rc::ptr_eq(d, item)).map(|i| list.remove(i))
48 };
49 match removed {
50 Some(d) => {
51 d.dispose();
52 true
53 }
54 None => false,
55 }
56 }
57
58 pub fn contains(&self, item: &Rc<dyn IDisposable>) -> bool {
60 if self.disposed.get() {
61 return false;
62 }
63
64 self.disposables.borrow().iter().any(|d| Rc::ptr_eq(d, item))
65 }
66
67 pub fn copy_to(&self, array: &mut [Rc<dyn IDisposable>], array_index: usize) {
73 if array_index >= array.len() {
74 panic!("Specified argument was out of the range of valid values. (Parameter 'arrayIndex')");
75 }
76
77 if self.disposed.get() {
79 return;
80 }
81
82 let disposables = self.disposables.borrow();
83 if array_index + disposables.len() > array.len() {
84 panic!("Specified argument was out of the range of valid values. (Parameter 'arrayIndex')");
87 }
88
89 for (i, d) in disposables.iter().enumerate() {
90 array[array_index + i] = d.clone();
91 }
92 }
93
94 pub fn is_read_only(&self) -> bool {
96 false
97 }
98
99 pub fn get_enumerator(&self) -> std::vec::IntoIter<Rc<dyn IDisposable>> {
102 if self.disposed.get() {
103 return Vec::new().into_iter();
104 }
105
106 self.disposables.borrow().clone().into_iter()
108 }
109
110 pub fn clear(&self) {
112 let items = std::mem::take(&mut *self.disposables.borrow_mut());
113 for d in items {
114 d.dispose();
115 }
116 }
117}
118
119impl IDisposable for CompositeDisposable {
120 fn dispose(&self) {
121 if !self.disposed.replace(true) {
122 self.clear();
123 }
124 }
125}
126
127#[cfg(test)]
128mod tests {
129 use super::*;
131 use crate::reactive::Disposable;
132
133 #[test]
134 fn contains_enumerates_and_copies_its_disposables() {
135 let first = Disposable::create(|| {});
136 let second = Disposable::create(|| {});
137 let other = Disposable::create(|| {});
138 let target = CompositeDisposable::from_disposables([first.clone(), second.clone()]);
139
140 assert!(!target.is_read_only());
141 assert!(target.contains(&first));
142 assert!(!target.contains(&other));
143
144 let items: Vec<_> = target.get_enumerator().collect();
145 assert_eq!(2, items.len());
146 assert!(Rc::ptr_eq(&items[0], &first) && Rc::ptr_eq(&items[1], &second));
147
148 let mut array = vec![other.clone(), other.clone(), other.clone()];
149 target.copy_to(&mut array, 1);
150 assert!(Rc::ptr_eq(&array[0], &other));
151 assert!(Rc::ptr_eq(&array[1], &first));
152 assert!(Rc::ptr_eq(&array[2], &second));
153
154 target.dispose();
155 assert!(!target.contains(&first));
156 assert_eq!(0, target.get_enumerator().count());
157 }
158
159 #[test]
160 #[should_panic(expected = "arrayIndex")]
161 fn copy_to_throws_without_room() {
162 let item = Disposable::create(|| {});
163 let target = CompositeDisposable::from_disposables([item.clone(), item.clone()]);
164 let mut array = vec![item.clone(), item.clone()];
165 target.copy_to(&mut array, 1);
166 }
167}