use core::{
cell::{Cell, RefCell},
ops::{Bound, RangeBounds},
};
pub use nami_core::collection::*;
use alloc::{rc::Rc, vec::Vec};
use nami_core::{
SignalIdentity,
observe::Origin,
watcher::{Context, Metadata},
};
use crate::{
Signal,
watcher::{WatcherManager, WatcherManagerGuard},
};
#[derive(Debug, Clone)]
pub struct SignalCollection<S> {
signal: S,
}
impl<S> SignalCollection<S> {
pub const fn new(signal: S) -> Self {
Self { signal }
}
}
#[derive(Debug)]
pub struct List<T> {
vec: Rc<RefCell<Vec<T>>>,
watchers: WatcherManager<Rc<[T]>>,
}
impl<T: 'static> From<Vec<T>> for List<T> {
#[track_caller]
fn from(value: Vec<T>) -> Self {
Self::from_vec(value)
}
}
impl<T: 'static> List<T> {
#[must_use]
#[track_caller]
pub fn new() -> Self {
Self::from_vec(Vec::new())
}
#[track_caller]
fn from_vec(value: Vec<T>) -> Self {
let vec = Rc::new(RefCell::new(value));
let origin = Origin::capture::<Self>(SignalIdentity::from_rc(&vec));
Self {
vec,
watchers: WatcherManager::with_origin(origin),
}
}
fn notify(&self)
where
T: Clone,
{
self.notify_with_metadata(Metadata::new());
}
fn notify_with_metadata(&self, metadata: Metadata)
where
T: Clone,
{
if self.watchers.is_empty() {
return;
}
let snapshot: Rc<[T]> = Rc::from(self.vec.borrow().as_slice());
self.watchers.notify(&Context::new(snapshot, metadata));
}
pub fn push(&self, value: T)
where
T: Clone,
{
self.vec.borrow_mut().push(value);
self.notify();
}
pub fn sort(&self)
where
T: Ord + Clone,
{
self.vec.borrow_mut().sort();
self.notify();
}
#[must_use]
pub fn pop(&self) -> Option<T>
where
T: Clone,
{
let result = self.vec.borrow_mut().pop();
if result.is_some() {
self.notify();
}
result
}
pub fn insert(&self, index: usize, value: T)
where
T: Clone,
{
self.vec.borrow_mut().insert(index, value);
self.notify();
}
#[must_use]
pub fn remove(&self, index: usize) -> T
where
T: Clone,
{
let result = self.vec.borrow_mut().remove(index);
self.notify();
result
}
pub fn clear(&self)
where
T: Clone,
{
let was_empty = self.vec.borrow().is_empty();
self.vec.borrow_mut().clear();
if !was_empty {
self.notify();
}
}
#[must_use]
pub fn replace(&self, value: Vec<T>) -> Vec<T>
where
T: Clone,
{
let previous = core::mem::replace(&mut *self.vec.borrow_mut(), value);
self.notify();
previous
}
#[must_use]
pub fn replace_with_metadata(&self, value: Vec<T>, metadata: Metadata) -> Vec<T>
where
T: Clone,
{
let previous = core::mem::replace(&mut *self.vec.borrow_mut(), value);
self.notify_with_metadata(metadata);
previous
}
#[must_use]
pub fn snapshot(&self) -> Vec<T>
where
T: Clone,
{
self.vec.borrow().clone()
}
#[must_use]
pub fn iter(&self) -> <&Self as IntoIterator>::IntoIter
where
T: Clone,
{
self.snapshot().into_iter()
}
}
impl<T: Clone + 'static> IntoIterator for List<T> {
type Item = T;
type IntoIter = alloc::vec::IntoIter<T>;
fn into_iter(self) -> Self::IntoIter {
match Rc::try_unwrap(self.vec) {
Ok(vec) => vec.into_inner().into_iter(),
Err(rc) => rc.borrow().clone().into_iter(),
}
}
}
impl<T: Clone + 'static> IntoIterator for &List<T> {
type Item = T;
type IntoIter = alloc::vec::IntoIter<T>;
fn into_iter(self) -> Self::IntoIter {
self.snapshot().into_iter()
}
}
impl<T> Clone for List<T> {
fn clone(&self) -> Self {
Self {
vec: self.vec.clone(),
watchers: self.watchers.clone(),
}
}
}
impl<T: 'static> Default for List<T> {
fn default() -> Self {
Self::new()
}
}
fn resolve_range(start_bound: Bound<usize>, end_bound: Bound<usize>, len: usize) -> (usize, usize) {
let mut start = match start_bound {
Bound::Included(n) => n,
Bound::Excluded(n) => n.saturating_add(1),
Bound::Unbounded => 0,
};
let mut end = match end_bound {
Bound::Included(n) => n.saturating_add(1),
Bound::Excluded(n) => n,
Bound::Unbounded => len,
};
start = start.min(len);
end = end.min(len);
if start > end {
start = end;
}
(start, end)
}
fn notify_signal_collection<T>(
watcher: &dyn for<'a> Fn(Context<&'a [T]>),
context: Context<Vec<T>>,
start_bound: Bound<usize>,
end_bound: Bound<usize>,
) {
let metadata = context.metadata().clone();
let snapshot = context.into_value();
let (start, end) = resolve_range(start_bound, end_bound, snapshot.len());
watcher(Context::new(&snapshot[start..end], metadata));
}
impl<S, T> Collection for SignalCollection<S>
where
S: Signal<Output = Vec<T>> + Clone,
T: Clone + 'static,
{
type Item = T;
type Guard = S::Guard;
fn get(&self, index: usize) -> Option<Self::Item> {
self.signal.get().as_slice().get(index).cloned()
}
fn len(&self) -> usize {
self.signal.get().len()
}
fn watch(
&self,
range: impl RangeBounds<usize>,
watcher: impl for<'a> Fn(Context<&'a [Self::Item]>) + 'static,
) -> Self::Guard {
let start_bound = range.start_bound().cloned();
let end_bound = range.end_bound().cloned();
let watcher = Rc::new(watcher);
let pending = Rc::new(RefCell::new(None));
let subscribed = Rc::new(Cell::new(false));
let guard = self.signal.watch({
let watcher = Rc::clone(&watcher);
let pending = Rc::clone(&pending);
let subscribed = Rc::clone(&subscribed);
move |context| {
if subscribed.get() {
notify_signal_collection(watcher.as_ref(), context, start_bound, end_bound);
} else {
*pending.borrow_mut() = Some(context);
}
}
});
let initial = pending
.borrow_mut()
.take()
.unwrap_or_else(|| Context::from(self.signal.get()));
subscribed.set(true);
notify_signal_collection(watcher.as_ref(), initial, start_bound, end_bound);
guard
}
}
impl<T: Clone + 'static> Collection for List<T> {
type Item = T;
type Guard = WatcherManagerGuard<Rc<[T]>>;
fn get(&self, index: usize) -> Option<Self::Item> {
self.vec.borrow().as_slice().get(index).cloned()
}
fn len(&self) -> usize {
self.vec.borrow().len()
}
fn watch(
&self,
range: impl RangeBounds<usize>,
watcher: impl for<'a> Fn(Context<&'a [Self::Item]>) + 'static,
) -> Self::Guard {
let start_bound = match range.start_bound() {
Bound::Included(&n) => Bound::Included(n),
Bound::Excluded(&n) => Bound::Excluded(n),
Bound::Unbounded => Bound::Unbounded,
};
let end_bound = match range.end_bound() {
Bound::Included(&n) => Bound::Included(n),
Bound::Excluded(&n) => Bound::Excluded(n),
Bound::Unbounded => Bound::Unbounded,
};
{
let snapshot: Rc<[T]> = Rc::from(self.vec.borrow().as_slice());
let (start, end) = resolve_range(start_bound, end_bound, snapshot.len());
watcher(Context::from(&snapshot[start..end]));
}
self.watchers.register_as_guard(move |ctx| {
let snapshot: Rc<[T]> = ctx.value().clone(); let metadata = ctx.metadata().clone();
let (start, end) = resolve_range(start_bound, end_bound, snapshot.len());
watcher(Context::new(&snapshot[start..end], metadata));
})
}
}
impl<T: 'static> FromIterator<T> for List<T> {
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
Self::from(iter.into_iter().collect::<Vec<_>>())
}
}
#[cfg(test)]
mod tests {
use super::*;
use alloc::{rc::Rc, vec};
use core::cell::{Cell, RefCell};
#[test]
fn test_collection_trait_basic_operations() {
let list = List::from(vec![1, 2, 3]);
assert_eq!(Collection::len(&list), 3);
assert!(!Collection::is_empty(&list));
assert_eq!(Collection::get(&list, 0), Some(1));
assert_eq!(Collection::get(&list, 1), Some(2));
assert_eq!(Collection::get(&list, 2), Some(3));
assert_eq!(Collection::get(&list, 3), None);
}
#[test]
fn replace_notifies_only_the_final_snapshot() {
let list = List::from(vec![1, 2]);
let observed = Rc::new(RefCell::new(Vec::<Vec<i32>>::new()));
let observed_for_watcher = Rc::clone(&observed);
let _guard = list.watch(.., move |context| {
observed_for_watcher
.borrow_mut()
.push(context.into_value().to_vec());
});
let previous = list.replace(vec![3, 4, 5]);
assert_eq!(previous, vec![1, 2]);
assert_eq!(*observed.borrow(), vec![vec![1, 2], vec![3, 4, 5]]);
}
#[test]
fn signal_collection_emits_initial_and_updated_snapshots() {
let values = crate::binding(vec![1, 2]);
let collection = SignalCollection::new(values.clone());
let observed = Rc::new(RefCell::new(Vec::<Vec<i32>>::new()));
let observed_for_watcher = Rc::clone(&observed);
let _guard = collection.watch(.., move |context| {
observed_for_watcher
.borrow_mut()
.push(context.into_value().to_vec());
});
values.set(vec![2, 3, 4]);
assert_eq!(*observed.borrow(), vec![vec![1, 2], vec![2, 3, 4]]);
}
#[test]
fn test_list_new_and_default() {
let list1: List<i32> = List::new();
let list2: List<i32> = List::default();
assert_eq!(Collection::len(&list1), 0);
assert!(Collection::is_empty(&list1));
assert_eq!(Collection::len(&list2), 0);
assert!(Collection::is_empty(&list2));
}
#[test]
fn test_list_from_vec() {
let vec = vec![1, 2, 3, 4, 5];
let list = List::from(vec);
assert_eq!(Collection::len(&list), 5);
for i in 0..5 {
assert_eq!(Collection::get(&list, i), Some(i + 1));
}
}
#[test]
fn test_list_push_and_pop() {
let list = List::new();
list.push(1);
list.push(2);
list.push(3);
assert_eq!(Collection::len(&list), 3);
assert_eq!(Collection::get(&list, 0), Some(1));
assert_eq!(Collection::get(&list, 1), Some(2));
assert_eq!(Collection::get(&list, 2), Some(3));
assert_eq!(list.pop(), Some(3));
assert_eq!(Collection::len(&list), 2);
assert_eq!(list.pop(), Some(2));
assert_eq!(list.pop(), Some(1));
assert_eq!(list.pop(), None);
assert!(Collection::is_empty(&list));
}
#[test]
fn test_list_insert_and_remove() {
let list = List::from(vec![1, 3, 5]);
list.insert(1, 2);
list.insert(3, 4);
assert_eq!(Collection::len(&list), 5);
assert_eq!(Collection::get(&list, 0), Some(1));
assert_eq!(Collection::get(&list, 1), Some(2));
assert_eq!(Collection::get(&list, 2), Some(3));
assert_eq!(Collection::get(&list, 3), Some(4));
assert_eq!(Collection::get(&list, 4), Some(5));
assert_eq!(list.remove(1), 2);
assert_eq!(list.remove(2), 4);
assert_eq!(Collection::len(&list), 3);
assert_eq!(Collection::get(&list, 0), Some(1));
assert_eq!(Collection::get(&list, 1), Some(3));
assert_eq!(Collection::get(&list, 2), Some(5));
}
#[test]
fn test_list_clear() {
let list = List::from(vec![1, 2, 3, 4, 5]);
assert_eq!(Collection::len(&list), 5);
list.clear();
assert_eq!(Collection::len(&list), 0);
assert!(Collection::is_empty(&list));
list.clear();
assert!(Collection::is_empty(&list));
}
#[test]
fn test_list_clone() {
let list1 = List::from(vec![1, 2, 3]);
let list2 = Clone::clone(&list1);
assert_eq!(Collection::len(&list1), Collection::len(&list2));
for i in 0..3 {
assert_eq!(Collection::get(&list1, i), Collection::get(&list2, i));
}
list1.push(4);
assert_eq!(Collection::len(&list2), 4);
assert_eq!(Collection::get(&list2, 3), Some(4));
}
#[test]
fn test_list_watcher_notifications() {
let list = List::new();
let notification_count = Rc::new(RefCell::new(0));
let count = notification_count.clone();
let _guard = Collection::watch(&list, .., move |_ctx| {
*count.borrow_mut() += 1;
});
assert_eq!(*notification_count.borrow(), 1);
{
let mut count_mut = notification_count.borrow_mut();
*count_mut = 0;
}
list.push(1);
assert_eq!(*notification_count.borrow(), 1);
list.push(2);
assert_eq!(*notification_count.borrow(), 2);
let _ = list.pop();
assert_eq!(*notification_count.borrow(), 3);
}
#[test]
fn test_list_watcher_range() {
let list = List::from(vec![1, 2, 3, 4, 5]);
let notification_count = Rc::new(RefCell::new(0));
let count = notification_count.clone();
let _guard = Collection::watch(&list, 1..4, move |ctx| {
*count.borrow_mut() += 1;
assert_eq!(ctx.into_value(), vec![2, 3, 4]);
});
list.push(6);
assert_eq!(*notification_count.borrow(), 2);
}
#[test]
fn test_vec_collection_implementation() {
let vec = vec![1, 2, 3, 4, 5];
assert_eq!(Collection::len(&vec), 5);
assert!(!Collection::is_empty(&vec));
assert_eq!(Collection::get(&vec, 2), Some(3));
assert_eq!(Collection::get(&vec, 10), None);
let called = Rc::new(Cell::new(false));
let c = called.clone();
Collection::watch(&vec, 1..3, move |_ctx| {
c.set(true);
});
assert!(!called.get()); }
#[test]
fn test_array_collection_implementation() {
let arr = [1, 2, 3, 4, 5];
assert_eq!(Collection::len(&arr), 5);
assert!(!Collection::is_empty(&arr));
assert_eq!(Collection::get(&arr, 2), Some(3));
assert_eq!(Collection::get(&arr, 10), None);
let called = Rc::new(Cell::new(false));
let c = called.clone();
Collection::watch(&arr, 0..2, move |_ctx| {
c.set(true);
});
assert!(!called.get()); }
#[test]
fn test_empty_array_collection() {
let arr: [i32; 0] = [];
assert_eq!(Collection::len(&arr), 0);
assert!(Collection::is_empty(&arr));
assert_eq!(Collection::get(&arr, 0), None);
let called = Rc::new(Cell::new(false));
let c = called.clone();
Collection::watch(&arr, .., move |_ctx| {
c.set(true);
});
assert!(!called.get()); }
#[test]
fn test_any_collection_basic_operations() {
let list = List::from(vec![1, 2, 3]);
let any_collection = AnyCollection::new(list);
assert_eq!(any_collection.len(), 3);
assert!(!any_collection.is_empty());
assert_eq!(any_collection.get(0), Some(1));
assert_eq!(any_collection.get(1), Some(2));
assert_eq!(any_collection.get(2), Some(3));
assert_eq!(any_collection.get(3), None);
}
#[test]
fn test_any_collection_from_vec() {
let vec = vec![10, 20, 30];
let any_collection = AnyCollection::new(vec);
assert_eq!(any_collection.len(), 3);
assert_eq!(any_collection.get(0), Some(10));
assert_eq!(any_collection.get(1), Some(20));
assert_eq!(any_collection.get(2), Some(30));
}
#[test]
fn test_any_collection_from_array() {
let arr = [100, 200, 300];
let any_collection = AnyCollection::new(arr);
assert_eq!(any_collection.len(), 3);
assert_eq!(any_collection.get(0), Some(100));
assert_eq!(any_collection.get(1), Some(200));
assert_eq!(any_collection.get(2), Some(300));
}
#[test]
fn test_any_collection_watcher() {
let list = List::from(vec![1, 2, 3, 4, 5]);
let any_collection = AnyCollection::new(list);
let called = Rc::new(RefCell::new(false));
let c = called.clone();
let _guard = any_collection.watch(1..3, move |ctx| {
*c.borrow_mut() = true;
assert_eq!(ctx.into_value(), vec![2, 3]);
});
assert!(*called.borrow());
}
#[test]
fn test_range_bounds_inclusive() {
let list = List::from(vec![0, 1, 2, 3, 4]);
let called = Rc::new(RefCell::new(false));
let c = called.clone();
let _guard = Collection::watch(&list, 1..=3, move |ctx| {
*c.borrow_mut() = true;
assert_eq!(ctx.into_value(), vec![1, 2, 3]);
});
assert!(*called.borrow());
}
#[test]
fn test_range_bounds_from() {
let list = List::from(vec![0, 1, 2, 3, 4]);
let called = Rc::new(RefCell::new(false));
let c = called.clone();
let _guard = Collection::watch(&list, 2.., move |ctx| {
*c.borrow_mut() = true;
assert_eq!(ctx.into_value(), vec![2, 3, 4]);
});
assert!(*called.borrow());
}
#[test]
fn test_range_bounds_to() {
let list = List::from(vec![0, 1, 2, 3, 4]);
let called = Rc::new(RefCell::new(false));
let c = called.clone();
let _guard = Collection::watch(&list, ..3, move |ctx| {
*c.borrow_mut() = true;
assert_eq!(ctx.into_value(), vec![0, 1, 2]);
});
assert!(*called.borrow());
}
#[test]
fn test_range_bounds_full() {
let list = List::from(vec![0, 1, 2, 3, 4]);
let called = Rc::new(RefCell::new(false));
let c = called.clone();
let _guard = Collection::watch(&list, .., move |ctx| {
*c.borrow_mut() = true;
assert_eq!(ctx.into_value(), vec![0, 1, 2, 3, 4]);
});
assert!(*called.borrow());
}
#[test]
fn test_out_of_bounds_range() {
let list = List::from(vec![1, 2, 3]);
let called = Rc::new(Cell::new(None::<bool>));
let c = called.clone();
let _guard = Collection::watch(&list, 10..20, move |ctx| {
let is_empty = ctx.map(<[i32]>::is_empty).into_value();
c.set(Some(is_empty));
});
assert_eq!(called.get(), Some(true));
}
#[test]
fn test_empty_range() {
let list = List::from(vec![1, 2, 3]);
let called = Rc::new(Cell::new(None::<bool>));
let c = called.clone();
let _guard = Collection::watch(&list, 2..2, move |ctx| {
let is_empty = ctx.map(<[i32]>::is_empty).into_value();
c.set(Some(is_empty));
});
assert_eq!(called.get(), Some(true));
}
#[test]
fn test_watcher_guard_cleanup() {
let list = List::new();
let notification_count = Rc::new(RefCell::new(0));
{
let count = notification_count.clone();
let _guard = Collection::watch(&list, .., move |_ctx| {
*count.borrow_mut() += 1;
});
assert_eq!(*notification_count.borrow(), 1);
{
let mut count_mut = notification_count.borrow_mut();
*count_mut = 0;
}
list.push(1);
assert_eq!(*notification_count.borrow(), 1);
}
list.push(2);
assert_eq!(*notification_count.borrow(), 1);
}
}