1use crate::cache::{ChangeSet, IConnectableCache, SourceCache};
25use crate::list::{IObservableList, SourceList};
26use ferroui_reactive::disposables::Disposable;
27use ferroui_reactive::{Observable, ObservableExt};
28use ferroui_system::threading::CancellationToken;
29use ferroui_system::{IDisposable, IObservable, IObserver, ObservableError};
30use std::future::Future;
31use std::hash::Hash;
32use std::rc::Rc;
33
34pub struct ObservableChangeSet;
36
37fn release(resource: Option<Rc<dyn IDisposable>>) {
40 if let Some(resource) = resource {
41 resource.dispose();
42 }
43}
44
45impl ObservableChangeSet {
46 pub fn create_cache<T: Clone + 'static, K: Eq + Hash + Clone + 'static>(
52 subscribe: impl Fn(SourceCache<T, K>) -> Rc<dyn IDisposable> + 'static,
53 key_selector: impl Fn(&T) -> K + 'static,
54 ) -> Rc<dyn IObservable<ChangeSet<T, K>>> {
55 Self::try_create_cache(move |cache| Ok(subscribe(cache)), key_selector)
56 }
57
58 pub fn try_create_cache<T: Clone + 'static, K: Eq + Hash + Clone + 'static>(
61 subscribe: impl Fn(SourceCache<T, K>) -> Result<Rc<dyn IDisposable>, ObservableError> + 'static,
62 key_selector: impl Fn(&T) -> K + 'static,
63 ) -> Rc<dyn IObservable<ChangeSet<T, K>>> {
64 let key_selector: Rc<dyn Fn(&T) -> K> = Rc::new(key_selector);
65 Observable::create(move |observer: Rc<dyn IObserver<ChangeSet<T, K>>>| {
66 let key_selector = key_selector.clone();
67 let cache = SourceCache::new(move |item: &T| key_selector(item));
68
69 let disposable = match subscribe(cache.clone()) {
70 Ok(disposable) => Some(disposable),
71 Err(error) => {
72 observer.on_error(error);
73 None
74 }
75 };
76
77 let completion = observer.clone();
78 let connection = cache.connect().subscribe_safe(observer);
79 Disposable::create(move || {
80 release(disposable);
81 completion.on_completed();
82 connection.dispose();
83 cache.dispose();
84 })
85 })
86 }
87
88 pub fn create_cache_with_action<T: Clone + 'static, K: Eq + Hash + Clone + 'static, A: FnOnce() + 'static>(
92 subscribe: impl Fn(SourceCache<T, K>) -> A + 'static,
93 key_selector: impl Fn(&T) -> K + 'static,
94 ) -> Rc<dyn IObservable<ChangeSet<T, K>>> {
95 Self::try_create_cache(move |cache| Ok(Disposable::create(subscribe(cache))), key_selector)
96 }
97
98 pub fn try_create_cache_with_action<T: Clone + 'static, K: Eq + Hash + Clone + 'static, A: FnOnce() + 'static>(
101 subscribe: impl Fn(SourceCache<T, K>) -> Result<A, ObservableError> + 'static,
102 key_selector: impl Fn(&T) -> K + 'static,
103 ) -> Rc<dyn IObservable<ChangeSet<T, K>>> {
104 Self::try_create_cache(move |cache| subscribe(cache).map(|action| Disposable::create(action)), key_selector)
105 }
106
107 pub fn create_cache_async_with_disposable_with_token<
112 T: Clone + 'static,
113 K: Eq + Hash + Clone + 'static,
114 Fut: Future<Output = Result<Rc<dyn IDisposable>, ObservableError>> + 'static,
115 >(
116 subscribe: impl Fn(SourceCache<T, K>, CancellationToken) -> Fut + 'static,
117 key_selector: impl Fn(&T) -> K + 'static,
118 ) -> Rc<dyn IObservable<ChangeSet<T, K>>> {
119 let key_selector: Rc<dyn Fn(&T) -> K> = Rc::new(key_selector);
120 Observable::create_async_with_disposable_with_token(move |observer: Rc<dyn IObserver<ChangeSet<T, K>>>, token: CancellationToken| {
121 let key_selector = key_selector.clone();
122 let cache = SourceCache::new(move |item: &T| key_selector(item));
123 let responder = cache.connect().subscribe_safe(observer.clone());
124 let future = subscribe(cache.clone(), token);
125 async move {
126 let disposable = match future.await {
127 Ok(disposable) => Some(disposable),
128 Err(error) => {
129 observer.on_error(error);
130 None
131 }
132 };
133
134 Ok::<Rc<dyn IDisposable>, ObservableError>(Disposable::create(move || {
135 responder.dispose();
136 cache.dispose();
137 release(disposable);
138 observer.on_completed();
139 }))
140 }
141 })
142 }
143
144 pub fn create_cache_async_with_disposable<
148 T: Clone + 'static,
149 K: Eq + Hash + Clone + 'static,
150 Fut: Future<Output = Result<Rc<dyn IDisposable>, ObservableError>> + 'static,
151 >(
152 subscribe: impl Fn(SourceCache<T, K>) -> Fut + 'static,
153 key_selector: impl Fn(&T) -> K + 'static,
154 ) -> Rc<dyn IObservable<ChangeSet<T, K>>> {
155 Self::create_cache_async_with_disposable_with_token(move |cache, _token| subscribe(cache), key_selector)
156 }
157
158 pub fn create_cache_async_with_action_with_token<
163 T: Clone + 'static,
164 K: Eq + Hash + Clone + 'static,
165 A: FnOnce() + 'static,
166 Fut: Future<Output = Result<A, ObservableError>> + 'static,
167 >(
168 subscribe: impl Fn(SourceCache<T, K>, CancellationToken) -> Fut + 'static,
169 key_selector: impl Fn(&T) -> K + 'static,
170 ) -> Rc<dyn IObservable<ChangeSet<T, K>>> {
171 let key_selector: Rc<dyn Fn(&T) -> K> = Rc::new(key_selector);
172 Observable::create_async_with_disposable_with_token(move |observer: Rc<dyn IObserver<ChangeSet<T, K>>>, token: CancellationToken| {
173 let key_selector = key_selector.clone();
174 let cache = SourceCache::new(move |item: &T| key_selector(item));
175 let responder = cache.connect().subscribe_safe(observer.clone());
176 let future = subscribe(cache.clone(), token);
177 async move {
178 let dispose_action = match future.await {
179 Ok(action) => Some(action),
180 Err(error) => {
181 observer.on_error(error);
182 None
183 }
184 };
185
186 Ok::<Rc<dyn IDisposable>, ObservableError>(Disposable::create(move || {
187 responder.dispose();
188 cache.dispose();
189 observer.on_completed();
190 if let Some(dispose_action) = dispose_action {
191 dispose_action();
192 }
193 }))
194 }
195 })
196 }
197
198 pub fn create_cache_async_with_action<
202 T: Clone + 'static,
203 K: Eq + Hash + Clone + 'static,
204 A: FnOnce() + 'static,
205 Fut: Future<Output = Result<A, ObservableError>> + 'static,
206 >(
207 subscribe: impl Fn(SourceCache<T, K>) -> Fut + 'static,
208 key_selector: impl Fn(&T) -> K + 'static,
209 ) -> Rc<dyn IObservable<ChangeSet<T, K>>> {
210 Self::create_cache_async_with_action_with_token(move |cache, _token| subscribe(cache), key_selector)
211 }
212
213 pub fn create_cache_async_with_token<T: Clone + 'static, K: Eq + Hash + Clone + 'static, Fut: Future<Output = Result<(), ObservableError>> + 'static>(
217 subscribe: impl Fn(SourceCache<T, K>, CancellationToken) -> Fut + 'static,
218 key_selector: impl Fn(&T) -> K + 'static,
219 ) -> Rc<dyn IObservable<ChangeSet<T, K>>> {
220 let key_selector: Rc<dyn Fn(&T) -> K> = Rc::new(key_selector);
221 Observable::create_async_with_disposable_with_token(move |observer: Rc<dyn IObserver<ChangeSet<T, K>>>, token: CancellationToken| {
222 let key_selector = key_selector.clone();
223 let cache = SourceCache::new(move |item: &T| key_selector(item));
224 let responder = cache.connect().subscribe_safe(observer.clone());
225 let future = subscribe(cache.clone(), token);
226 async move {
227 if let Err(error) = future.await {
228 observer.on_error(error);
229 }
230
231 Ok::<Rc<dyn IDisposable>, ObservableError>(Disposable::create(move || {
232 responder.dispose();
233 cache.dispose();
234 observer.on_completed();
235 }))
236 }
237 })
238 }
239
240 pub fn create_cache_async<T: Clone + 'static, K: Eq + Hash + Clone + 'static, Fut: Future<Output = Result<(), ObservableError>> + 'static>(
243 subscribe: impl Fn(SourceCache<T, K>) -> Fut + 'static,
244 key_selector: impl Fn(&T) -> K + 'static,
245 ) -> Rc<dyn IObservable<ChangeSet<T, K>>> {
246 Self::create_cache_async_with_token(move |cache, _token| subscribe(cache), key_selector)
247 }
248
249 pub fn create_list<T: Clone + PartialEq + 'static>(
255 subscribe: impl Fn(SourceList<T>) -> Rc<dyn IDisposable> + 'static,
256 ) -> Rc<dyn IObservable<crate::list::ChangeSet<T>>> {
257 Self::try_create_list(move |list| Ok(subscribe(list)))
258 }
259
260 pub fn try_create_list<T: Clone + PartialEq + 'static>(
263 subscribe: impl Fn(SourceList<T>) -> Result<Rc<dyn IDisposable>, ObservableError> + 'static,
264 ) -> Rc<dyn IObservable<crate::list::ChangeSet<T>>> {
265 Observable::create(move |observer: Rc<dyn IObserver<crate::list::ChangeSet<T>>>| {
266 let list = SourceList::new();
267
268 let dispose_action = match subscribe(list.clone()) {
269 Ok(disposable) => Some(disposable),
270 Err(error) => {
271 observer.on_error(error);
272 None
273 }
274 };
275
276 let completion = observer.clone();
277 let connection = list.connect().subscribe_safe(observer);
278 Disposable::create(move || {
279 connection.dispose();
280 list.dispose();
281 completion.on_completed();
282 release(dispose_action);
283 })
284 })
285 }
286
287 pub fn create_list_with_action<T: Clone + PartialEq + 'static, A: FnOnce() + 'static>(
290 subscribe: impl Fn(SourceList<T>) -> A + 'static,
291 ) -> Rc<dyn IObservable<crate::list::ChangeSet<T>>> {
292 Self::try_create_list(move |list| Ok(Disposable::create(subscribe(list))))
293 }
294
295 pub fn try_create_list_with_action<T: Clone + PartialEq + 'static, A: FnOnce() + 'static>(
298 subscribe: impl Fn(SourceList<T>) -> Result<A, ObservableError> + 'static,
299 ) -> Rc<dyn IObservable<crate::list::ChangeSet<T>>> {
300 Self::try_create_list(move |list| subscribe(list).map(|action| Disposable::create(action)))
301 }
302
303 pub fn create_list_async_with_disposable_with_token<
306 T: Clone + PartialEq + 'static,
307 Fut: Future<Output = Result<Rc<dyn IDisposable>, ObservableError>> + 'static,
308 >(
309 subscribe: impl Fn(SourceList<T>, CancellationToken) -> Fut + 'static,
310 ) -> Rc<dyn IObservable<crate::list::ChangeSet<T>>> {
311 Observable::create_async_with_disposable_with_token(move |observer: Rc<dyn IObserver<crate::list::ChangeSet<T>>>, token: CancellationToken| {
312 let list = SourceList::new();
313 let responder = list.connect().subscribe_safe(observer.clone());
314 let future = subscribe(list.clone(), token);
315 async move {
316 let dispose_action = match future.await {
317 Ok(disposable) => Some(disposable),
318 Err(error) => {
319 observer.on_error(error);
320 None
321 }
322 };
323
324 Ok::<Rc<dyn IDisposable>, ObservableError>(Disposable::create(move || {
325 responder.dispose();
326 list.dispose();
327 observer.on_completed();
328 release(dispose_action);
329 }))
330 }
331 })
332 }
333
334 pub fn create_list_async_with_disposable<T: Clone + PartialEq + 'static, Fut: Future<Output = Result<Rc<dyn IDisposable>, ObservableError>> + 'static>(
337 subscribe: impl Fn(SourceList<T>) -> Fut + 'static,
338 ) -> Rc<dyn IObservable<crate::list::ChangeSet<T>>> {
339 Self::create_list_async_with_disposable_with_token(move |list, _token| subscribe(list))
340 }
341
342 pub fn create_list_async_with_action_with_token<
345 T: Clone + PartialEq + 'static,
346 A: FnOnce() + 'static,
347 Fut: Future<Output = Result<A, ObservableError>> + 'static,
348 >(
349 subscribe: impl Fn(SourceList<T>, CancellationToken) -> Fut + 'static,
350 ) -> Rc<dyn IObservable<crate::list::ChangeSet<T>>> {
351 Observable::create_async_with_disposable_with_token(move |observer: Rc<dyn IObserver<crate::list::ChangeSet<T>>>, token: CancellationToken| {
352 let list = SourceList::new();
353 let responder = list.connect().subscribe_safe(observer.clone());
354 let future = subscribe(list.clone(), token);
355 async move {
356 let dispose_action = match future.await {
357 Ok(action) => Some(action),
358 Err(error) => {
359 observer.on_error(error);
360 None
361 }
362 };
363
364 Ok::<Rc<dyn IDisposable>, ObservableError>(Disposable::create(move || {
365 responder.dispose();
366 list.dispose();
367 observer.on_completed();
368 if let Some(dispose_action) = dispose_action {
369 dispose_action();
370 }
371 }))
372 }
373 })
374 }
375
376 pub fn create_list_async_with_action<T: Clone + PartialEq + 'static, A: FnOnce() + 'static, Fut: Future<Output = Result<A, ObservableError>> + 'static>(
379 subscribe: impl Fn(SourceList<T>) -> Fut + 'static,
380 ) -> Rc<dyn IObservable<crate::list::ChangeSet<T>>> {
381 Self::create_list_async_with_action_with_token(move |list, _token| subscribe(list))
382 }
383
384 pub fn create_list_async<T: Clone + PartialEq + 'static, Fut: Future<Output = Result<(), ObservableError>> + 'static>(
388 subscribe: impl Fn(SourceList<T>) -> Fut + 'static,
389 ) -> Rc<dyn IObservable<crate::list::ChangeSet<T>>> {
390 Observable::create_async_with_disposable(move |observer: Rc<dyn IObserver<crate::list::ChangeSet<T>>>| {
391 let list = SourceList::new();
392 let responder = list.connect().subscribe_safe(observer.clone());
393 let future = subscribe(list.clone());
394 async move {
395 if let Err(error) = future.await {
396 observer.on_error(error);
397 }
398
399 Ok::<Rc<dyn IDisposable>, ObservableError>(Disposable::create(move || {
400 responder.dispose();
401 list.dispose();
402 observer.on_completed();
403 }))
404 }
405 })
406 }
407
408 pub fn create_list_async_with_token<T: Clone + PartialEq + 'static, Fut: Future<Output = Result<(), ObservableError>> + 'static>(
413 subscribe: impl Fn(SourceList<T>, CancellationToken) -> Fut + 'static,
414 ) -> Rc<dyn IObservable<crate::list::ChangeSet<T>>> {
415 Observable::create_async_with_disposable_with_token(move |observer: Rc<dyn IObserver<crate::list::ChangeSet<T>>>, token: CancellationToken| {
416 let list = SourceList::new();
417 let future = subscribe(list.clone(), token);
418 async move {
419 if let Err(error) = future.await {
420 observer.on_error(error);
421 }
422
423 let connection = list.connect().subscribe_safe(observer.clone());
424 Ok::<Rc<dyn IDisposable>, ObservableError>(Disposable::create(move || {
425 connection.dispose();
426 list.dispose();
427 observer.on_completed();
428 }))
429 }
430 })
431 }
432}