#![allow(clippy::type_complexity)]
use super::signal::{Signal, Subscription};
use super::tracker::notify_dependents;
use super::SignalId;
use std::sync::Arc;
use std::sync::Mutex as StdMutex;
#[derive(Debug, Clone, PartialEq)]
pub enum VecDiff<T> {
Insert {
index: usize,
value: T,
},
Update {
index: usize,
old_value: T,
new_value: T,
},
Remove {
index: usize,
value: T,
},
Move {
old_index: usize,
new_index: usize,
value: T,
},
Replace {
old_values: Vec<T>,
new_values: Vec<T>,
},
}
impl<T> VecDiff<T> {
pub fn index(&self) -> Option<usize> {
match self {
Self::Insert { index, .. } => Some(*index),
Self::Update { index, .. } => Some(*index),
Self::Remove { index, .. } => Some(*index),
Self::Move { old_index, .. } => Some(*old_index),
Self::Replace { .. } => None,
}
}
pub fn value(&self) -> Option<&T> {
match self {
Self::Insert { value, .. } => Some(value),
Self::Update { new_value, .. } => Some(new_value),
Self::Remove { value, .. } => Some(value),
Self::Move { value, .. } => Some(value),
Self::Replace { .. } => None,
}
}
}
pub struct SignalVec<T> {
inner: Signal<Vec<T>>,
id: SignalId,
diff_subscribers: Arc<StdMutex<Vec<Arc<dyn Fn(VecDiff<T>) + Send + Sync>>>>,
}
impl<T: Send + Sync + Clone + 'static> SignalVec<T> {
pub fn new(values: Vec<T>) -> Self {
let inner = Signal::new(values);
let id = SignalId::new();
Self {
inner,
id,
diff_subscribers: Arc::new(StdMutex::new(Vec::new())),
}
}
pub fn read(&self) -> std::sync::RwLockReadGuard<'_, Vec<T>> {
self.inner.read()
}
pub fn get(&self) -> Vec<T> {
self.inner.get()
}
pub fn len(&self) -> usize {
self.inner.with(|v| v.len())
}
pub fn is_empty(&self) -> bool {
self.inner.with(|v| v.is_empty())
}
pub fn get_index(&self, index: usize) -> Option<T> {
self.inner.with(|v| v.get(index).cloned())
}
pub fn insert(&self, index: usize, value: T) {
self.inner.update(|v| {
v.insert(index, value.clone());
});
self.notify_diff(VecDiff::Insert { index, value });
}
pub fn push(&self, value: T) {
let index = self.len();
self.insert(index, value);
}
pub fn remove(&self, index: usize) -> Option<T> {
let old_value = self.get_index(index)?;
self.inner.update(|v| {
v.remove(index);
});
self.notify_diff(VecDiff::Remove {
index,
value: old_value.clone(),
});
Some(old_value)
}
pub fn update(&self, index: usize, new_value: T) {
let old_value = self.get_index(index);
if let Some(old) = old_value {
self.inner.update(|v| {
if let Some(elem) = v.get_mut(index) {
*elem = new_value.clone();
}
});
self.notify_diff(VecDiff::Update {
index,
old_value: old,
new_value,
});
}
}
pub fn modify(&self, index: usize, f: impl FnOnce(&mut T)) {
let old_value = self.get_index(index);
self.inner.update(|v| {
if let Some(elem) = v.get_mut(index) {
f(elem);
}
});
if let Some(old) = old_value {
let new_value = self.get_index(index);
if let Some(new) = new_value {
self.notify_diff(VecDiff::Update {
index,
old_value: old,
new_value: new,
});
}
}
}
pub fn pop(&self) -> Option<T> {
let index = self.len().checked_sub(1)?;
self.remove(index)
}
pub fn clear(&self) {
let old_values = self.get();
self.inner.update(|v| v.clear());
self.notify_diff(VecDiff::Replace {
old_values,
new_values: vec![],
});
}
pub fn replace(&self, new_values: Vec<T>) {
let old_values = self.get();
self.inner.set(new_values.clone());
self.notify_diff(VecDiff::Replace {
old_values,
new_values,
});
}
pub fn subscribe_diff(
&self,
callback: impl Fn(VecDiff<T>) + Send + Sync + 'static,
) -> VecSubscription<T> {
let callback = Arc::new(callback);
if let Ok(mut subs) = self.diff_subscribers.lock() {
subs.push(callback.clone());
}
let inner_sub = self.inner.subscribe(|| {});
VecSubscription {
_inner: inner_sub,
_callback: callback,
_diff_subscribers: self.diff_subscribers.clone(),
}
}
fn notify_diff(&self, diff: VecDiff<T>) {
if let Ok(subs) = self.diff_subscribers.lock() {
for callback in subs.iter() {
callback(diff.clone());
}
}
notify_dependents(self.id);
}
pub fn inner(&self) -> &Signal<Vec<T>> {
&self.inner
}
pub fn id(&self) -> SignalId {
self.id
}
}
impl<T: Send + Sync + Clone + 'static> Clone for SignalVec<T> {
fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
id: self.id,
diff_subscribers: Arc::clone(&self.diff_subscribers),
}
}
}
pub struct VecSubscription<T> {
_inner: Subscription,
_callback: Arc<dyn Fn(VecDiff<T>) + Send + Sync>,
_diff_subscribers: Arc<StdMutex<Vec<Arc<dyn Fn(VecDiff<T>) + Send + Sync>>>>,
}
impl<T> Drop for VecSubscription<T> {
fn drop(&mut self) {
if let Ok(mut subs) = self._diff_subscribers.lock() {
subs.retain(|cb| !Arc::ptr_eq(cb, &self._callback));
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_signal_vec_new() {
let vec = SignalVec::new(vec![1, 2, 3]);
assert_eq!(vec.len(), 3);
assert!(!vec.is_empty());
}
#[test]
fn test_signal_vec_push() {
let vec = SignalVec::new(vec![1, 2, 3]);
vec.push(4);
assert_eq!(vec.len(), 4);
assert_eq!(vec.get(), vec![1, 2, 3, 4]);
}
#[test]
fn test_signal_vec_insert() {
let vec = SignalVec::new(vec![1, 2, 4]);
vec.insert(2, 3);
assert_eq!(vec.get(), vec![1, 2, 3, 4]);
}
#[test]
fn test_signal_vec_remove() {
let vec = SignalVec::new(vec![1, 2, 3, 4]);
let removed = vec.remove(2);
assert_eq!(removed, Some(3));
assert_eq!(vec.get(), vec![1, 2, 4]);
}
#[test]
fn test_signal_vec_update() {
let vec = SignalVec::new(vec![1, 2, 3]);
vec.update(1, 20);
assert_eq!(vec.get(), vec![1, 20, 3]);
}
#[test]
fn test_signal_vec_modify() {
let vec = SignalVec::new(vec![1, 2, 3]);
vec.modify(1, |v| *v *= 10);
assert_eq!(vec.get(), vec![1, 20, 3]);
}
#[test]
fn test_signal_vec_pop() {
let vec = SignalVec::new(vec![1, 2, 3]);
let popped = vec.pop();
assert_eq!(popped, Some(3));
assert_eq!(vec.get(), vec![1, 2]);
}
#[test]
fn test_signal_vec_clear() {
let vec = SignalVec::new(vec![1, 2, 3]);
vec.clear();
assert_eq!(vec.len(), 0);
assert!(vec.is_empty());
}
#[test]
fn test_signal_vec_replace() {
let vec = SignalVec::new(vec![1, 2, 3]);
vec.replace(vec![4, 5, 6]);
assert_eq!(vec.get(), vec![4, 5, 6]);
}
#[test]
fn test_signal_vec_get_index() {
let vec = SignalVec::new(vec![1, 2, 3]);
assert_eq!(vec.get_index(0), Some(1));
assert_eq!(vec.get_index(1), Some(2));
assert_eq!(vec.get_index(10), None);
}
#[test]
fn test_vec_diff_insert() {
let diff = VecDiff::Insert {
index: 0,
value: 42,
};
assert_eq!(diff.index(), Some(0));
assert_eq!(diff.value(), Some(&42));
}
#[test]
fn test_vec_diff_remove() {
let diff = VecDiff::Remove {
index: 1,
value: "test",
};
assert_eq!(diff.index(), Some(1));
assert_eq!(diff.value(), Some(&"test"));
}
#[test]
fn test_vec_diff_update() {
let diff = VecDiff::Update {
index: 2,
old_value: 1,
new_value: 10,
};
assert_eq!(diff.index(), Some(2));
assert_eq!(diff.value(), Some(&10));
}
#[test]
fn test_vec_diff_replace_no_index() {
let diff = VecDiff::Replace {
old_values: vec![1, 2],
new_values: vec![3, 4],
};
assert_eq!(diff.index(), None);
assert_eq!(diff.value(), None);
}
#[test]
fn test_signal_vec_clone() {
let vec1 = SignalVec::new(vec![1, 2, 3]);
let vec2 = vec1.clone();
vec1.push(4);
assert_eq!(vec2.get(), vec![1, 2, 3, 4]);
}
}