use super::{merge_many_cache_change_sets, ChangeSetSelector, Comparer, EqualityComparer, ExceptionCallback, TransformFuture};
use crate::binding::ObservableCollectionEx;
use crate::cache::{ChangeSet, IConnectableCache, IObservableCache};
use crate::kernel::Error;
use crate::EnumerableEx;
use ferroui_reactive::Observable;
use ferroui_system::{IObservable, ObservableError};
use std::future::Future;
use std::hash::Hash;
use std::rc::Rc;
pub(crate) type AsyncChangeSetTransformer<S, SK, D, DK> = Rc<dyn Fn(S, SK) -> TransformFuture<Rc<dyn IObservable<ChangeSet<D, DK>>>>>;
pub(crate) fn transform_many_async<S: Clone + 'static, SK: Eq + Hash + Clone + 'static, D: Clone + 'static, DK: Eq + Hash + Clone + 'static>(
source: Rc<dyn IObservable<ChangeSet<S, SK>>>,
transformer: AsyncChangeSetTransformer<S, SK, D, DK>,
equality_comparer: EqualityComparer<D>,
comparer: Option<Comparer<D>>,
error_handler: Option<ExceptionCallback<S, SK>>,
) -> Rc<dyn IObservable<ChangeSet<D, DK>>> {
let selector: ChangeSetSelector<S, SK, D, DK> = match error_handler {
None => Rc::new(move |item: &S, key: &SK| {
let (transformer, item, key) = (transformer.clone(), item.clone(), key.clone());
Observable::defer_async(move || transformer(item.clone(), key.clone()))
}),
Some(error_handler) => Rc::new(move |item: &S, key: &SK| {
let (transformer, error_handler, item, key) = (transformer.clone(), error_handler.clone(), item.clone(), key.clone());
Observable::defer_async(move || error_handling_transform(transformer.clone(), error_handler.clone(), item.clone(), key.clone()))
}),
};
merge_many_cache_change_sets(source, selector, equality_comparer, comparer)
}
async fn error_handling_transform<S: 'static, SK: 'static, D: 'static, DK: 'static>(
transformer: AsyncChangeSetTransformer<S, SK, D, DK>,
error_handler: ExceptionCallback<S, SK>,
item: S,
key: SK,
) -> Result<Rc<dyn IObservable<ChangeSet<D, DK>>>, ObservableError>
where
S: Clone,
SK: Clone,
{
match transformer(item.clone(), key.clone()).await {
Ok(children) => Ok(children),
Err(error) => {
error_handler(Error::new(Some(error), item, key));
Ok(Observable::empty())
}
}
}
pub(crate) fn enumerable_change_set_transformer<S: 'static, SK: 'static, D: Clone + 'static, DK: Eq + Hash + Clone + 'static, Fut: Future<Output = Result<Vec<D>, ObservableError>> + 'static>(
many_selector: impl Fn(S, SK) -> Fut + 'static,
key_selector: impl Fn(&D) -> DK + 'static,
) -> AsyncChangeSetTransformer<S, SK, D, DK> {
let key_selector: Rc<dyn Fn(&D) -> DK> = Rc::new(key_selector);
Rc::new(move |item: S, key: SK| -> TransformFuture<Rc<dyn IObservable<ChangeSet<D, DK>>>> {
let items = many_selector(item, key);
let key_selector = key_selector.clone();
Box::pin(async move {
let items = items.await?;
Ok::<_, ObservableError>(items.as_observable_change_set_cache(move |item: &D| key_selector(item)))
})
})
}
pub(crate) fn observable_collection_change_set_transformer<
S: 'static,
SK: 'static,
D: Clone + PartialEq + 'static,
DK: Eq + Hash + Clone + 'static,
C: ObservableCollectionEx<D> + 'static,
Fut: Future<Output = Result<C, ObservableError>> + 'static,
>(
many_selector: impl Fn(S, SK) -> Fut + 'static,
key_selector: impl Fn(&D) -> DK + 'static,
) -> AsyncChangeSetTransformer<S, SK, D, DK> {
let key_selector: Rc<dyn Fn(&D) -> DK> = Rc::new(key_selector);
Rc::new(move |item: S, key: SK| -> TransformFuture<Rc<dyn IObservable<ChangeSet<D, DK>>>> {
let collection = many_selector(item, key);
let key_selector = key_selector.clone();
Box::pin(async move {
let collection = collection.await?;
Ok::<_, ObservableError>(collection.to_observable_change_set_with_key(move |item: &D| key_selector(item)))
})
})
}
pub(crate) fn observable_cache_change_set_transformer<
S: 'static,
SK: 'static,
D: Clone + 'static,
DK: Eq + Hash + Clone + 'static,
C: IObservableCache<D, DK> + 'static,
Fut: Future<Output = Result<C, ObservableError>> + 'static,
>(
many_selector: impl Fn(S, SK) -> Fut + 'static,
) -> AsyncChangeSetTransformer<S, SK, D, DK> {
Rc::new(move |item: S, key: SK| -> TransformFuture<Rc<dyn IObservable<ChangeSet<D, DK>>>> {
let cache = many_selector(item, key);
Box::pin(async move {
let cache = cache.await?;
Ok::<_, ObservableError>(IConnectableCache::connect(&cache))
})
})
}