use std::{borrow::Borrow, cell::RefCell, fmt::Display, ops::Deref, rc::Rc};
pub trait PropertyUpdate<S: ?Sized> {
type UpdateContext;
fn compare_and_set_ref(dest: &mut Self, src: &S, ctx: &mut Self::UpdateContext);
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default, Hash)]
pub struct Prop<T> {
inner: T,
}
impl<T> Prop<T> {
#[inline]
pub fn new(inner: T) -> Self {
Self { inner }
}
}
impl<T> Deref for Prop<T> {
type Target = T;
#[inline]
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl<T> AsRef<T> for Prop<T> {
#[inline]
fn as_ref(&self) -> &T {
&self.inner
}
}
impl<T> Borrow<T> for Prop<T> {
#[inline]
fn borrow(&self) -> &T {
&self.inner
}
}
impl<T: Display> Display for Prop<T> {
#[inline]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.inner.fmt(f)
}
}
pub trait PropAsRef<S: ?Sized + PartialEq> {
fn property_as_ref(&self) -> &S;
fn property_to_owned(s: &S) -> Self
where
Self: Sized;
}
impl<S: ?Sized + PartialEq + ToOwned<Owned = T>, T: Borrow<S>> PropAsRef<S> for T {
#[inline]
fn property_as_ref(&self) -> &S {
self.borrow()
}
#[inline]
fn property_to_owned(s: &S) -> Self
where
Self: Sized,
{
s.to_owned()
}
}
impl<S: ?Sized + PartialEq, T: PropAsRef<S>> PropertyUpdate<S> for Prop<T> {
type UpdateContext = bool;
#[inline]
fn compare_and_set_ref(dest: &mut Self, src: &S, ctx: &mut bool) {
if dest.inner.property_as_ref() == src {
return;
}
dest.inner = PropAsRef::property_to_owned(src);
*ctx = true;
}
}
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Default)]
pub struct BindingProp<T> {
value: BindingValue<T>,
}
impl<T> BindingProp<T> {
#[inline]
pub fn new(default_value: T) -> Self {
Self {
value: BindingValue::new(default_value),
}
}
#[inline]
pub fn set(&mut self, v: T) {
self.value.set(v);
}
#[inline]
pub fn with<R>(&self, f: impl FnOnce(&T) -> R) -> R {
self.value.with(f)
}
#[inline]
pub fn update<R>(&self, f: impl FnOnce(&mut T) -> R) -> R {
self.value.update(f)
}
}
impl<T: Clone> BindingProp<T> {
pub fn get(&self) -> T {
self.value.get()
}
}
impl<T> PropertyUpdate<BindingValue<T>> for BindingProp<T> {
type UpdateContext = bool;
#[inline]
fn compare_and_set_ref(dest: &mut Self, src: &BindingValue<T>, ctx: &mut Self::UpdateContext) {
if BindingValue::ptr_eq(&dest.value, src) {
return;
}
dest.value = src.clone_ref();
*ctx = true;
}
}
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Default)]
pub struct BindingValue<T> {
inner: Rc<RefCell<T>>,
}
impl<T> BindingValue<T> {
#[inline]
pub fn new(default_value: T) -> Self {
Self {
inner: Rc::new(RefCell::new(default_value)),
}
}
#[doc(hidden)]
#[inline]
pub fn ptr_eq(a: &Self, b: &Self) -> bool {
Rc::ptr_eq(&a.inner, &b.inner)
}
#[doc(hidden)]
#[inline]
pub fn clone_ref(&self) -> Self {
if Rc::strong_count(&self.inner) > 1 {
panic!("A `BindingValue` cannot be associated to more than one `BindingProp`");
}
Self {
inner: self.inner.clone(),
}
}
#[inline]
pub fn set(&mut self, v: T) {
*self.inner.borrow_mut() = v;
}
#[inline]
pub fn with<R>(&self, f: impl FnOnce(&T) -> R) -> R {
f(&(*self.inner).borrow())
}
#[inline]
pub fn update<R>(&self, f: impl FnOnce(&mut T) -> R) -> R {
f(&mut (*self.inner).borrow_mut())
}
}
impl<T: Clone> BindingValue<T> {
pub fn get(&self) -> T {
(*self.inner).borrow().clone()
}
}
pub trait ListPropertyInit {
type UpdateContext;
fn init_list(dest: &mut Self, count: usize, ctx: &mut Self::UpdateContext)
where
Self: Sized;
}
pub trait ListPropertyUpdate<S: ?Sized>: ListPropertyInit {
type ItemValue: ?Sized;
fn compare_and_set_item_ref<U: ListPropertyItem<Self, S, Value = Self::ItemValue>>(
dest: &mut Self,
index: usize,
src: &S,
ctx: &mut Self::UpdateContext,
) where
Self: Sized;
}
pub trait ListPropertyItem<L: ListPropertyUpdate<S>, S: ?Sized> {
type Value: ?Sized;
fn item_value<'a>(
dest: &mut L,
index: usize,
s: &'a S,
ctx: &mut L::UpdateContext,
) -> &'a Self::Value;
}
#[derive(Debug, Clone, Default, PartialEq, Eq, PartialOrd, Ord)]
pub struct ListProp<T: Default> {
inner: Box<[T]>,
}
impl<T: Default> ListProp<T> {
#[inline]
pub fn new() -> Self {
Self {
inner: Box::new([]),
}
}
}
impl<'a, T: Default> IntoIterator for &'a ListProp<T> {
type IntoIter = std::slice::Iter<'a, T>;
type Item = &'a T;
#[inline]
fn into_iter(self) -> Self::IntoIter {
self.inner.into_iter()
}
}
impl<T: Default> Deref for ListProp<T> {
type Target = [T];
#[inline]
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl<T: Default> AsRef<[T]> for ListProp<T> {
#[inline]
fn as_ref(&self) -> &[T] {
&self.inner
}
}
impl<T: Default> Borrow<[T]> for ListProp<T> {
#[inline]
fn borrow(&self) -> &[T] {
&self.inner
}
}
impl<T: Default + PartialEq + Clone> PropertyUpdate<[T]> for ListProp<T> {
type UpdateContext = bool;
#[inline]
fn compare_and_set_ref(dest: &mut Self, src: &[T], ctx: &mut Self::UpdateContext) {
if &*dest.inner == src {
return;
}
dest.inner = src.iter().cloned().collect();
*ctx = true;
}
}
impl<T: Default> ListPropertyInit for ListProp<T> {
type UpdateContext = bool;
#[inline]
fn init_list(dest: &mut Self, count: usize, _ctx: &mut bool) {
let mut v = Vec::with_capacity(count);
v.resize_with(count, T::default);
dest.inner = v.into_boxed_slice();
}
}
impl<S: ?Sized + PartialEq, T: Default + PropAsRef<S>> ListPropertyUpdate<S> for ListProp<T> {
type ItemValue = ();
#[inline]
fn compare_and_set_item_ref<U: ListPropertyItem<Self, S, Value = ()>>(
dest: &mut Self,
index: usize,
src: &S,
ctx: &mut Self::UpdateContext,
) where
Self: Sized,
{
U::item_value(dest, index, src, ctx);
}
}
impl<S: ?Sized + PartialEq, T: Default + PropAsRef<S>> ListPropertyItem<ListProp<T>, S> for T {
type Value = ();
#[inline]
fn item_value<'a>(
dest: &mut ListProp<T>,
index: usize,
src: &'a S,
ctx: &mut bool,
) -> &'a Self::Value {
if dest.inner[index].property_as_ref() == src {
return &();
}
dest.inner[index] = PropAsRef::property_to_owned(src);
*ctx = true;
&()
}
}