1use super::{ChangeSet, IObservableCache, ObservableCache, ObservableCacheEx};
6use crate::diagnostics::ChangeSummary;
7use ferroui_reactive::disposables::Disposable;
8use ferroui_reactive::ObservableExt;
9use ferroui_system::{IDisposable, IObservable, ObservableError};
10use std::cell::{Cell, RefCell};
11use std::hash::Hash;
12use std::rc::Rc;
13
14struct State<T, K> {
15 messages: RefCell<Vec<ChangeSet<T, K>>>,
16 error: RefCell<Option<ObservableError>>,
17 is_completed: Cell<bool>,
18 summary: RefCell<ChangeSummary>,
19}
20
21pub struct ChangeSetAggregator<T, K> {
24 state: Rc<State<T, K>>,
25 data: ObservableCache<T, K>,
26 disposer: Rc<dyn IDisposable>,
27}
28
29impl<T: Clone + 'static, K: Eq + Hash + Clone + 'static> ChangeSetAggregator<T, K> {
30 pub fn new(source: Rc<dyn IObservable<ChangeSet<T, K>>>) -> Self {
32 let state = Rc::new(State { messages: RefCell::new(Vec::new()), error: RefCell::new(None), is_completed: Cell::new(false), summary: RefCell::new(ChangeSummary::empty()) });
33 let published = source.publish();
34 let published_source: Rc<dyn IObservable<ChangeSet<T, K>>> = published.clone();
35 let data = published_source.as_observable_cache();
36 let (on_next, on_error, on_completed) = (state.clone(), state.clone(), state.clone());
37 let results = published_source.subscribe_all(
38 move |changes| on_next.messages.borrow_mut().push(changes),
39 move |error| *on_error.error.borrow_mut() = Some(error),
40 move || on_completed.is_completed.set(true),
41 );
42 let summary_target = state.clone();
43 let summariser = published_source.collect_update_stats().subscribe_all(move |summary| *summary_target.summary.borrow_mut() = summary, |_| {}, || {});
44 let connected = published.connect();
45 let cache = data.clone();
46 let disposer = Disposable::create(move || {
47 cache.dispose();
48 connected.dispose();
49 summariser.dispose();
50 results.dispose();
51 });
52 Self { state, data, disposer }
53 }
54
55 pub fn data(&self) -> &ObservableCache<T, K> {
57 &self.data
58 }
59
60 pub fn messages(&self) -> Vec<ChangeSet<T, K>> {
62 self.state.messages.borrow().clone()
63 }
64
65 pub fn error(&self) -> Option<ObservableError> {
67 self.state.error.borrow().clone()
68 }
69
70 pub fn is_completed(&self) -> bool {
72 self.state.is_completed.get()
73 }
74
75 pub fn summary(&self) -> ChangeSummary {
77 self.state.summary.borrow().clone()
78 }
79
80 pub fn count(&self) -> usize {
82 self.data.count()
83 }
84}
85
86impl<T, K> IDisposable for ChangeSetAggregator<T, K> {
87 fn dispose(&self) {
88 self.disposer.dispose();
89 }
90}
91
92impl<T, K> Drop for ChangeSetAggregator<T, K> {
93 fn drop(&mut self) {
95 self.disposer.dispose();
96 }
97}
98
99struct TypedState<C> {
100 messages: RefCell<Vec<C>>,
101 error: RefCell<Option<ObservableError>>,
102 is_completed: Cell<bool>,
103 summary: RefCell<ChangeSummary>,
104}
105
106pub struct TypedChangeSetAggregator<T, K, C> {
115 state: Rc<TypedState<C>>,
116 data: ObservableCache<T, K>,
117 disposer: Rc<dyn IDisposable>,
118}
119
120pub type SortedChangeSetAggregator<T, K> = TypedChangeSetAggregator<T, K, super::SortedChangeSet<T, K>>;
122
123pub type PagedChangeSetAggregator<T, K> = TypedChangeSetAggregator<T, K, super::PagedChangeSet<T, K>>;
125
126pub type VirtualChangeSetAggregator<T, K> = TypedChangeSetAggregator<T, K, super::VirtualChangeSet<T, K>>;
128
129pub type ContextChangeSetAggregator<T, K, X> = TypedChangeSetAggregator<T, K, super::ChangeSetWithContext<T, K, X>>;
131
132impl<T: Clone + 'static, K: Eq + Hash + Clone + 'static, C: super::IChangeSet<T, K>> TypedChangeSetAggregator<T, K, C> {
133 pub fn new(source: Rc<dyn IObservable<C>>) -> Self {
136 let state = Rc::new(TypedState { messages: RefCell::new(Vec::new()), error: RefCell::new(None), is_completed: Cell::new(false), summary: RefCell::new(ChangeSummary::empty()) });
137 let published = source.publish();
138 let published_source: Rc<dyn IObservable<C>> = published.clone();
139 let data = ObservableCacheEx::<T, K>::as_observable_cache(&published_source);
140 let (on_next, on_error, on_completed) = (state.clone(), state.clone(), state.clone());
141 let results = published_source.subscribe_all(
142 move |changes| on_next.messages.borrow_mut().push(changes),
143 move |error| *on_error.error.borrow_mut() = Some(error),
144 move || on_completed.is_completed.set(true),
145 );
146 let summary_target = state.clone();
147 let summariser = ObservableCacheEx::<T, K>::collect_update_stats(&published_source).subscribe_all(move |summary| *summary_target.summary.borrow_mut() = summary, |_| {}, || {});
148 let connected = published.connect();
149 let cache = data.clone();
150 let disposer = Disposable::create(move || {
151 cache.dispose();
152 connected.dispose();
153 summariser.dispose();
154 results.dispose();
155 });
156 Self { state, data, disposer }
157 }
158
159 pub fn data(&self) -> &ObservableCache<T, K> {
161 &self.data
162 }
163
164 pub fn messages(&self) -> Vec<C> {
166 self.state.messages.borrow().clone()
167 }
168
169 pub fn error(&self) -> Option<ObservableError> {
171 self.state.error.borrow().clone()
172 }
173
174 pub fn is_completed(&self) -> bool {
176 self.state.is_completed.get()
177 }
178
179 pub fn summary(&self) -> ChangeSummary {
181 self.state.summary.borrow().clone()
182 }
183
184 pub fn count(&self) -> usize {
186 self.data.count()
187 }
188}
189
190impl<T, K, C> IDisposable for TypedChangeSetAggregator<T, K, C> {
191 fn dispose(&self) {
192 self.disposer.dispose();
193 }
194}
195
196impl<T, K, C> Drop for TypedChangeSetAggregator<T, K, C> {
197 fn drop(&mut self) {
199 self.disposer.dispose();
200 }
201}
202
203struct GroupAggregatorState<T, K, G> {
204 messages: std::cell::RefCell<Vec<super::GroupChangeSet<T, K, G>>>,
205 error: std::cell::RefCell<Option<ferroui_system::ObservableError>>,
206 is_completed: std::cell::Cell<bool>,
207 summary: std::cell::RefCell<crate::diagnostics::ChangeSummary>,
208}
209
210pub struct GroupChangeSetAggregator<T, K, G> {
214 state: std::rc::Rc<GroupAggregatorState<T, K, G>>,
215 data: super::ObservableCache<super::Group<T, K, G>, G>,
216 groups: super::ObservableCache<std::rc::Rc<ChangeSetAggregator<T, K>>, G>,
217 disposer: std::rc::Rc<dyn ferroui_system::IDisposable>,
218}
219
220impl<T: Clone + 'static, K: Eq + std::hash::Hash + Clone + 'static, G: Eq + std::hash::Hash + Clone + 'static> GroupChangeSetAggregator<T, K, G> {
221 pub fn new(source: std::rc::Rc<dyn ferroui_system::IObservable<super::GroupChangeSet<T, K, G>>>) -> Self {
223 let state = std::rc::Rc::new(GroupAggregatorState {
224 messages: std::cell::RefCell::new(Vec::new()),
225 error: std::cell::RefCell::new(None),
226 is_completed: std::cell::Cell::new(false),
227 summary: std::cell::RefCell::new(crate::diagnostics::ChangeSummary::empty()),
228 });
229 let published = ferroui_reactive::ObservableExt::publish(&source);
230 let published_source: std::rc::Rc<dyn ferroui_system::IObservable<super::GroupChangeSet<T, K, G>>> = published.clone();
231
232 let data = super::ObservableCache::from_source(published_source.clone());
233 let groups = super::ObservableCache::from_source(group_aggregators(published_source.clone()));
235 let (on_next, on_error, on_completed) = (state.clone(), state.clone(), state.clone());
236 let results = ferroui_reactive::ObservableExt::subscribe_all(
237 &published_source,
238 move |changes| on_next.messages.borrow_mut().push(changes),
239 move |error| *on_error.error.borrow_mut() = Some(error),
240 move || on_completed.is_completed.set(true),
241 );
242 let summary_target = state.clone();
243 let summariser = ferroui_reactive::ObservableExt::subscribe_all(
244 &super::ObservableCacheEx::collect_update_stats(&published_source),
245 move |summary| *summary_target.summary.borrow_mut() = summary,
246 |_| {},
247 || {},
248 );
249 let connected = published.connect();
250
251 let (data_cache, groups_cache) = (data.clone(), groups.clone());
252 let disposer = ferroui_reactive::disposables::Disposable::create(move || {
253 ferroui_system::IDisposable::dispose(&data_cache);
254 ferroui_system::IDisposable::dispose(&groups_cache);
255 results.dispose();
256 summariser.dispose();
257 connected.dispose();
258 });
259 Self { state, data, groups, disposer }
260 }
261
262 pub fn data(&self) -> &super::ObservableCache<super::Group<T, K, G>, G> {
264 &self.data
265 }
266
267 pub fn groups(&self) -> &super::ObservableCache<std::rc::Rc<ChangeSetAggregator<T, K>>, G> {
269 &self.groups
270 }
271
272 pub fn messages(&self) -> Vec<super::GroupChangeSet<T, K, G>> {
274 self.state.messages.borrow().clone()
275 }
276
277 pub fn error(&self) -> Option<ferroui_system::ObservableError> {
279 self.state.error.borrow().clone()
280 }
281
282 pub fn is_completed(&self) -> bool {
284 self.state.is_completed.get()
285 }
286
287 pub fn summary(&self) -> crate::diagnostics::ChangeSummary {
289 self.state.summary.borrow().clone()
290 }
291}
292
293impl<T, K, G> ferroui_system::IDisposable for GroupChangeSetAggregator<T, K, G> {
294 fn dispose(&self) {
295 self.disposer.dispose();
296 }
297}
298
299impl<T, K, G> Drop for GroupChangeSetAggregator<T, K, G> {
300 fn drop(&mut self) {
302 self.disposer.dispose();
303 }
304}
305
306fn group_aggregators<T: Clone + 'static, K: Eq + std::hash::Hash + Clone + 'static, G: Eq + std::hash::Hash + Clone + 'static>(
309 source: std::rc::Rc<dyn ferroui_system::IObservable<super::GroupChangeSet<T, K, G>>>,
310) -> std::rc::Rc<dyn ferroui_system::IObservable<super::ChangeSet<std::rc::Rc<ChangeSetAggregator<T, K>>, G>>> {
311 ferroui_reactive::Observable::create(move |observer: std::rc::Rc<dyn ferroui_system::IObserver<super::ChangeSet<std::rc::Rc<ChangeSetAggregator<T, K>>, G>>>| {
312 let aggregators = std::rc::Rc::new(std::cell::RefCell::new(super::ChangeAwareCache::<std::rc::Rc<ChangeSetAggregator<T, K>>, G>::new()));
313 let (target, on_error, on_completed) = (aggregators.clone(), observer.clone(), observer.clone());
314 let subscription = ferroui_reactive::ObservableExt::subscribe_all(
315 &source,
316 move |changes: super::GroupChangeSet<T, K, G>| {
317 let mut replaced = Vec::new();
318 let output = {
319 let mut cache = target.borrow_mut();
320 for change in &changes {
321 match change.reason() {
322 super::ChangeReason::Add | super::ChangeReason::Update => {
323 let connected = super::IConnectableCache::connect(change.current().observable_cache());
324 let aggregator = std::rc::Rc::new(ChangeSetAggregator::new(connected));
325 replaced.extend(super::IQuery::lookup(&*cache, change.key()));
326 super::ICache::add_or_update(&mut *cache, aggregator, change.key().clone());
327 }
328 super::ChangeReason::Remove => {
329 replaced.extend(super::IQuery::lookup(&*cache, change.key()));
330 super::ICache::remove(&mut *cache, change.key());
331 }
332 super::ChangeReason::Refresh => super::ICache::refresh(&mut *cache, change.key()),
333 super::ChangeReason::Moved => {}
334 }
335 }
336 cache.capture_changes()
337 };
338 observer.on_next(output);
339 for aggregator in replaced {
340 ferroui_system::IDisposable::dispose(&*aggregator);
341 }
342 },
343 move |error| on_error.on_error(error),
344 move || on_completed.on_completed(),
345 );
346 ferroui_reactive::disposables::Disposable::create(move || {
347 subscription.dispose();
348 let remaining = super::IQuery::items(&*aggregators.borrow());
349 for aggregator in remaining {
350 ferroui_system::IDisposable::dispose(&*aggregator);
351 }
352 })
353 })
354}
355
356pub trait GroupChangeSetAggregatorEx<T, K, G> {
362 fn as_group_aggregator(&self) -> GroupChangeSetAggregator<T, K, G>;
364}
365
366impl<T: Clone + 'static, K: Eq + std::hash::Hash + Clone + 'static, G: Eq + std::hash::Hash + Clone + 'static> GroupChangeSetAggregatorEx<T, K, G>
367 for std::rc::Rc<dyn ferroui_system::IObservable<super::GroupChangeSet<T, K, G>>>
368{
369 fn as_group_aggregator(&self) -> GroupChangeSetAggregator<T, K, G> {
370 GroupChangeSetAggregator::new(self.clone())
371 }
372}