use crate::id::Id as RawId;
use crate::{AnyView, View};
use alloc::fmt::Debug;
use alloc::{boxed::Box, collections::BTreeMap, rc::Rc, vec::Vec};
use core::any::type_name;
use core::fmt;
use core::num::NonZeroI32;
use core::ops::{Bound, RangeBounds};
use core::{
cell::{Cell, RefCell},
hash::Hash,
};
use nami::collection::Collection;
use nami::watcher::{BoxWatcherGuard, Context, WatcherGuard};
use nami::{Computed, Signal};
use crate::id::{Identifiable, SelfId};
pub trait Views {
type Id: 'static + Hash + Ord + Clone;
type Guard: WatcherGuard;
type View: View;
fn get_id(&self, index: usize) -> Option<Self::Id>;
fn len(&self) -> Computed<usize>;
fn is_empty(&self) -> bool {
nami::Signal::get(&self.len()) == 0
}
fn watch(
&self,
range: impl RangeBounds<usize>,
watcher: impl for<'a> Fn(Context<&'a [Self::Id]>) + 'static, ) -> Self::Guard;
fn get_view(&self, id: usize) -> Option<Self::View>;
}
pub struct AnyViews<V>(Box<dyn AnyViewsImpl<View = V>>);
impl<V> Debug for AnyViews<V> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(type_name::<Self>())
}
}
pub struct SharedAnyViews<V>(Rc<dyn AnyViewsImpl<View = V>>);
impl<V> Clone for SharedAnyViews<V> {
fn clone(&self) -> Self {
Self(self.0.clone())
}
}
impl<V> SharedAnyViews<V> {
pub fn new(contents: impl Views<View = V> + 'static) -> Self {
Self(Rc::new(IntoAnyViews::new(contents)))
}
}
impl<V> From<AnyViews<V>> for SharedAnyViews<V> {
fn from(value: AnyViews<V>) -> Self {
Self(Rc::from(value.0))
}
}
impl<V> Debug for SharedAnyViews<V> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(type_name::<Self>())
}
}
impl<V: View> Views for SharedAnyViews<V> {
type Id = SelfId<RawId>;
type Guard = BoxWatcherGuard;
type View = V;
fn get_id(&self, index: usize) -> Option<Self::Id> {
self.0.get_id(index).map(SelfId::new)
}
fn get_view(&self, index: usize) -> Option<Self::View> {
self.0.get_view(index)
}
fn len(&self) -> Computed<usize> {
self.0.len()
}
fn watch(
&self,
range: impl RangeBounds<usize>,
watcher: impl for<'a> Fn(Context<&'a [Self::Id]>) + 'static, ) -> Self::Guard {
self.0.watch(
(range.start_bound().cloned(), range.end_bound().cloned()),
Box::new(move |ctx| {
let ctx =
ctx.map(|value| value.iter().copied().map(SelfId::new).collect::<Vec<_>>());
watcher(ctx.as_deref());
}),
)
}
}
trait AnyViewsImpl {
type View;
fn get_view(&self, index: usize) -> Option<Self::View>;
fn get_id(&self, index: usize) -> Option<RawId>;
fn len(&self) -> Computed<usize>;
#[allow(clippy::type_complexity)]
fn watch(
&self,
range: (Bound<usize>, Bound<usize>),
watcher: Box<dyn for<'a> Fn(Context<&'a [RawId]>) + 'static>,
) -> BoxWatcherGuard;
}
#[derive(Debug)]
struct IdGenerator<Id> {
map: RefCell<BTreeMap<Id, i32>>,
counter: Cell<i32>,
}
impl<Id: Hash + Ord> Default for IdGenerator<Id> {
fn default() -> Self {
Self::new()
}
}
impl<Id: Hash + Ord> IdGenerator<Id> {
pub const fn new() -> Self {
Self {
map: RefCell::new(BTreeMap::new()),
counter: Cell::new(i32::MIN),
}
}
pub fn to_id(&self, value: Id) -> RawId {
let mut this = self.map.borrow_mut();
if let Some(&id) = this.get(&value) {
return RawId::from(NonZeroI32::new(id).expect("stored raw id must never be zero"));
}
let mut id = self.counter.get();
if id == 0 {
id = 1;
}
let mut next = id.checked_add(1).expect("id counter exhausted");
if next == 0 {
next = next.checked_add(1).expect("id counter exhausted");
}
self.counter.set(next);
this.insert(value, id);
RawId::from(NonZeroI32::new(id).expect("generated raw id must never be zero"))
}
}
struct IntoAnyViews<V>
where
V: Views,
{
contents: V,
id: Rc<IdGenerator<V::Id>>,
}
impl<V> IntoAnyViews<V>
where
V: Views + 'static,
{
pub fn new(contents: V) -> Self {
Self {
contents,
id: Rc::default(),
}
}
}
impl<V> AnyViewsImpl for IntoAnyViews<V>
where
V: Views + 'static,
{
type View = V::View;
fn get_view(&self, index: usize) -> Option<Self::View> {
self.contents.get_view(index)
}
fn get_id(&self, index: usize) -> Option<RawId> {
self.contents.get_id(index).map(|item| self.id.to_id(item))
}
fn len(&self) -> Computed<usize> {
self.contents.len()
}
fn watch(
&self,
range: (Bound<usize>, Bound<usize>),
watcher: Box<dyn for<'a> Fn(Context<&'a [RawId]>) + 'static>,
) -> BoxWatcherGuard {
let id = self.id.clone();
Box::new(self.contents.watch(range, move |ctx| {
let ctx = ctx.map(|value| {
value
.iter()
.map(|data| id.to_id(data.clone()))
.collect::<Vec<_>>()
});
watcher(ctx.as_deref());
}))
}
}
impl<V> AnyViews<V>
where
V: View,
{
pub fn new<C>(contents: C) -> Self
where
C: Views<View = V> + 'static,
{
Self(Box::new(IntoAnyViews {
id: Rc::new(IdGenerator::<C::Id>::new()),
contents,
}))
}
}
impl<V> Views for AnyViews<V>
where
V: View,
{
type Id = SelfId<RawId>;
type Guard = BoxWatcherGuard;
type View = V;
fn get_id(&self, index: usize) -> Option<Self::Id> {
self.0.get_id(index).map(SelfId::new)
}
fn get_view(&self, index: usize) -> Option<Self::View> {
self.0.get_view(index)
}
fn len(&self) -> Computed<usize> {
self.0.len()
}
fn watch(
&self,
range: impl RangeBounds<usize>,
watcher: impl for<'a> Fn(Context<&'a [Self::Id]>) + 'static, ) -> Self::Guard {
self.0.watch(
(range.start_bound().cloned(), range.end_bound().cloned()),
Box::new(move |ctx| {
let ctx =
ctx.map(|value| value.iter().copied().map(SelfId::new).collect::<Vec<_>>());
watcher(ctx.as_deref());
}),
)
}
}
#[derive(Debug, Clone)]
pub struct ForEach<C, F, V>
where
C: Collection,
C::Item: Identifiable,
F: Fn(C::Item) -> V,
V: View,
{
data: C,
generator: F,
}
impl<C, F, V> ForEach<C, F, V>
where
C: Collection,
C::Item: Identifiable,
F: Fn(C::Item) -> V,
V: View,
{
pub const fn new(data: C, generator: F) -> Self {
Self { data, generator }
}
pub fn into_inner(self) -> (C, F) {
(self.data, self.generator)
}
}
#[derive(Clone)]
struct CollectionLenSignal<C>(C);
impl<C> Signal for CollectionLenSignal<C>
where
C: Collection + Clone,
{
type Output = usize;
type Guard = C::Guard;
fn get(&self) -> Self::Output {
self.0.len()
}
fn watch(&self, watcher: impl Fn(Context<Self::Output>) + 'static) -> Self::Guard {
self.0.watch(.., move |ctx| {
let len = ctx.value().len();
watcher(ctx.map(move |_| len));
})
}
}
impl<C, F, V> Collection for ForEach<C, F, V>
where
C: Collection,
C::Item: Identifiable,
F: 'static + Fn(C::Item) -> V,
V: View,
{
type Item = <C::Item as Identifiable>::Id;
type Guard = C::Guard;
fn get(&self, index: usize) -> Option<Self::Item> {
self.data.get(index).map(|item| item.id())
}
fn len(&self) -> usize {
self.data.len()
}
fn watch(
&self,
range: impl RangeBounds<usize>,
watcher: impl for<'a> Fn(Context<&'a [Self::Item]>) + 'static, ) -> Self::Guard {
self.data.watch(range, move |ctx| {
let ctx = ctx.map(|value| value.iter().map(Identifiable::id).collect::<Vec<_>>());
watcher(ctx.as_deref());
})
}
}
impl<C, F, V> Views for ForEach<C, F, V>
where
C: Collection + Clone,
C::Item: Identifiable,
F: 'static + Fn(C::Item) -> V,
V: View,
{
type Id = <C::Item as Identifiable>::Id;
type View = V;
type Guard = C::Guard;
fn get_id(&self, index: usize) -> Option<Self::Id> {
self.data.get(index).map(|item| item.id())
}
fn get_view(&self, index: usize) -> Option<Self::View> {
self.data.get(index).map(|item| (self.generator)(item))
}
fn len(&self) -> Computed<usize> {
Computed::new(CollectionLenSignal(self.data.clone()))
}
fn watch(
&self,
range: impl RangeBounds<usize>,
watcher: impl for<'a> Fn(Context<&'a [Self::Id]>) + 'static, ) -> Self::Guard {
self.data.watch(range, move |ctx| {
let ctx = ctx.map(|value| value.iter().map(Identifiable::id).collect::<Vec<_>>());
watcher(ctx.as_deref());
})
}
}
#[derive(Debug, Clone)]
pub struct Constant<C>
where
C: Collection,
C::Item: View,
{
value: C,
}
impl<C> Constant<C>
where
C: Collection,
C::Item: View,
{
pub const fn new(value: C) -> Self {
Self { value }
}
}
impl<C> Collection for Constant<C>
where
C: Collection + Clone,
C::Item: View,
{
type Item = SelfId<usize>;
type Guard = ();
fn get(&self, index: usize) -> Option<Self::Item> {
if index < self.value.len() {
Some(SelfId::new(index))
} else {
None
}
}
fn len(&self) -> usize {
self.value.len()
}
fn watch(
&self,
_range: impl RangeBounds<usize>,
_watcher: impl for<'a> Fn(Context<&'a [Self::Item]>) + 'static, ) -> Self::Guard {
}
}
impl<V> Views for Constant<V>
where
V: Collection + Clone,
V::Item: View,
{
type Id = SelfId<usize>;
type Guard = ();
type View = V::Item;
fn len(&self) -> Computed<usize> {
Computed::constant(self.value.len())
}
fn get_id(&self, index: usize) -> Option<Self::Id> {
if index < self.value.len() {
Some(SelfId::new(index))
} else {
None
}
}
fn get_view(&self, index: usize) -> Option<Self::View> {
self.value.get(index)
}
fn watch(
&self,
_range: impl RangeBounds<usize>,
_watcher: impl for<'a> Fn(Context<&'a [Self::Id]>) + 'static, ) -> Self::Guard {
}
}
impl<V: View + Clone> Views for Vec<V> {
type Id = SelfId<usize>;
type Guard = ();
type View = V;
fn len(&self) -> Computed<usize> {
Computed::constant(self.as_slice().len())
}
fn get_id(&self, index: usize) -> Option<Self::Id> {
if index < self.as_slice().len() {
Some(SelfId::new(index))
} else {
None
}
}
fn get_view(&self, index: usize) -> Option<Self::View> {
self.as_slice().get(index).cloned()
}
fn watch(
&self,
_range: impl RangeBounds<usize>,
_watcher: impl for<'a> Fn(Context<&'a [Self::Id]>) + 'static, ) -> Self::Guard {
}
}
impl<V: View + Clone, const N: usize> Views for [V; N] {
type Id = SelfId<usize>;
type Guard = ();
type View = V;
fn len(&self) -> Computed<usize> {
Computed::constant(self.as_ref().len())
}
fn get_id(&self, index: usize) -> Option<Self::Id> {
if index < self.as_ref().len() {
Some(SelfId::new(index))
} else {
None
}
}
fn get_view(&self, index: usize) -> Option<Self::View> {
Collection::get(self, index)
}
fn watch(
&self,
_range: impl RangeBounds<usize>,
_watcher: impl for<'a> Fn(Context<&'a [Self::Id]>) + 'static, ) -> Self::Guard {
}
}
#[derive(Debug, Clone)]
pub struct Map<C, F> {
source: C,
f: F,
}
impl<C, F, V> Map<C, F>
where
C: Views,
F: Fn(C::View) -> V,
V: View,
{
#[must_use]
pub const fn new(source: C, f: F) -> Self {
Self { source, f }
}
}
impl<C, F, V> Views for Map<C, F>
where
C: Views,
F: Clone + Fn(C::View) -> V,
V: View,
{
type Id = C::Id;
type Guard = C::Guard;
type View = V;
fn len(&self) -> Computed<usize> {
self.source.len()
}
fn get_id(&self, index: usize) -> Option<Self::Id> {
self.source.get_id(index)
}
fn get_view(&self, index: usize) -> Option<Self::View> {
self.source.get_view(index).map(&self.f)
}
fn watch(
&self,
range: impl RangeBounds<usize>,
watcher: impl for<'a> Fn(Context<&'a [Self::Id]>) + 'static, ) -> Self::Guard {
self.source.watch(range, watcher)
}
}
pub trait ViewsExt: Views {
fn map<F, V>(self, f: F) -> Map<Self, F>
where
Self: Sized,
F: Fn(Self::View) -> V,
V: View,
{
Map::new(self, f)
}
fn erase(self) -> AnyViews<AnyView>
where
Self: 'static + Sized,
{
AnyViews::new(self.map(AnyView::new))
}
}
impl<T: Views> ViewsExt for T {}
#[cfg(test)]
mod tests {
use super::*;
use alloc::{rc::Rc, vec, vec::Vec};
use core::cell::RefCell;
use nami::{Signal, SignalExt, binding, collection::List};
#[derive(Clone, Debug)]
struct TestItem {
id: i32,
}
impl Identifiable for TestItem {
type Id = i32;
fn id(&self) -> Self::Id {
self.id
}
}
#[derive(Clone)]
struct ReactiveLenViews {
len_signal: nami::Binding<usize>,
}
impl Views for ReactiveLenViews {
type Id = SelfId<usize>;
type Guard = ();
type View = ();
fn get_id(&self, index: usize) -> Option<Self::Id> {
(index < self.len_signal.get()).then_some(SelfId::new(index))
}
fn len(&self) -> Computed<usize> {
self.len_signal.computed()
}
fn watch(
&self,
_range: impl RangeBounds<usize>,
_watcher: impl for<'a> Fn(Context<&'a [Self::Id]>) + 'static,
) -> Self::Guard {
}
fn get_view(&self, index: usize) -> Option<Self::View> {
(index < self.len_signal.get()).then_some(())
}
}
#[test]
fn len_tracks_reactive_len_signal() {
let len_signal = binding(2usize);
let views = ReactiveLenViews {
len_signal: len_signal.clone(),
};
assert_eq!(views.len().get(), 2);
len_signal.set(5);
assert_eq!(views.len().get(), 5);
}
#[test]
fn map_preserves_reactive_len() {
let len_signal = binding(1usize);
let views = ReactiveLenViews {
len_signal: len_signal.clone(),
};
let mapped = views.map(|view| view);
assert_eq!(mapped.len().get(), 1);
len_signal.set(4);
assert_eq!(mapped.len().get(), 4);
}
#[test]
fn for_each_watch_uses_requested_range() {
let list = List::from(vec![
TestItem { id: 1 },
TestItem { id: 2 },
TestItem { id: 3 },
TestItem { id: 4 },
]);
let views = ForEach::new(list.clone(), |_item| ());
let snapshots: Rc<RefCell<Vec<Vec<i32>>>> = Rc::new(RefCell::new(Vec::new()));
let snapshots_ref = snapshots.clone();
let _guard = Views::watch(&views, 1..3, move |ctx: Context<&[i32]>| {
snapshots_ref.borrow_mut().push(ctx.into_value().to_vec());
});
assert_eq!(snapshots.borrow().as_slice(), &[vec![2, 3]]);
list.insert(0, TestItem { id: 9 });
let borrowed = snapshots.borrow();
let latest_snapshot = borrowed.last().expect("watch should emit after insert");
assert_eq!(latest_snapshot.as_slice(), &[1, 2]);
}
}