use core::num::NonZeroI32;
use core::num::TryFromIntError;
use core::{hash::Hash, ops::Deref};
use crate::{AnyView, View};
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Id(pub(crate) NonZeroI32);
impl From<Id> for i32 {
fn from(val: Id) -> Self {
val.0.get()
}
}
impl From<NonZeroI32> for Id {
fn from(value: NonZeroI32) -> Self {
Self(value)
}
}
impl TryFrom<i32> for Id {
type Error = TryFromIntError;
fn try_from(value: i32) -> Result<Self, Self::Error> {
NonZeroI32::try_from(value).map(Id)
}
}
#[diagnostic::on_unimplemented(
message = "`{Self}` has no stable WaterUI identity",
label = "expected a type implementing `Identifiable`",
note = "Collections such as `ForEach` and `List` diff items by id, so each item needs `Identifiable`: derive it with `#[derive(Identifiable)]` and `#[id]` on the identifier field (`use waterui::Identifiable;` — the derive is not in the prelude), for a type you do not own, wrap the value with `.use_id(..)` / `.self_id()`."
)]
pub trait Identifiable {
type Id: Hash + Ord + Clone;
fn id(&self) -> Self::Id;
}
#[derive(Debug)]
pub struct UseId<T, F> {
value: T,
f: F,
}
impl<T, F> UseId<T, F> {
pub const fn new(value: T, f: F) -> Self {
Self { value, f }
}
pub fn into_inner(self) -> T {
self.value
}
}
impl<T, F> Deref for UseId<T, F> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.value
}
}
impl<T, F, Id> Identifiable for UseId<T, F>
where
F: Fn(&T) -> Id,
Id: Ord + Hash + Clone,
{
type Id = Id;
fn id(&self) -> Self::Id {
(self.f)(&self.value)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct SelfId<T>(T);
impl<T> SelfId<T> {
pub const fn new(value: T) -> Self {
Self(value)
}
pub fn into_inner(self) -> T {
self.0
}
}
impl<T: Hash + Ord + Clone> Identifiable for SelfId<T> {
type Id = T;
fn id(&self) -> Self::Id {
self.0.clone()
}
}
impl<T> Deref for SelfId<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.0
}
}
pub trait IdentifiableExt: Sized {
fn use_id<F, Id>(self, f: F) -> UseId<Self, F>
where
F: Fn(&Self) -> Id,
Id: Ord + Hash,
{
UseId { value: self, f }
}
fn self_id(self) -> SelfId<Self> {
SelfId(self)
}
}
impl<T> IdentifiableExt for T {}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct TaggedView<T, V> {
pub tag: T,
pub content: V,
}
impl<T, V: View> TaggedView<T, V> {
pub const fn new(tag: T, content: V) -> Self {
Self { tag, content }
}
pub fn map<F, T2>(self, f: F) -> TaggedView<T2, V>
where
F: Fn(T) -> T2,
{
TaggedView {
tag: f(self.tag),
content: self.content,
}
}
pub fn mapping(self, mapping: &Mapping<T>) -> TaggedView<Id, V>
where
T: Ord + Clone,
{
self.map(move |v| mapping.register(v))
}
pub fn erase(self) -> TaggedView<T, AnyView> {
TaggedView {
tag: self.tag,
content: AnyView::new(self.content),
}
}
}
use core::cell::RefCell;
use alloc::{collections::btree_map::BTreeMap, rc::Rc};
use nami::Binding;
#[derive(Debug)]
struct MappingInner<T> {
counter: i32,
to_id: BTreeMap<T, Id>,
from_id: BTreeMap<Id, T>,
}
impl<T: Ord + Clone> MappingInner<T> {
pub const fn new() -> Self {
Self {
counter: 1,
to_id: BTreeMap::new(),
from_id: BTreeMap::new(),
}
}
pub fn register(&mut self, value: T) -> Id {
let id = Id(NonZeroI32::new(self.counter).expect("counter should not be zero"));
self.to_id.insert(value.clone(), id);
self.from_id.insert(id, value);
self.counter = self
.counter
.checked_add(1)
.expect("counter should not overflow");
id
}
pub fn try_to_id(&self, value: &T) -> Option<Id> {
self.to_id.get(value).copied()
}
pub fn to_data(&self, id: Id) -> Option<T> {
self.from_id.get(&id).cloned()
}
#[allow(clippy::wrong_self_convention)]
pub fn to_id(&mut self, value: T) -> Id {
self.try_to_id(&value)
.unwrap_or_else(|| self.register(value))
}
}
#[derive(Debug)]
pub struct Mapping<T>(Rc<RefCell<MappingInner<T>>>);
impl<T> Clone for Mapping<T> {
fn clone(&self) -> Self {
Self(self.0.clone())
}
}
impl<T: Ord + Clone> Default for Mapping<T> {
fn default() -> Self {
Self::new()
}
}
impl<T: Ord + Clone> Mapping<T> {
#[must_use]
pub fn new() -> Self {
Self(Rc::new(RefCell::new(MappingInner::new())))
}
pub fn register(&self, value: T) -> Id {
self.0.borrow_mut().register(value)
}
pub fn try_to_id(&self, value: &T) -> Option<Id> {
self.0.borrow().try_to_id(value)
}
pub fn to_id(&self, value: T) -> Id {
self.0.borrow_mut().to_id(value)
}
#[must_use]
pub fn to_data(&self, id: Id) -> Option<T> {
self.0.borrow().to_data(id)
}
#[must_use]
pub fn binding(&self, source: &Binding<T>) -> Binding<Id>
where
T: 'static,
{
let mapping = self.clone();
let mapping2 = self.clone();
Binding::mapping(
source,
move |value| mapping.to_id(value),
move |binding, value| {
binding.set(
mapping2
.to_data(value)
.expect("Invalid binding mapping : Data not found"),
);
},
)
}
#[must_use]
pub fn optional_binding(&self, source: &Binding<Option<T>>) -> Binding<Option<Id>>
where
T: 'static,
{
let mapping = self.clone();
let mapping2 = self.clone();
Binding::mapping(
source,
move |value| value.map(|value| mapping.to_id(value)),
move |binding, value| {
binding.set(value.map(|value| {
mapping2
.to_data(value)
.expect("Invalid optional binding mapping : Data not found")
}));
},
)
}
}