use crate::cache::{ChangeSet, IConnectableCache, SourceCache};
use crate::list::{IObservableList, SourceList};
use ferroui_reactive::disposables::Disposable;
use ferroui_reactive::{Observable, ObservableExt};
use ferroui_system::threading::CancellationToken;
use ferroui_system::{IDisposable, IObservable, IObserver, ObservableError};
use std::future::Future;
use std::hash::Hash;
use std::rc::Rc;
pub struct ObservableChangeSet;
fn release(resource: Option<Rc<dyn IDisposable>>) {
if let Some(resource) = resource {
resource.dispose();
}
}
impl ObservableChangeSet {
pub fn create_cache<T: Clone + 'static, K: Eq + Hash + Clone + 'static>(
subscribe: impl Fn(SourceCache<T, K>) -> Rc<dyn IDisposable> + 'static,
key_selector: impl Fn(&T) -> K + 'static,
) -> Rc<dyn IObservable<ChangeSet<T, K>>> {
Self::try_create_cache(move |cache| Ok(subscribe(cache)), key_selector)
}
pub fn try_create_cache<T: Clone + 'static, K: Eq + Hash + Clone + 'static>(
subscribe: impl Fn(SourceCache<T, K>) -> Result<Rc<dyn IDisposable>, ObservableError> + 'static,
key_selector: impl Fn(&T) -> K + 'static,
) -> Rc<dyn IObservable<ChangeSet<T, K>>> {
let key_selector: Rc<dyn Fn(&T) -> K> = Rc::new(key_selector);
Observable::create(move |observer: Rc<dyn IObserver<ChangeSet<T, K>>>| {
let key_selector = key_selector.clone();
let cache = SourceCache::new(move |item: &T| key_selector(item));
let disposable = match subscribe(cache.clone()) {
Ok(disposable) => Some(disposable),
Err(error) => {
observer.on_error(error);
None
}
};
let completion = observer.clone();
let connection = cache.connect().subscribe_safe(observer);
Disposable::create(move || {
release(disposable);
completion.on_completed();
connection.dispose();
cache.dispose();
})
})
}
pub fn create_cache_with_action<T: Clone + 'static, K: Eq + Hash + Clone + 'static, A: FnOnce() + 'static>(
subscribe: impl Fn(SourceCache<T, K>) -> A + 'static,
key_selector: impl Fn(&T) -> K + 'static,
) -> Rc<dyn IObservable<ChangeSet<T, K>>> {
Self::try_create_cache(move |cache| Ok(Disposable::create(subscribe(cache))), key_selector)
}
pub fn try_create_cache_with_action<T: Clone + 'static, K: Eq + Hash + Clone + 'static, A: FnOnce() + 'static>(
subscribe: impl Fn(SourceCache<T, K>) -> Result<A, ObservableError> + 'static,
key_selector: impl Fn(&T) -> K + 'static,
) -> Rc<dyn IObservable<ChangeSet<T, K>>> {
Self::try_create_cache(move |cache| subscribe(cache).map(|action| Disposable::create(action)), key_selector)
}
pub fn create_cache_async_with_disposable_with_token<
T: Clone + 'static,
K: Eq + Hash + Clone + 'static,
Fut: Future<Output = Result<Rc<dyn IDisposable>, ObservableError>> + 'static,
>(
subscribe: impl Fn(SourceCache<T, K>, CancellationToken) -> Fut + 'static,
key_selector: impl Fn(&T) -> K + 'static,
) -> Rc<dyn IObservable<ChangeSet<T, K>>> {
let key_selector: Rc<dyn Fn(&T) -> K> = Rc::new(key_selector);
Observable::create_async_with_disposable_with_token(move |observer: Rc<dyn IObserver<ChangeSet<T, K>>>, token: CancellationToken| {
let key_selector = key_selector.clone();
let cache = SourceCache::new(move |item: &T| key_selector(item));
let responder = cache.connect().subscribe_safe(observer.clone());
let future = subscribe(cache.clone(), token);
async move {
let disposable = match future.await {
Ok(disposable) => Some(disposable),
Err(error) => {
observer.on_error(error);
None
}
};
Ok::<Rc<dyn IDisposable>, ObservableError>(Disposable::create(move || {
responder.dispose();
cache.dispose();
release(disposable);
observer.on_completed();
}))
}
})
}
pub fn create_cache_async_with_disposable<
T: Clone + 'static,
K: Eq + Hash + Clone + 'static,
Fut: Future<Output = Result<Rc<dyn IDisposable>, ObservableError>> + 'static,
>(
subscribe: impl Fn(SourceCache<T, K>) -> Fut + 'static,
key_selector: impl Fn(&T) -> K + 'static,
) -> Rc<dyn IObservable<ChangeSet<T, K>>> {
Self::create_cache_async_with_disposable_with_token(move |cache, _token| subscribe(cache), key_selector)
}
pub fn create_cache_async_with_action_with_token<
T: Clone + 'static,
K: Eq + Hash + Clone + 'static,
A: FnOnce() + 'static,
Fut: Future<Output = Result<A, ObservableError>> + 'static,
>(
subscribe: impl Fn(SourceCache<T, K>, CancellationToken) -> Fut + 'static,
key_selector: impl Fn(&T) -> K + 'static,
) -> Rc<dyn IObservable<ChangeSet<T, K>>> {
let key_selector: Rc<dyn Fn(&T) -> K> = Rc::new(key_selector);
Observable::create_async_with_disposable_with_token(move |observer: Rc<dyn IObserver<ChangeSet<T, K>>>, token: CancellationToken| {
let key_selector = key_selector.clone();
let cache = SourceCache::new(move |item: &T| key_selector(item));
let responder = cache.connect().subscribe_safe(observer.clone());
let future = subscribe(cache.clone(), token);
async move {
let dispose_action = match future.await {
Ok(action) => Some(action),
Err(error) => {
observer.on_error(error);
None
}
};
Ok::<Rc<dyn IDisposable>, ObservableError>(Disposable::create(move || {
responder.dispose();
cache.dispose();
observer.on_completed();
if let Some(dispose_action) = dispose_action {
dispose_action();
}
}))
}
})
}
pub fn create_cache_async_with_action<
T: Clone + 'static,
K: Eq + Hash + Clone + 'static,
A: FnOnce() + 'static,
Fut: Future<Output = Result<A, ObservableError>> + 'static,
>(
subscribe: impl Fn(SourceCache<T, K>) -> Fut + 'static,
key_selector: impl Fn(&T) -> K + 'static,
) -> Rc<dyn IObservable<ChangeSet<T, K>>> {
Self::create_cache_async_with_action_with_token(move |cache, _token| subscribe(cache), key_selector)
}
pub fn create_cache_async_with_token<T: Clone + 'static, K: Eq + Hash + Clone + 'static, Fut: Future<Output = Result<(), ObservableError>> + 'static>(
subscribe: impl Fn(SourceCache<T, K>, CancellationToken) -> Fut + 'static,
key_selector: impl Fn(&T) -> K + 'static,
) -> Rc<dyn IObservable<ChangeSet<T, K>>> {
let key_selector: Rc<dyn Fn(&T) -> K> = Rc::new(key_selector);
Observable::create_async_with_disposable_with_token(move |observer: Rc<dyn IObserver<ChangeSet<T, K>>>, token: CancellationToken| {
let key_selector = key_selector.clone();
let cache = SourceCache::new(move |item: &T| key_selector(item));
let responder = cache.connect().subscribe_safe(observer.clone());
let future = subscribe(cache.clone(), token);
async move {
if let Err(error) = future.await {
observer.on_error(error);
}
Ok::<Rc<dyn IDisposable>, ObservableError>(Disposable::create(move || {
responder.dispose();
cache.dispose();
observer.on_completed();
}))
}
})
}
pub fn create_cache_async<T: Clone + 'static, K: Eq + Hash + Clone + 'static, Fut: Future<Output = Result<(), ObservableError>> + 'static>(
subscribe: impl Fn(SourceCache<T, K>) -> Fut + 'static,
key_selector: impl Fn(&T) -> K + 'static,
) -> Rc<dyn IObservable<ChangeSet<T, K>>> {
Self::create_cache_async_with_token(move |cache, _token| subscribe(cache), key_selector)
}
pub fn create_list<T: Clone + PartialEq + 'static>(
subscribe: impl Fn(SourceList<T>) -> Rc<dyn IDisposable> + 'static,
) -> Rc<dyn IObservable<crate::list::ChangeSet<T>>> {
Self::try_create_list(move |list| Ok(subscribe(list)))
}
pub fn try_create_list<T: Clone + PartialEq + 'static>(
subscribe: impl Fn(SourceList<T>) -> Result<Rc<dyn IDisposable>, ObservableError> + 'static,
) -> Rc<dyn IObservable<crate::list::ChangeSet<T>>> {
Observable::create(move |observer: Rc<dyn IObserver<crate::list::ChangeSet<T>>>| {
let list = SourceList::new();
let dispose_action = match subscribe(list.clone()) {
Ok(disposable) => Some(disposable),
Err(error) => {
observer.on_error(error);
None
}
};
let completion = observer.clone();
let connection = list.connect().subscribe_safe(observer);
Disposable::create(move || {
connection.dispose();
list.dispose();
completion.on_completed();
release(dispose_action);
})
})
}
pub fn create_list_with_action<T: Clone + PartialEq + 'static, A: FnOnce() + 'static>(
subscribe: impl Fn(SourceList<T>) -> A + 'static,
) -> Rc<dyn IObservable<crate::list::ChangeSet<T>>> {
Self::try_create_list(move |list| Ok(Disposable::create(subscribe(list))))
}
pub fn try_create_list_with_action<T: Clone + PartialEq + 'static, A: FnOnce() + 'static>(
subscribe: impl Fn(SourceList<T>) -> Result<A, ObservableError> + 'static,
) -> Rc<dyn IObservable<crate::list::ChangeSet<T>>> {
Self::try_create_list(move |list| subscribe(list).map(|action| Disposable::create(action)))
}
pub fn create_list_async_with_disposable_with_token<
T: Clone + PartialEq + 'static,
Fut: Future<Output = Result<Rc<dyn IDisposable>, ObservableError>> + 'static,
>(
subscribe: impl Fn(SourceList<T>, CancellationToken) -> Fut + 'static,
) -> Rc<dyn IObservable<crate::list::ChangeSet<T>>> {
Observable::create_async_with_disposable_with_token(move |observer: Rc<dyn IObserver<crate::list::ChangeSet<T>>>, token: CancellationToken| {
let list = SourceList::new();
let responder = list.connect().subscribe_safe(observer.clone());
let future = subscribe(list.clone(), token);
async move {
let dispose_action = match future.await {
Ok(disposable) => Some(disposable),
Err(error) => {
observer.on_error(error);
None
}
};
Ok::<Rc<dyn IDisposable>, ObservableError>(Disposable::create(move || {
responder.dispose();
list.dispose();
observer.on_completed();
release(dispose_action);
}))
}
})
}
pub fn create_list_async_with_disposable<T: Clone + PartialEq + 'static, Fut: Future<Output = Result<Rc<dyn IDisposable>, ObservableError>> + 'static>(
subscribe: impl Fn(SourceList<T>) -> Fut + 'static,
) -> Rc<dyn IObservable<crate::list::ChangeSet<T>>> {
Self::create_list_async_with_disposable_with_token(move |list, _token| subscribe(list))
}
pub fn create_list_async_with_action_with_token<
T: Clone + PartialEq + 'static,
A: FnOnce() + 'static,
Fut: Future<Output = Result<A, ObservableError>> + 'static,
>(
subscribe: impl Fn(SourceList<T>, CancellationToken) -> Fut + 'static,
) -> Rc<dyn IObservable<crate::list::ChangeSet<T>>> {
Observable::create_async_with_disposable_with_token(move |observer: Rc<dyn IObserver<crate::list::ChangeSet<T>>>, token: CancellationToken| {
let list = SourceList::new();
let responder = list.connect().subscribe_safe(observer.clone());
let future = subscribe(list.clone(), token);
async move {
let dispose_action = match future.await {
Ok(action) => Some(action),
Err(error) => {
observer.on_error(error);
None
}
};
Ok::<Rc<dyn IDisposable>, ObservableError>(Disposable::create(move || {
responder.dispose();
list.dispose();
observer.on_completed();
if let Some(dispose_action) = dispose_action {
dispose_action();
}
}))
}
})
}
pub fn create_list_async_with_action<T: Clone + PartialEq + 'static, A: FnOnce() + 'static, Fut: Future<Output = Result<A, ObservableError>> + 'static>(
subscribe: impl Fn(SourceList<T>) -> Fut + 'static,
) -> Rc<dyn IObservable<crate::list::ChangeSet<T>>> {
Self::create_list_async_with_action_with_token(move |list, _token| subscribe(list))
}
pub fn create_list_async<T: Clone + PartialEq + 'static, Fut: Future<Output = Result<(), ObservableError>> + 'static>(
subscribe: impl Fn(SourceList<T>) -> Fut + 'static,
) -> Rc<dyn IObservable<crate::list::ChangeSet<T>>> {
Observable::create_async_with_disposable(move |observer: Rc<dyn IObserver<crate::list::ChangeSet<T>>>| {
let list = SourceList::new();
let responder = list.connect().subscribe_safe(observer.clone());
let future = subscribe(list.clone());
async move {
if let Err(error) = future.await {
observer.on_error(error);
}
Ok::<Rc<dyn IDisposable>, ObservableError>(Disposable::create(move || {
responder.dispose();
list.dispose();
observer.on_completed();
}))
}
})
}
pub fn create_list_async_with_token<T: Clone + PartialEq + 'static, Fut: Future<Output = Result<(), ObservableError>> + 'static>(
subscribe: impl Fn(SourceList<T>, CancellationToken) -> Fut + 'static,
) -> Rc<dyn IObservable<crate::list::ChangeSet<T>>> {
Observable::create_async_with_disposable_with_token(move |observer: Rc<dyn IObserver<crate::list::ChangeSet<T>>>, token: CancellationToken| {
let list = SourceList::new();
let future = subscribe(list.clone(), token);
async move {
if let Err(error) = future.await {
observer.on_error(error);
}
let connection = list.connect().subscribe_safe(observer.clone());
Ok::<Rc<dyn IDisposable>, ObservableError>(Disposable::create(move || {
connection.dispose();
list.dispose();
observer.on_completed();
}))
}
})
}
}