use std::mem::MaybeUninit;
use std::pin::Pin;
use wasm_bindgen::JsValue;
use web_sys::Node;
use crate::dom::Anchor;
use crate::internal::{empty_node, In, Out};
use crate::{Mountable, View};
macro_rules! branch {
($name:ident < $($var:ident),* >) => {
#[repr(C)]
pub enum $name<$($var),*> {
$(
$var($var),
)*
}
impl<$($var),*> View for $name<$($var),*>
where
$(
$var: View,
)*
{
type Product = $name<$($var::Product),*>;
fn build(self, p: In<Self::Product>) -> Out<Self::Product> {
let p: In<$name<$(MaybeUninit<$var::Product>),*>> = unsafe { p.cast() };
let out = match self {
$(
$name::$var(html) => {
let mut p = p.put($name::$var(MaybeUninit::uninit()));
match &mut *p {
$name::$var(field) => {
In::pinned(unsafe { Pin::new_unchecked(field) }, move |p| html.build(p));
}
_ => unsafe { std::hint::unreachable_unchecked() }
}
p
},
)*
};
unsafe { out.cast() }
}
fn update(self, p: &mut Self::Product) {
match (self, p) {
$(
($name::$var(html), $name::$var(p)) => html.update(p),
)*
(html, p) => {
let old = In::replace(p, move |p| html.build(p));
old.replace_with(p.js());
}
}
}
}
impl<$($var),*> Mountable for $name<$($var),*>
where
$(
$var: Mountable,
)*
{
type Js = Node;
fn js(&self) -> &JsValue {
match self {
$(
$name::$var(p) => p.js(),
)*
}
}
fn replace_with(&self, new: &JsValue) {
match self {
$(
$name::$var(p) => p.replace_with(new),
)*
}
}
fn unmount(&self) {
match self {
$(
$name::$var(p) => p.unmount(),
)*
}
}
}
};
}
branch!(Branch2<A, B>);
branch!(Branch3<A, B, C>);
branch!(Branch4<A, B, C, D>);
branch!(Branch5<A, B, C, D, E>);
branch!(Branch6<A, B, C, D, E, F>);
branch!(Branch7<A, B, C, D, E, F, G>);
branch!(Branch8<A, B, C, D, E, F, G, H>);
branch!(Branch9<A, B, C, D, E, F, G, H, I>);
pub struct EmptyNode(Node);
pub struct Empty;
impl Anchor for EmptyNode {
type Js = Node;
type Target = Node;
fn anchor(&self) -> &Node {
&self.0
}
}
impl View for Empty {
type Product = EmptyNode;
fn build(self, p: In<EmptyNode>) -> Out<EmptyNode> {
p.put(EmptyNode(empty_node()))
}
fn update(self, _: &mut EmptyNode) {}
}
impl<T: View> View for Option<T> {
type Product = Branch2<T::Product, EmptyNode>;
fn build(self, p: In<Self::Product>) -> Out<Self::Product> {
let p: In<Branch2<MaybeUninit<T::Product>, MaybeUninit<EmptyNode>>> = unsafe { p.cast() };
let out = match self {
Some(html) => {
let mut p = p.put(Branch2::A(MaybeUninit::uninit()));
match &mut *p {
Branch2::A(field) => {
In::pinned(unsafe { Pin::new_unchecked(field) }, move |p| html.build(p));
}
Branch2::B(_) => unsafe { std::hint::unreachable_unchecked() },
}
p
}
None => p.put(Branch2::B(MaybeUninit::new(EmptyNode(empty_node())))),
};
unsafe { out.cast() }
}
fn update(self, p: &mut Self::Product) {
match (self, p) {
(Some(html), Branch2::A(p)) => html.update(p),
(None, Branch2::B(_)) => (),
(html, p) => {
let old = In::replace(p, move |p| html.build(p));
old.replace_with(p.js());
}
}
}
}