use std::ops::Deref;
use web_sys::Node;
use crate::attribute::Attribute;
use crate::dom::{Anchor, TextContent};
use crate::internal::{In, Out};
use crate::value::{IntoText, Value};
use crate::{init, Mountable, View};
mod vstring;
pub use vstring::VString;
pub const fn fence<D, V, F>(guard: D, render: F) -> Fence<D, F>
where
D: Diff,
V: View,
F: FnOnce() -> V,
{
Fence {
guard,
inner: render,
}
}
pub struct Fence<D, F> {
guard: D,
inner: F,
}
impl<D, F, V> View for Fence<D, F>
where
D: Diff,
F: FnOnce() -> V,
V: View,
{
type Product = Fence<D::Memo, V::Product>;
fn build(self, p: In<Self::Product>) -> Out<Self::Product> {
p.in_place(|p| unsafe {
init!(p.guard = self.guard.into_memo());
init!(p.inner @ (self.inner)().build(p));
Out::from_raw(p)
})
}
fn update(self, p: &mut Self::Product) {
if self.guard.diff(&mut p.guard) {
(self.inner)().update(&mut p.inner);
}
}
}
impl<D, P> Anchor for Fence<D, P>
where
P: Mountable,
{
type Js = P::Js;
type Target = P;
fn anchor(&self) -> &P {
&self.inner
}
}
pub trait Diff: Copy {
type Memo: 'static;
fn into_memo(self) -> Self::Memo;
fn diff(self, memo: &mut Self::Memo) -> bool;
}
macro_rules! impl_diff_str {
($($ty:ty),*) => {
$(
impl Diff for $ty {
type Memo = String;
fn into_memo(self) -> String {
self.into()
}
fn diff(self, memo: &mut String) -> bool {
if self != memo {
self.clone_into(memo);
true
} else {
false
}
}
}
)*
};
}
macro_rules! impl_diff {
($($ty:ty),*) => {
$(
impl Diff for $ty {
type Memo = $ty;
fn into_memo(self) -> $ty {
self
}
fn diff(self, memo: &mut $ty) -> bool {
if self != *memo {
*memo = self;
true
} else {
false
}
}
}
)*
};
}
impl_diff_str!(&str, &String);
impl_diff!(bool, u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize, f32, f64);
#[repr(transparent)]
pub struct Ref<T: ?Sized>(T);
impl<T: ?Sized> Deref for Ref<T> {
type Target = T;
fn deref(&self) -> &T {
&self.0
}
}
impl<T: ?Sized> AsRef<T> for Ref<T> {
fn as_ref(&self) -> &T {
&self.0
}
}
impl<T: ?Sized> Diff for &Ref<T> {
type Memo = *const ();
fn into_memo(self) -> Self::Memo {
&self.0 as *const _ as *const ()
}
fn diff(self, memo: &mut Self::Memo) -> bool {
let ptr = &self.0 as *const _ as *const ();
if ptr != *memo {
*memo = ptr;
true
} else {
false
}
}
}
#[derive(Clone, Copy)]
#[repr(transparent)]
pub struct Eager<T>(pub(crate) T);
#[derive(Clone, Copy)]
#[repr(transparent)]
pub struct Static<T>(pub(crate) T);
macro_rules! impl_no_diff {
($name:ident, $update:expr) => {
impl<T> Deref for $name<T> {
type Target = T;
fn deref(&self) -> &T {
&self.0
}
}
impl<T> View for $name<T>
where
T: Value<TextContent> + IntoText + Copy,
{
type Product = Node;
fn build(self, p: In<Node>) -> Out<Node> {
p.put(self.into_text())
}
fn update(self, node: &mut Node) {
if $update {
self.0.set_prop(TextContent, node);
}
}
}
impl<T, P> Attribute<P> for $name<T>
where
T: Value<P>,
{
type Product = ();
fn build(self) {}
fn build_in(self, prop: P, node: &Node) {
self.0.set_prop(prop, node);
}
fn update_in(self, prop: P, node: &Node, _: &mut ()) {
if $update {
self.0.set_prop(prop, node);
}
}
}
impl<T> Diff for $name<T>
where
T: Copy,
{
type Memo = ();
fn into_memo(self) {}
fn diff(self, _: &mut ()) -> bool {
$update
}
}
impl AsRef<str> for $name<&str> {
fn as_ref(&self) -> &str {
self.0
}
}
};
}
impl_no_diff!(Eager, true);
impl_no_diff!(Static, false);