use super::{ChangeSet, IObservableCache, ObservableCache, ObservableCacheEx};
use crate::diagnostics::ChangeSummary;
use ferroui_reactive::disposables::Disposable;
use ferroui_reactive::ObservableExt;
use ferroui_system::{IDisposable, IObservable, ObservableError};
use std::cell::{Cell, RefCell};
use std::hash::Hash;
use std::rc::Rc;
struct State<T, K> {
messages: RefCell<Vec<ChangeSet<T, K>>>,
error: RefCell<Option<ObservableError>>,
is_completed: Cell<bool>,
summary: RefCell<ChangeSummary>,
}
pub struct ChangeSetAggregator<T, K> {
state: Rc<State<T, K>>,
data: ObservableCache<T, K>,
disposer: Rc<dyn IDisposable>,
}
impl<T: Clone + 'static, K: Eq + Hash + Clone + 'static> ChangeSetAggregator<T, K> {
pub fn new(source: Rc<dyn IObservable<ChangeSet<T, K>>>) -> Self {
let state = Rc::new(State { messages: RefCell::new(Vec::new()), error: RefCell::new(None), is_completed: Cell::new(false), summary: RefCell::new(ChangeSummary::empty()) });
let published = source.publish();
let published_source: Rc<dyn IObservable<ChangeSet<T, K>>> = published.clone();
let data = published_source.as_observable_cache();
let (on_next, on_error, on_completed) = (state.clone(), state.clone(), state.clone());
let results = published_source.subscribe_all(
move |changes| on_next.messages.borrow_mut().push(changes),
move |error| *on_error.error.borrow_mut() = Some(error),
move || on_completed.is_completed.set(true),
);
let summary_target = state.clone();
let summariser = published_source.collect_update_stats().subscribe_all(move |summary| *summary_target.summary.borrow_mut() = summary, |_| {}, || {});
let connected = published.connect();
let cache = data.clone();
let disposer = Disposable::create(move || {
cache.dispose();
connected.dispose();
summariser.dispose();
results.dispose();
});
Self { state, data, disposer }
}
pub fn data(&self) -> &ObservableCache<T, K> {
&self.data
}
pub fn messages(&self) -> Vec<ChangeSet<T, K>> {
self.state.messages.borrow().clone()
}
pub fn error(&self) -> Option<ObservableError> {
self.state.error.borrow().clone()
}
pub fn is_completed(&self) -> bool {
self.state.is_completed.get()
}
pub fn summary(&self) -> ChangeSummary {
self.state.summary.borrow().clone()
}
pub fn count(&self) -> usize {
self.data.count()
}
}
impl<T, K> IDisposable for ChangeSetAggregator<T, K> {
fn dispose(&self) {
self.disposer.dispose();
}
}
impl<T, K> Drop for ChangeSetAggregator<T, K> {
fn drop(&mut self) {
self.disposer.dispose();
}
}
struct TypedState<C> {
messages: RefCell<Vec<C>>,
error: RefCell<Option<ObservableError>>,
is_completed: Cell<bool>,
summary: RefCell<ChangeSummary>,
}
pub struct TypedChangeSetAggregator<T, K, C> {
state: Rc<TypedState<C>>,
data: ObservableCache<T, K>,
disposer: Rc<dyn IDisposable>,
}
pub type SortedChangeSetAggregator<T, K> = TypedChangeSetAggregator<T, K, super::SortedChangeSet<T, K>>;
pub type PagedChangeSetAggregator<T, K> = TypedChangeSetAggregator<T, K, super::PagedChangeSet<T, K>>;
pub type VirtualChangeSetAggregator<T, K> = TypedChangeSetAggregator<T, K, super::VirtualChangeSet<T, K>>;
pub type ContextChangeSetAggregator<T, K, X> = TypedChangeSetAggregator<T, K, super::ChangeSetWithContext<T, K, X>>;
impl<T: Clone + 'static, K: Eq + Hash + Clone + 'static, C: super::IChangeSet<T, K>> TypedChangeSetAggregator<T, K, C> {
pub fn new(source: Rc<dyn IObservable<C>>) -> Self {
let state = Rc::new(TypedState { messages: RefCell::new(Vec::new()), error: RefCell::new(None), is_completed: Cell::new(false), summary: RefCell::new(ChangeSummary::empty()) });
let published = source.publish();
let published_source: Rc<dyn IObservable<C>> = published.clone();
let data = ObservableCacheEx::<T, K>::as_observable_cache(&published_source);
let (on_next, on_error, on_completed) = (state.clone(), state.clone(), state.clone());
let results = published_source.subscribe_all(
move |changes| on_next.messages.borrow_mut().push(changes),
move |error| *on_error.error.borrow_mut() = Some(error),
move || on_completed.is_completed.set(true),
);
let summary_target = state.clone();
let summariser = ObservableCacheEx::<T, K>::collect_update_stats(&published_source).subscribe_all(move |summary| *summary_target.summary.borrow_mut() = summary, |_| {}, || {});
let connected = published.connect();
let cache = data.clone();
let disposer = Disposable::create(move || {
cache.dispose();
connected.dispose();
summariser.dispose();
results.dispose();
});
Self { state, data, disposer }
}
pub fn data(&self) -> &ObservableCache<T, K> {
&self.data
}
pub fn messages(&self) -> Vec<C> {
self.state.messages.borrow().clone()
}
pub fn error(&self) -> Option<ObservableError> {
self.state.error.borrow().clone()
}
pub fn is_completed(&self) -> bool {
self.state.is_completed.get()
}
pub fn summary(&self) -> ChangeSummary {
self.state.summary.borrow().clone()
}
pub fn count(&self) -> usize {
self.data.count()
}
}
impl<T, K, C> IDisposable for TypedChangeSetAggregator<T, K, C> {
fn dispose(&self) {
self.disposer.dispose();
}
}
impl<T, K, C> Drop for TypedChangeSetAggregator<T, K, C> {
fn drop(&mut self) {
self.disposer.dispose();
}
}
struct GroupAggregatorState<T, K, G> {
messages: std::cell::RefCell<Vec<super::GroupChangeSet<T, K, G>>>,
error: std::cell::RefCell<Option<ferroui_system::ObservableError>>,
is_completed: std::cell::Cell<bool>,
summary: std::cell::RefCell<crate::diagnostics::ChangeSummary>,
}
pub struct GroupChangeSetAggregator<T, K, G> {
state: std::rc::Rc<GroupAggregatorState<T, K, G>>,
data: super::ObservableCache<super::Group<T, K, G>, G>,
groups: super::ObservableCache<std::rc::Rc<ChangeSetAggregator<T, K>>, G>,
disposer: std::rc::Rc<dyn ferroui_system::IDisposable>,
}
impl<T: Clone + 'static, K: Eq + std::hash::Hash + Clone + 'static, G: Eq + std::hash::Hash + Clone + 'static> GroupChangeSetAggregator<T, K, G> {
pub fn new(source: std::rc::Rc<dyn ferroui_system::IObservable<super::GroupChangeSet<T, K, G>>>) -> Self {
let state = std::rc::Rc::new(GroupAggregatorState {
messages: std::cell::RefCell::new(Vec::new()),
error: std::cell::RefCell::new(None),
is_completed: std::cell::Cell::new(false),
summary: std::cell::RefCell::new(crate::diagnostics::ChangeSummary::empty()),
});
let published = ferroui_reactive::ObservableExt::publish(&source);
let published_source: std::rc::Rc<dyn ferroui_system::IObservable<super::GroupChangeSet<T, K, G>>> = published.clone();
let data = super::ObservableCache::from_source(published_source.clone());
let groups = super::ObservableCache::from_source(group_aggregators(published_source.clone()));
let (on_next, on_error, on_completed) = (state.clone(), state.clone(), state.clone());
let results = ferroui_reactive::ObservableExt::subscribe_all(
&published_source,
move |changes| on_next.messages.borrow_mut().push(changes),
move |error| *on_error.error.borrow_mut() = Some(error),
move || on_completed.is_completed.set(true),
);
let summary_target = state.clone();
let summariser = ferroui_reactive::ObservableExt::subscribe_all(
&super::ObservableCacheEx::collect_update_stats(&published_source),
move |summary| *summary_target.summary.borrow_mut() = summary,
|_| {},
|| {},
);
let connected = published.connect();
let (data_cache, groups_cache) = (data.clone(), groups.clone());
let disposer = ferroui_reactive::disposables::Disposable::create(move || {
ferroui_system::IDisposable::dispose(&data_cache);
ferroui_system::IDisposable::dispose(&groups_cache);
results.dispose();
summariser.dispose();
connected.dispose();
});
Self { state, data, groups, disposer }
}
pub fn data(&self) -> &super::ObservableCache<super::Group<T, K, G>, G> {
&self.data
}
pub fn groups(&self) -> &super::ObservableCache<std::rc::Rc<ChangeSetAggregator<T, K>>, G> {
&self.groups
}
pub fn messages(&self) -> Vec<super::GroupChangeSet<T, K, G>> {
self.state.messages.borrow().clone()
}
pub fn error(&self) -> Option<ferroui_system::ObservableError> {
self.state.error.borrow().clone()
}
pub fn is_completed(&self) -> bool {
self.state.is_completed.get()
}
pub fn summary(&self) -> crate::diagnostics::ChangeSummary {
self.state.summary.borrow().clone()
}
}
impl<T, K, G> ferroui_system::IDisposable for GroupChangeSetAggregator<T, K, G> {
fn dispose(&self) {
self.disposer.dispose();
}
}
impl<T, K, G> Drop for GroupChangeSetAggregator<T, K, G> {
fn drop(&mut self) {
self.disposer.dispose();
}
}
fn group_aggregators<T: Clone + 'static, K: Eq + std::hash::Hash + Clone + 'static, G: Eq + std::hash::Hash + Clone + 'static>(
source: std::rc::Rc<dyn ferroui_system::IObservable<super::GroupChangeSet<T, K, G>>>,
) -> std::rc::Rc<dyn ferroui_system::IObservable<super::ChangeSet<std::rc::Rc<ChangeSetAggregator<T, K>>, G>>> {
ferroui_reactive::Observable::create(move |observer: std::rc::Rc<dyn ferroui_system::IObserver<super::ChangeSet<std::rc::Rc<ChangeSetAggregator<T, K>>, G>>>| {
let aggregators = std::rc::Rc::new(std::cell::RefCell::new(super::ChangeAwareCache::<std::rc::Rc<ChangeSetAggregator<T, K>>, G>::new()));
let (target, on_error, on_completed) = (aggregators.clone(), observer.clone(), observer.clone());
let subscription = ferroui_reactive::ObservableExt::subscribe_all(
&source,
move |changes: super::GroupChangeSet<T, K, G>| {
let mut replaced = Vec::new();
let output = {
let mut cache = target.borrow_mut();
for change in &changes {
match change.reason() {
super::ChangeReason::Add | super::ChangeReason::Update => {
let connected = super::IConnectableCache::connect(change.current().observable_cache());
let aggregator = std::rc::Rc::new(ChangeSetAggregator::new(connected));
replaced.extend(super::IQuery::lookup(&*cache, change.key()));
super::ICache::add_or_update(&mut *cache, aggregator, change.key().clone());
}
super::ChangeReason::Remove => {
replaced.extend(super::IQuery::lookup(&*cache, change.key()));
super::ICache::remove(&mut *cache, change.key());
}
super::ChangeReason::Refresh => super::ICache::refresh(&mut *cache, change.key()),
super::ChangeReason::Moved => {}
}
}
cache.capture_changes()
};
observer.on_next(output);
for aggregator in replaced {
ferroui_system::IDisposable::dispose(&*aggregator);
}
},
move |error| on_error.on_error(error),
move || on_completed.on_completed(),
);
ferroui_reactive::disposables::Disposable::create(move || {
subscription.dispose();
let remaining = super::IQuery::items(&*aggregators.borrow());
for aggregator in remaining {
ferroui_system::IDisposable::dispose(&*aggregator);
}
})
})
}
pub trait GroupChangeSetAggregatorEx<T, K, G> {
fn as_group_aggregator(&self) -> GroupChangeSetAggregator<T, K, G>;
}
impl<T: Clone + 'static, K: Eq + std::hash::Hash + Clone + 'static, G: Eq + std::hash::Hash + Clone + 'static> GroupChangeSetAggregatorEx<T, K, G>
for std::rc::Rc<dyn ferroui_system::IObservable<super::GroupChangeSet<T, K, G>>>
{
fn as_group_aggregator(&self) -> GroupChangeSetAggregator<T, K, G> {
GroupChangeSetAggregator::new(self.clone())
}
}