use super::signal_vec::SignalVec;
use super::tracker::notify_dependents;
use super::SignalId;
use crate::utils::lock::lock_or_recover;
use std::sync::{Arc, Mutex};
#[allow(clippy::type_complexity)]
pub struct IncrementalHandlers<T: 'static, R: 'static> {
pub on_insert: Arc<dyn Fn(&mut R, usize, T) + Send + Sync>,
pub on_update: Arc<dyn Fn(&mut R, usize, T, T) + Send + Sync>,
pub on_remove: Arc<dyn Fn(&mut R, usize, T) + Send + Sync>,
pub on_replace: Arc<dyn Fn(&SignalVec<T>) -> R + Send + Sync>,
}
impl<T: 'static, R: 'static> Clone for IncrementalHandlers<T, R> {
fn clone(&self) -> Self {
Self {
on_insert: Arc::clone(&self.on_insert),
on_update: Arc::clone(&self.on_update),
on_remove: Arc::clone(&self.on_remove),
on_replace: Arc::clone(&self.on_replace),
}
}
}
impl<T: 'static, R: 'static> IncrementalHandlers<T, R> {
pub fn new() -> Self {
Self {
on_insert: Arc::new(|_, _, _| {}),
on_update: Arc::new(|_, _, _, _| {}),
on_remove: Arc::new(|_, _, _| {}),
on_replace: Arc::new(|_| panic!("Replace handler not implemented")),
}
}
pub fn insert(mut self, f: impl Fn(&mut R, usize, T) + Send + Sync + 'static) -> Self {
self.on_insert = Arc::new(f);
self
}
pub fn update(mut self, f: impl Fn(&mut R, usize, T, T) + Send + Sync + 'static) -> Self {
self.on_update = Arc::new(f);
self
}
pub fn remove(mut self, f: impl Fn(&mut R, usize, T) + Send + Sync + 'static) -> Self {
self.on_remove = Arc::new(f);
self
}
pub fn replace(mut self, f: impl Fn(&SignalVec<T>) -> R + Send + Sync + 'static) -> Self {
self.on_replace = Arc::new(f);
self
}
}
impl<T: 'static, R: 'static> Default for IncrementalHandlers<T, R> {
fn default() -> Self {
Self::new()
}
}
pub struct IncrementalComputed<T: Clone + Send + Sync + 'static, R: Clone + Send + Sync + 'static> {
source: SignalVec<T>,
cached: Arc<Mutex<R>>,
id: SignalId,
}
impl<T: Clone + Send + Sync + 'static, R: Clone + Send + Sync + 'static> IncrementalComputed<T, R> {
pub fn new(
source: SignalVec<T>,
init: impl Fn(&[T]) -> R + Send + Sync + 'static,
handlers: IncrementalHandlers<T, R>,
) -> Self {
let id = SignalId::new();
let initial_result = init(&source.get());
let cached = Arc::new(Mutex::new(initial_result));
let handlers_clone = handlers.clone();
let source_clone = source.clone();
let cached_clone = cached.clone();
source.subscribe_diff(move |diff| {
if let Ok(mut result) = cached_clone.lock() {
match diff {
super::signal_vec::VecDiff::Insert { index, value } => {
(handlers_clone.on_insert)(&mut result, index, value);
}
super::signal_vec::VecDiff::Update {
index,
old_value,
new_value,
} => {
(handlers_clone.on_update)(&mut result, index, old_value, new_value);
}
super::signal_vec::VecDiff::Remove { index, value } => {
(handlers_clone.on_remove)(&mut result, index, value);
}
super::signal_vec::VecDiff::Move {
old_index,
new_index,
value,
} => {
(handlers_clone.on_remove)(&mut result, old_index, value.clone());
(handlers_clone.on_insert)(&mut result, new_index, value);
}
super::signal_vec::VecDiff::Replace {
old_values: _,
new_values: _,
} => {
let new_result = (handlers_clone.on_replace)(&source_clone);
*result = new_result;
}
}
notify_dependents(id);
}
});
Self { source, cached, id }
}
pub fn get(&self) -> R {
lock_or_recover(&self.cached).clone()
}
pub fn read(&self) -> std::sync::MutexGuard<'_, R> {
lock_or_recover(&self.cached)
}
pub fn source(&self) -> &SignalVec<T> {
&self.source
}
pub fn invalidate(&self) {
notify_dependents(self.id);
}
pub fn id(&self) -> SignalId {
self.id
}
}
impl<T: Clone + Send + Sync + 'static, R: Clone + Send + Sync + 'static> Clone
for IncrementalComputed<T, R>
{
fn clone(&self) -> Self {
Self {
source: self.source.clone(),
cached: Arc::clone(&self.cached),
id: self.id,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_incremental_handlers_new() {
let _handlers: IncrementalHandlers<i32, Vec<i32>> = IncrementalHandlers::new();
assert!(true);
}
#[test]
fn test_incremental_handlers_builder() {
let _handlers = IncrementalHandlers::<i32, Vec<i32>>::new()
.insert(|result, index, value| {
result.insert(index, value);
})
.update(|_result, _index, _old, _new| {
})
.remove(|_result, _index, _value| {
})
.replace(|_source: &SignalVec<i32>| vec![]);
assert!(true);
}
#[test]
fn test_incremental_computed_new() {
use crate::reactive::signal_vec;
let items = signal_vec(vec![1, 2, 3, 4, 5]);
let filtered: IncrementalComputed<i32, Vec<i32>> = IncrementalComputed::new(
items.clone(),
|v| v.iter().filter(|x| *x % 2 == 0).copied().collect(),
IncrementalHandlers::new(),
);
let result = filtered.get();
assert_eq!(result, vec![2, 4]);
}
#[test]
fn test_incremental_handlers_default() {
let _handlers: IncrementalHandlers<i32, Vec<i32>> = IncrementalHandlers::default();
assert!(true);
}
#[test]
fn test_incremental_handlers_clone() {
let handlers = IncrementalHandlers::<i32, Vec<i32>>::new()
.insert(|_, _, _| {})
.update(|_, _, _, _| {})
.remove(|_, _, _| {})
.replace(|_| vec![]);
let _cloned = handlers.clone();
}
#[test]
fn test_incremental_computed_get() {
use crate::reactive::signal_vec;
let items = signal_vec(vec![1, 2, 3]);
let computed =
IncrementalComputed::new(items.clone(), |v| v.len(), IncrementalHandlers::new());
assert_eq!(computed.get(), 3);
}
#[test]
fn test_incremental_computed_source() {
use crate::reactive::signal_vec;
let items = signal_vec(vec![1, 2, 3]);
let computed =
IncrementalComputed::new(items.clone(), |v| v.len(), IncrementalHandlers::new());
let _source_ref = computed.source();
}
#[test]
fn test_incremental_computed_clone() {
use crate::reactive::signal_vec;
let items = signal_vec(vec![1, 2, 3]);
let computed1 =
IncrementalComputed::new(items.clone(), |v| v.len(), IncrementalHandlers::new());
let computed2 = computed1.clone();
assert_eq!(computed1.get(), 3);
assert_eq!(computed2.get(), 3);
}
#[test]
fn test_incremental_computed_invalidate() {
use crate::reactive::signal_vec;
let items = signal_vec(vec![1, 2, 3]);
let computed =
IncrementalComputed::new(items.clone(), |v| v.len(), IncrementalHandlers::new());
computed.invalidate();
}
#[test]
fn test_incremental_computed_id() {
use crate::reactive::signal_vec;
let items = signal_vec(vec![1, 2, 3]);
let computed1 =
IncrementalComputed::new(items.clone(), |v| v.len(), IncrementalHandlers::new());
let computed2 =
IncrementalComputed::new(items.clone(), |v| v.len(), IncrementalHandlers::new());
assert_ne!(computed1.id(), computed2.id());
}
#[test]
fn test_incremental_computed_read() {
use crate::reactive::signal_vec;
let items = signal_vec(vec![1, 2, 3]);
let computed =
IncrementalComputed::new(items.clone(), |v| v.len(), IncrementalHandlers::new());
let guard = computed.read();
assert_eq!(*guard, 3);
}
#[test]
fn test_incremental_handlers_with_string() {
use crate::reactive::signal_vec;
let _items = signal_vec(vec!["a", "b", "c"]);
let _handlers = IncrementalHandlers::<&str, String>::new()
.insert(|result, _, value| {
result.push_str(value);
})
.update(|result, _, old, new| {
*result = result.replace(old, new);
})
.remove(|result, _, value| {
*result = result.replace(value, "");
})
.replace(|source| source.get().join(","));
assert!(true);
}
#[test]
fn test_incremental_computed_with_vec_result() {
use crate::reactive::signal_vec;
let items = signal_vec(vec![1, 2, 3]);
let computed =
IncrementalComputed::new(items.clone(), |v| v.to_vec(), IncrementalHandlers::new());
assert_eq!(computed.get(), vec![1, 2, 3]);
}
#[test]
fn test_incremental_computed_with_count_result() {
use crate::reactive::signal_vec;
let items = signal_vec(vec![1, 2, 3, 4, 5]);
let computed =
IncrementalComputed::new(items.clone(), |v| v.len(), IncrementalHandlers::new());
assert_eq!(computed.get(), 5);
}
#[test]
fn test_incremental_computed_with_sum_result() {
use crate::reactive::signal_vec;
let items = signal_vec(vec![1, 2, 3, 4, 5]);
let computed = IncrementalComputed::new(
items.clone(),
|v| v.iter().sum::<i32>(),
IncrementalHandlers::new(),
);
assert_eq!(computed.get(), 15);
}
#[test]
fn test_incremental_handlers_insert_method() {
let mut result = Vec::new();
let handlers = IncrementalHandlers::<i32, Vec<i32>>::new().insert(|res, index, value| {
res.insert(index, value);
});
(handlers.on_insert)(&mut result, 0, 42);
assert_eq!(result, vec![42]);
}
#[test]
fn test_incremental_handlers_update_method() {
let mut result = vec![1, 2, 3];
let handlers =
IncrementalHandlers::<i32, Vec<i32>>::new().update(|res, index, _old, new| {
res[index] = new;
});
(handlers.on_update)(&mut result, 1, 2, 20);
assert_eq!(result, vec![1, 20, 3]);
}
#[test]
fn test_incremental_handlers_remove_method() {
let mut result = vec![1, 2, 3];
let handlers = IncrementalHandlers::<i32, Vec<i32>>::new().remove(|res, index, _value| {
res.remove(index);
});
(handlers.on_remove)(&mut result, 1, 2);
assert_eq!(result, vec![1, 3]);
}
#[test]
fn test_incremental_handlers_replace_method() {
use crate::reactive::signal_vec;
let items = signal_vec(vec![1, 2, 3]);
let handlers =
IncrementalHandlers::<i32, usize>::new().replace(|source| source.get().len());
let result = (handlers.on_replace)(&items);
assert_eq!(result, 3);
}
}