ferroui_dynamic_data/cache/
lock_free_observable_cache.rs1use super::internal::{filter_static, CacheUpdater};
5use super::{Change, ChangeAwareCache, ChangeReason, ChangeSet, ICache, ICacheUpdater, IChangeSet, IConnectableCache, IObservableCache, IQuery, ObservableCacheEx};
6use crate::kernel::return_with;
7use ferroui_reactive::disposables::Disposable;
8use ferroui_reactive::subjects::Subject;
9use ferroui_reactive::{Observable, ObservableExt};
10use ferroui_system::collections::generic::Dictionary;
11use ferroui_system::{IDisposable, IObservable, IObserver};
12use std::cell::RefCell;
13use std::hash::Hash;
14use std::rc::Rc;
15
16struct Inner<T, K> {
17 changes: Rc<Subject<ChangeSet<T, K>>>,
18 changes_preview: Rc<Subject<ChangeSet<T, K>>>,
19 count_changed: Rc<Subject<usize>>,
20 inner_cache: Rc<RefCell<ChangeAwareCache<T, K>>>,
21 clean_up: RefCell<Option<Rc<dyn IDisposable>>>,
22}
23
24pub struct LockFreeObservableCache<T, K> {
30 inner: Rc<Inner<T, K>>,
31}
32
33impl<T, K> Clone for LockFreeObservableCache<T, K> {
34 fn clone(&self) -> Self {
35 Self { inner: self.inner.clone() }
36 }
37}
38
39impl<T: Clone + 'static, K: Eq + Hash + Clone + 'static> Default for LockFreeObservableCache<T, K> {
40 fn default() -> Self {
41 Self::new()
42 }
43}
44
45impl<T: Clone + 'static, K: Eq + Hash + Clone + 'static> LockFreeObservableCache<T, K> {
46 fn create() -> Self {
47 Self {
48 inner: Rc::new(Inner {
49 changes: Subject::new(),
50 changes_preview: Subject::new(),
51 count_changed: Subject::new(),
52 inner_cache: Rc::new(RefCell::new(ChangeAwareCache::new())),
53 clean_up: RefCell::new(None),
54 }),
55 }
56 }
57
58 pub fn new() -> Self {
60 let cache = Self::create();
61 let (changes, count_changed) = (cache.inner.changes.clone(), cache.inner.count_changed.clone());
62 *cache.inner.clean_up.borrow_mut() = Some(Disposable::create(move || {
63 changes.on_completed();
64 count_changed.on_completed();
65 }));
66 cache
67 }
68
69 pub fn from_source<C: IChangeSet<T, K>>(source: Rc<dyn IObservable<C>>) -> Self {
71 let cache = Self::create();
72 let inner_cache = cache.inner.inner_cache.clone();
73 let loader = source
74 .change_sets()
75 .select(move |changes: ChangeSet<T, K>| {
76 let mut inner_cache = inner_cache.borrow_mut();
77 inner_cache.clone_changes(&changes);
78 inner_cache.capture_changes()
79 })
80 .subscribe_safe(cache.inner.changes.as_observer());
81 let (changes_preview, changes, count_changed) = (cache.inner.changes_preview.clone(), cache.inner.changes.clone(), cache.inner.count_changed.clone());
82 *cache.inner.clean_up.borrow_mut() = Some(Disposable::create(move || {
83 loader.dispose();
84 changes_preview.on_completed();
85 changes.on_completed();
86 count_changed.on_completed();
87 }));
88 cache
89 }
90
91 pub fn edit(&self, edit_action: impl FnOnce(&mut dyn ICacheUpdater<T, K>)) {
93 let changes = {
94 let cache: &RefCell<dyn ICache<T, K>> = &*self.inner.inner_cache;
95 let mut updater = CacheUpdater::new(cache, None);
96 edit_action(&mut updater);
97 self.inner.inner_cache.borrow_mut().capture_changes()
98 };
99 self.inner.changes.on_next(changes);
100 }
101
102 pub fn clear(&self) {
104 self.edit(|updater| updater.clear());
105 }
106
107 fn get_initial_updates(inner_cache: &RefCell<ChangeAwareCache<T, K>>, predicate: Option<&dyn Fn(&T) -> bool>) -> ChangeSet<T, K> {
108 inner_cache
109 .borrow()
110 .key_values()
111 .into_iter()
112 .filter(|(_, item)| predicate.is_none_or(|predicate| predicate(item)))
113 .map(|(key, item)| Change::new(ChangeReason::Add, key, item))
114 .collect()
115 }
116}
117
118impl<T: Clone + 'static, K: Eq + Hash + Clone + 'static> IConnectableCache<T, K> for LockFreeObservableCache<T, K> {
119 fn count_changed(&self) -> Rc<dyn IObservable<usize>> {
120 let count = self.inner.inner_cache.borrow().count();
121 self.inner.count_changed.as_observable().start_with([count]).distinct_until_changed()
122 }
123
124 fn connect_with(&self, predicate: Option<Rc<dyn Fn(&T) -> bool>>, suppress_empty_change_sets: bool) -> Rc<dyn IObservable<ChangeSet<T, K>>> {
125 let (inner_cache, changes) = (self.inner.inner_cache.clone(), self.inner.changes.clone());
126 Observable::defer(move || {
127 let (inner_cache, filter) = (inner_cache.clone(), predicate.clone());
128 let initial = return_with(move || Self::get_initial_updates(&inner_cache, filter.as_deref()));
129 let changes = initial.concat(changes.as_observable());
130 match &predicate {
131 Some(predicate) => filter_static(changes, predicate.clone(), suppress_empty_change_sets),
132 None if suppress_empty_change_sets => changes.not_empty(),
133 None => changes,
134 }
135 })
136 }
137
138 fn preview_with(&self, predicate: Option<Rc<dyn Fn(&T) -> bool>>) -> Rc<dyn IObservable<ChangeSet<T, K>>> {
139 let preview = self.inner.changes_preview.as_observable();
140 match predicate {
141 None => preview,
142 Some(predicate) => filter_static(preview, predicate, true),
143 }
144 }
145
146 fn watch(&self, key: K) -> Rc<dyn IObservable<Change<T, K>>> {
147 let (inner_cache, changes) = (self.inner.inner_cache.clone(), self.inner.changes.clone());
148 Observable::create(move |observer: Rc<dyn IObserver<Change<T, K>>>| {
149 let initial = inner_cache.borrow().lookup(&key);
150 if let Some(initial) = initial {
151 observer.on_next(Change::new(ChangeReason::Add, key.clone(), initial));
152 }
153 let key = key.clone();
154 changes.as_observable().subscribe_next(move |changes: ChangeSet<T, K>| {
155 for change in &changes {
156 if *change.key() == key {
157 observer.on_next(change.clone());
158 }
159 }
160 })
161 })
162 }
163}
164
165impl<T: Clone + 'static, K: Eq + Hash + Clone + 'static> IObservableCache<T, K> for LockFreeObservableCache<T, K> {
166 fn count(&self) -> usize {
167 self.inner.inner_cache.borrow().count()
168 }
169
170 fn items(&self) -> Vec<T> {
171 self.inner.inner_cache.borrow().items()
172 }
173
174 fn keys(&self) -> Vec<K> {
175 self.inner.inner_cache.borrow().keys()
176 }
177
178 fn key_values(&self) -> Dictionary<K, T> {
179 self.inner.inner_cache.borrow().dictionary().clone()
180 }
181
182 fn lookup(&self, key: &K) -> Option<T> {
183 self.inner.inner_cache.borrow().lookup(key)
184 }
185}
186
187impl<T: Clone + 'static, K: Eq + Hash + Clone + 'static> IDisposable for LockFreeObservableCache<T, K> {
188 fn dispose(&self) {
189 let clean_up = self.inner.clean_up.borrow_mut().take();
190 if let Some(clean_up) = clean_up {
191 clean_up.dispose();
192 }
193 }
194}