1use super::internal::{KeySelector, ReaderWriter};
5use super::{Change, ChangeReason, ChangeSet, ICacheUpdater, IChangeSet, ISourceUpdater, ObservableCacheEx};
6use crate::internal::DeliveryQueue;
7use ferroui_reactive::disposables::Disposable;
8use ferroui_reactive::subjects::{BehaviorSubject, Subject, SubjectBase};
9use ferroui_reactive::{Observable, ObservableExt};
10use ferroui_system::collections::generic::Dictionary;
11use ferroui_system::{IDisposable, IObservable, IObserver, ObservableError};
12use std::cell::{Cell, RefCell};
13use std::hash::Hash;
14use std::rc::{Rc, Weak};
15
16pub trait IConnectableCache<T, K> {
18 fn count_changed(&self) -> Rc<dyn IObservable<usize>>;
20 fn connect_with(&self, predicate: Option<Rc<dyn Fn(&T) -> bool>>, suppress_empty_change_sets: bool) -> Rc<dyn IObservable<ChangeSet<T, K>>>;
23 fn preview_with(&self, predicate: Option<Rc<dyn Fn(&T) -> bool>>) -> Rc<dyn IObservable<ChangeSet<T, K>>>;
25 fn watch(&self, key: K) -> Rc<dyn IObservable<Change<T, K>>>;
27
28 fn connect(&self) -> Rc<dyn IObservable<ChangeSet<T, K>>> {
30 self.connect_with(None, true)
31 }
32
33 fn connect_filtered(&self, predicate: impl Fn(&T) -> bool + 'static) -> Rc<dyn IObservable<ChangeSet<T, K>>>
35 where
36 Self: Sized,
37 {
38 self.connect_with(Some(Rc::new(predicate)), true)
39 }
40
41 fn preview(&self) -> Rc<dyn IObservable<ChangeSet<T, K>>> {
43 self.preview_with(None)
44 }
45}
46
47pub trait IObservableCache<T, K>: IConnectableCache<T, K> + IDisposable {
49 fn count(&self) -> usize;
51 fn items(&self) -> Vec<T>;
53 fn keys(&self) -> Vec<K>;
55 fn key_values(&self) -> Dictionary<K, T>;
57 fn lookup(&self, key: &K) -> Option<T>;
59}
60
61#[derive(Clone)]
63struct CacheUpdate<T, K> {
64 changes: Option<ChangeSet<T, K>>,
65 count: usize,
66 version: u64,
67}
68
69struct SuspensionTracker<T, K> {
71 are_notifications_suspended: Rc<BehaviorSubject<bool>>,
72 pending_changes: Vec<Change<T, K>>,
73 count_suspend_count: usize,
74 notify_suspend_count: usize,
75 disposed: bool,
76}
77
78impl<T: Clone + 'static, K: Clone + 'static> SuspensionTracker<T, K> {
79 fn new() -> Self {
80 Self { are_notifications_suspended: BehaviorSubject::new(false), pending_changes: Vec::new(), count_suspend_count: 0, notify_suspend_count: 0, disposed: false }
81 }
82
83 fn is_count_suspended(&self) -> bool {
84 self.count_suspend_count > 0
85 }
86
87 fn are_notifications_suspended(&self) -> bool {
88 self.notify_suspend_count > 0
89 }
90}
91
92struct Inner<T, K> {
93 this: Weak<Inner<T, K>>,
94 changes: Rc<Subject<ChangeSet<T, K>>>,
95 changes_preview: Rc<Subject<ChangeSet<T, K>>>,
96 count_changed: RefCell<Option<Rc<Subject<usize>>>>,
97 suspension: RefCell<Option<SuspensionTracker<T, K>>>,
98 reader_writer: ReaderWriter<T, K>,
99 notifications: Rc<DeliveryQueue<CacheUpdate<T, K>>>,
100 is_edit_in_progress: RefCell<Option<Rc<BehaviorSubject<bool>>>>,
101 edit_level: Cell<usize>,
102 current_version: Cell<u64>,
103 current_delivery_version: Cell<u64>,
104 loader: RefCell<Option<Rc<dyn IDisposable>>>,
105 disposed: Cell<bool>,
106}
107
108pub struct ObservableCache<T, K> {
111 inner: Rc<Inner<T, K>>,
112}
113
114impl<T, K> Clone for ObservableCache<T, K> {
115 fn clone(&self) -> Self {
116 Self { inner: self.inner.clone() }
117 }
118}
119
120impl<T: Clone + 'static, K: Eq + Hash + Clone + 'static> ObservableCache<T, K> {
121 fn create(key_selector: Option<KeySelector<T, K>>) -> Self {
122 let inner = Rc::new_cyclic(|this: &Weak<Inner<T, K>>| Inner {
123 this: this.clone(),
124 changes: Subject::new(),
125 changes_preview: Subject::new(),
126 count_changed: RefCell::new(None),
127 suspension: RefCell::new(None),
128 reader_writer: ReaderWriter::new(key_selector),
129 notifications: DeliveryQueue::new(Rc::new(CacheUpdateObserver { cache: this.clone() })),
130 is_edit_in_progress: RefCell::new(None),
131 edit_level: Cell::new(0),
132 current_version: Cell::new(0),
133 current_delivery_version: Cell::new(0),
134 loader: RefCell::new(None),
135 disposed: Cell::new(false),
136 });
137 Self { inner }
138 }
139
140 pub fn new(key_selector: impl Fn(&T) -> K + 'static) -> Self {
143 Self::create(Some(Rc::new(key_selector)))
144 }
145
146 pub fn without_key_selector() -> Self {
149 Self::create(None)
150 }
151
152 pub fn from_source<C: IChangeSet<T, K>>(source: Rc<dyn IObservable<C>>) -> Self {
154 let cache = Self::create(None);
155 let inner = Rc::downgrade(&cache.inner);
156 let (error_target, completed_target) = (inner.clone(), inner.clone());
157 let loader = source.change_sets().subscribe_all(
158 move |change_set| {
159 if let Some(inner) = inner.upgrade() {
160 let notifications = inner.notifications.acquire();
161 let preview_handler = inner.preview_handler();
162 let changes = inner.reader_writer.write_changes(&change_set, preview_handler.as_ref().map(|h| h as &dyn Fn(&ChangeSet<T, K>)), inner.changes.has_observers());
163 let version = inner.current_version.get() + 1;
164 inner.current_version.set(version);
165 notifications.enqueue_next(CacheUpdate { changes: Some(changes), count: inner.reader_writer.count(), version });
166 }
167 },
168 move |error| {
169 if let Some(inner) = error_target.upgrade() {
170 inner.notify_error(error);
171 }
172 },
173 move || {
174 if let Some(inner) = completed_target.upgrade() {
175 inner.notify_completed();
176 }
177 },
178 );
179 *cache.inner.loader.borrow_mut() = Some(loader);
180 cache
181 }
182
183 pub fn key_selector(&self) -> Option<&KeySelector<T, K>> {
185 self.inner.reader_writer.key_selector()
186 }
187
188 pub fn update_from_source(&self, update: impl FnOnce(&mut dyn ISourceUpdater<T, K>)) {
191 let mut update = Some(update);
192 self.inner.update(&mut |updater| (update.take().expect("an edit runs once"))(updater));
193 }
194
195 pub fn update_from_intermediate(&self, update: impl FnOnce(&mut dyn ICacheUpdater<T, K>)) {
197 let mut update = Some(update);
198 self.inner.update(&mut |updater| (update.take().expect("an edit runs once"))(updater));
199 }
200
201 pub(crate) fn get_initial_updates(&self, filter: Option<&dyn Fn(&T) -> bool>) -> ChangeSet<T, K> {
203 self.inner.reader_writer.get_initial_updates(filter)
204 }
205
206 pub fn suspend_count(&self) -> Rc<dyn IDisposable> {
209 self.inner.suspension_tracker(|tracker| tracker.count_suspend_count += 1);
210 let inner = self.inner.clone();
211 Disposable::create(move || inner.resume_count())
212 }
213
214 pub fn suspend_notifications(&self) -> Rc<dyn IDisposable> {
217 let suspended = self.inner.suspension_tracker(|tracker| {
218 tracker.notify_suspend_count += 1;
219 (tracker.notify_suspend_count == 1 && !tracker.disposed).then(|| tracker.are_notifications_suspended.clone())
220 });
221 if let Some(subject) = suspended {
222 subject.on_next(true);
223 }
224 let inner = self.inner.clone();
225 Disposable::create(move || inner.resume_notifications())
226 }
227}
228
229impl<T: Clone + 'static, K: Eq + Hash + Clone + 'static> Inner<T, K> {
230 fn preview_handler(&self) -> Option<impl Fn(&ChangeSet<T, K>)> {
231 if !self.changes_preview.has_observers() {
232 return None;
233 }
234 let this = self.this.clone();
235 Some(move |changes: &ChangeSet<T, K>| {
236 if let Some(inner) = this.upgrade() {
237 if changes.count() != 0 && !inner.notifications.is_terminated() {
238 inner.changes_preview.on_next(changes.clone());
239 }
240 }
241 })
242 }
243
244 fn edit_in_progress_subject(&self) -> Option<Rc<BehaviorSubject<bool>>> {
245 self.is_edit_in_progress.borrow().clone()
246 }
247
248 fn update(&self, update: &mut dyn FnMut(&mut dyn ISourceUpdater<T, K>)) {
250 let notifications = self.notifications.acquire();
251 let level = self.edit_level.get() + 1;
252 self.edit_level.set(level);
253 if level == 1 {
254 if let Some(subject) = self.edit_in_progress_subject() {
255 subject.on_next(true);
256 }
257 }
258 struct Unwind<'a, T: Clone + 'static, K: Eq + Hash + Clone + 'static> {
262 inner: &'a Inner<T, K>,
263 armed: bool,
264 }
265 impl<T: Clone + 'static, K: Eq + Hash + Clone + 'static> Drop for Unwind<'_, T, K> {
266 fn drop(&mut self) {
267 if self.armed {
268 let level = self.inner.edit_level.get() - 1;
269 self.inner.edit_level.set(level);
270 if level == 0 {
271 if let Some(subject) = self.inner.edit_in_progress_subject() {
272 subject.on_next(false);
273 }
274 }
275 }
276 }
277 }
278 let mut unwind = Unwind { inner: self, armed: true };
279 let changes = if level == 1 {
280 let preview_handler = self.preview_handler();
281 Some(self.reader_writer.write(update, preview_handler.as_ref().map(|h| h as &dyn Fn(&ChangeSet<T, K>)), self.changes.has_observers()))
282 } else {
283 self.reader_writer.write_nested(update);
284 None
285 };
286 unwind.armed = false;
287 drop(unwind);
288 let level = self.edit_level.get() - 1;
291 self.edit_level.set(level);
292 if let Some(changes) = changes {
293 if level == 0 {
294 let version = self.current_version.get() + 1;
295 self.current_version.set(version);
296 notifications.enqueue_next(CacheUpdate { changes: Some(changes), count: self.reader_writer.count(), version });
297 }
298 }
299 if level == 0 {
300 if let Some(subject) = self.edit_in_progress_subject() {
301 subject.on_next(false);
302 }
303 }
304 drop(notifications);
305 }
306
307 fn suspension_tracker<R>(&self, action: impl FnOnce(&mut SuspensionTracker<T, K>) -> R) -> R {
308 let mut tracker = self.suspension.borrow_mut();
309 action(tracker.get_or_insert_with(SuspensionTracker::new))
310 }
311
312 fn notify_completed(&self) {
313 self.notifications.acquire().enqueue_completed();
314 }
315
316 fn notify_error(&self, error: ObservableError) {
317 self.notifications.acquire().enqueue_error(error);
318 }
319
320 fn resume_count(&self) {
321 let notifications = self.notifications.acquire();
322 let resumed = self.suspension_tracker(|tracker| {
323 tracker.count_suspend_count -= 1;
324 tracker.count_suspend_count == 0
325 });
326 if resumed && self.count_changed.borrow().is_some() {
327 notifications.enqueue_next(CacheUpdate { changes: None, count: self.reader_writer.count(), version: 0 });
328 }
329 }
330
331 fn resume_notifications(&self) {
332 let notifications = self.notifications.acquire();
333 let (changes, emit_resume) = self.suspension_tracker(|tracker| {
334 tracker.notify_suspend_count -= 1;
335 if tracker.notify_suspend_count == 0 && !tracker.disposed {
336 let changes = (!tracker.pending_changes.is_empty()).then(|| ChangeSet::from(std::mem::take(&mut tracker.pending_changes)));
337 (changes, true)
338 } else {
339 (None, false)
340 }
341 });
342 if let Some(changes) = changes {
343 let version = self.current_version.get() + 1;
344 self.current_version.set(version);
345 notifications.enqueue_next(CacheUpdate { changes: Some(changes), count: self.reader_writer.count(), version });
346 }
347 if emit_resume {
348 let subject = self.suspension_tracker(|tracker| (!tracker.disposed).then(|| tracker.are_notifications_suspended.clone()));
349 if let Some(subject) = subject {
350 subject.on_next(false);
351 }
352 }
353 }
354
355 fn changes_after_snapshot(&self) -> Rc<dyn IObservable<ChangeSet<T, K>>> {
359 let changes = self.changes.as_observable();
360 if !self.notifications.has_pending() {
361 return changes;
362 }
363 let snapshot_version = self.current_version.get();
364 let this = self.this.clone();
365 changes.skip_while(move |_| this.upgrade().is_some_and(|inner| inner.current_delivery_version.get() <= snapshot_version))
366 }
367
368 fn create_connect_observable(&self, predicate: Option<Rc<dyn Fn(&T) -> bool>>, suppress_empty_change_sets: bool) -> Rc<dyn IObservable<ChangeSet<T, K>>> {
370 let this = self.this.clone();
371 Observable::create(move |observer: Rc<dyn IObserver<ChangeSet<T, K>>>| {
372 let Some(inner) = this.upgrade() else { return Disposable::empty() };
373 let (snapshot_inner, snapshot_predicate) = (this.clone(), predicate.clone());
374 let initial = Observable::defer(move || {
375 let initial = snapshot_inner.upgrade().map(|inner| inner.reader_writer.get_initial_updates(snapshot_predicate.as_deref())).unwrap_or_default();
376 Observable::return_(initial)
377 });
378 let mut changes = initial.concat(inner.changes_after_snapshot());
379 if let Some(predicate) = &predicate {
380 let predicate = predicate.clone();
381 changes = changes.filter_with(move |item: &T| predicate(item), suppress_empty_change_sets);
382 } else if suppress_empty_change_sets {
383 changes = changes.not_empty();
384 }
385 changes.subscribe(observer)
386 })
387 }
388
389 fn create_watch_observable(&self, key: K) -> Rc<dyn IObservable<Change<T, K>>> {
391 let this = self.this.clone();
392 Observable::create(move |observer: Rc<dyn IObserver<Change<T, K>>>| {
393 let Some(inner) = this.upgrade() else { return Disposable::empty() };
394 if let Some(initial) = inner.reader_writer.lookup(&key) {
395 observer.on_next(Change::new(ChangeReason::Add, key.clone(), initial));
396 }
397 let (completed, key) = (observer.clone(), key.clone());
398 inner.changes_after_snapshot().finally(move || completed.on_completed()).subscribe_next(move |changes: ChangeSet<T, K>| {
399 for change in &changes {
400 if change.key() == &key {
401 observer.on_next(change.clone());
402 }
403 }
404 })
405 })
406 }
407
408 fn create_deferred_notification_observable<V: Clone + 'static>(&self, factory: Rc<dyn Fn() -> Rc<dyn IObservable<V>>>) -> Rc<dyn IObservable<V>> {
411 let this = self.this.clone();
412 Observable::create(move |observer: Rc<dyn IObserver<V>>| {
413 let Some(inner) = this.upgrade() else { return Disposable::empty() };
414 let suspension_active = inner.suspension.borrow().is_some();
415 let edit_active = inner.is_edit_in_progress.borrow().is_some() || inner.edit_level.get() != 0;
416 let observable: Rc<dyn IObservable<V>> = match (suspension_active, edit_active) {
417 (false, false) => factory(),
418 (false, true) => {
419 let (inner2, factory2, this2) = (inner.clone(), factory.clone(), this.clone());
420 inner
421 .edit_in_progress()
422 .where_(|in_progress| !*in_progress)
423 .take(1)
424 .select_many(move |_| {
425 let suspended = inner2.suspension.borrow().as_ref().is_some_and(SuspensionTracker::are_notifications_suspended);
426 match suspended {
427 true => fully_deferred(&this2, &factory2),
428 false => factory2(),
429 }
430 })
431 }
432 (true, false) => {
433 let (inner2, factory2, this2) = (inner.clone(), factory.clone(), this.clone());
434 inner
435 .notifications_suspended()
436 .where_(|suspended| !*suspended)
437 .take(1)
438 .select_many(move |_| {
439 let editing = inner2.is_edit_in_progress.borrow().as_ref().is_some_and(|subject| subject.value().unwrap_or(false));
440 match editing {
441 true => fully_deferred(&this2, &factory2),
442 false => factory2(),
443 }
444 })
445 }
446 (true, true) => fully_deferred(&this, &factory),
447 };
448 observable.subscribe(observer)
449 })
450 }
451
452 fn edit_in_progress(&self) -> Rc<dyn IObservable<bool>> {
454 let mut subject = self.is_edit_in_progress.borrow_mut();
455 subject.get_or_insert_with(|| BehaviorSubject::new(self.edit_level.get() != 0)).as_observable()
456 }
457
458 fn notifications_suspended(&self) -> Rc<dyn IObservable<bool>> {
460 self.suspension_tracker(|tracker| tracker.are_notifications_suspended.as_observable())
461 }
462}
463
464fn fully_deferred<T: Clone + 'static, K: Eq + Hash + Clone + 'static, V: Clone + 'static>(
466 this: &Weak<Inner<T, K>>,
467 factory: &Rc<dyn Fn() -> Rc<dyn IObservable<V>>>,
468) -> Rc<dyn IObservable<V>> {
469 let Some(inner) = this.upgrade() else { return Observable::empty() };
470 let factory = factory.clone();
471 inner
472 .notifications_suspended()
473 .combine_latest(inner.edit_in_progress(), |suspended, editing| suspended || editing)
474 .where_(|deferred| !*deferred)
475 .take(1)
476 .select(move |_| factory())
477 .switch()
478}
479
480struct CacheUpdateObserver<T, K> {
483 cache: Weak<Inner<T, K>>,
484}
485
486impl<T: Clone + 'static, K: Eq + Hash + Clone + 'static> IObserver<CacheUpdate<T, K>> for CacheUpdateObserver<T, K> {
487 fn on_next(&self, value: CacheUpdate<T, K>) {
488 let Some(cache) = self.cache.upgrade() else { return };
489 if let Some(changes) = value.changes {
490 cache.current_delivery_version.set(value.version);
491 let held_back = cache.suspension.borrow_mut().as_mut().is_some_and(|tracker| {
492 if tracker.are_notifications_suspended() {
493 tracker.pending_changes.extend(changes.iter().cloned());
494 true
495 } else {
496 false
497 }
498 });
499 if !held_back {
500 cache.changes.on_next(changes);
501 }
502 }
503 let count_suspended = cache.suspension.borrow().as_ref().is_some_and(SuspensionTracker::is_count_suspended);
504 if !count_suspended {
505 let subject = cache.count_changed.borrow().clone();
506 if let Some(subject) = subject {
507 subject.on_next(value.count);
508 }
509 }
510 }
511
512 fn on_error(&self, error: ObservableError) {
513 let Some(cache) = self.cache.upgrade() else { return };
514 cache.changes_preview.on_error(error.clone());
515 cache.changes.on_error(error.clone());
516 if let Some(subject) = cache.edit_in_progress_subject() {
517 subject.on_error(error.clone());
518 }
519 let count = cache.count_changed.borrow().clone();
520 if let Some(subject) = count {
521 subject.on_error(error.clone());
522 }
523 let suspended = cache.suspension.borrow_mut().as_mut().map(|tracker| {
524 tracker.disposed = true;
525 tracker.are_notifications_suspended.clone()
526 });
527 if let Some(subject) = suspended {
528 subject.on_error(error);
529 }
530 }
531
532 fn on_completed(&self) {
533 let Some(cache) = self.cache.upgrade() else { return };
534 cache.changes.on_completed();
535 cache.changes_preview.on_completed();
536 if let Some(subject) = cache.edit_in_progress_subject() {
537 subject.on_completed();
538 }
539 let count = cache.count_changed.borrow().clone();
540 if let Some(subject) = count {
541 subject.on_completed();
542 }
543 let suspended = cache.suspension.borrow_mut().as_mut().map(|tracker| {
544 tracker.disposed = true;
545 tracker.are_notifications_suspended.clone()
546 });
547 if let Some(subject) = suspended {
548 subject.on_completed();
549 }
550 }
551}
552
553impl<T: Clone + 'static, K: Eq + Hash + Clone + 'static> IConnectableCache<T, K> for ObservableCache<T, K> {
554 fn count_changed(&self) -> Rc<dyn IObservable<usize>> {
555 let this = Rc::downgrade(&self.inner);
556 Observable::create(move |observer: Rc<dyn IObserver<usize>>| {
557 let Some(inner) = this.upgrade() else { return Disposable::empty() };
558 let subject = inner.count_changed.borrow_mut().get_or_insert_with(Subject::new).clone();
559 let mut count_changed = subject.as_observable();
560 if inner.notifications.has_pending() {
561 let snapshot_version = inner.current_version.get();
562 let weak = this.clone();
563 count_changed = count_changed.skip_while(move |_| weak.upgrade().is_some_and(|inner| inner.current_delivery_version.get() <= snapshot_version));
564 }
565 count_changed.start_with([inner.reader_writer.count()]).distinct_until_changed().subscribe(observer)
566 })
567 }
568
569 fn connect_with(&self, predicate: Option<Rc<dyn Fn(&T) -> bool>>, suppress_empty_change_sets: bool) -> Rc<dyn IObservable<ChangeSet<T, K>>> {
570 let this = Rc::downgrade(&self.inner);
571 let factory: Rc<dyn Fn() -> Rc<dyn IObservable<ChangeSet<T, K>>>> = Rc::new(move || match this.upgrade() {
572 Some(inner) => inner.create_connect_observable(predicate.clone(), suppress_empty_change_sets),
573 None => Observable::empty(),
574 });
575 self.inner.create_deferred_notification_observable(factory)
576 }
577
578 fn preview_with(&self, predicate: Option<Rc<dyn Fn(&T) -> bool>>) -> Rc<dyn IObservable<ChangeSet<T, K>>> {
579 let preview = self.inner.changes_preview.as_observable();
580 match predicate {
581 None => preview,
582 Some(predicate) => preview.filter(move |item: &T| predicate(item)),
583 }
584 }
585
586 fn watch(&self, key: K) -> Rc<dyn IObservable<Change<T, K>>> {
587 let this = Rc::downgrade(&self.inner);
588 let factory: Rc<dyn Fn() -> Rc<dyn IObservable<Change<T, K>>>> = Rc::new(move || match this.upgrade() {
589 Some(inner) => inner.create_watch_observable(key.clone()),
590 None => Observable::empty(),
591 });
592 self.inner.create_deferred_notification_observable(factory)
593 }
594}
595
596impl<T: Clone + 'static, K: Eq + Hash + Clone + 'static> IObservableCache<T, K> for ObservableCache<T, K> {
597 fn count(&self) -> usize {
598 self.inner.reader_writer.count()
599 }
600
601 fn items(&self) -> Vec<T> {
602 self.inner.reader_writer.items()
603 }
604
605 fn keys(&self) -> Vec<K> {
606 self.inner.reader_writer.keys()
607 }
608
609 fn key_values(&self) -> Dictionary<K, T> {
610 self.inner.reader_writer.key_values()
611 }
612
613 fn lookup(&self, key: &K) -> Option<T> {
614 self.inner.reader_writer.lookup(key)
615 }
616}
617
618impl<T: Clone + 'static, K: Eq + Hash + Clone + 'static> IDisposable for ObservableCache<T, K> {
619 fn dispose(&self) {
621 if self.inner.disposed.replace(true) {
622 return;
623 }
624 let loader = self.inner.loader.borrow_mut().take();
625 if let Some(loader) = loader {
626 loader.dispose();
627 }
628 self.inner.notify_completed();
629 }
630}