use std::ops::DerefMut as _;
use wasm_bindgen::UnwrapThrowExt;
use crate::core::{AnyElement, SuperElement, ViewElement};
use crate::{AnyNode, DomNode, ViewCtx};
pub struct Pod<N: DomNode> {
pub node: N,
pub flags: PodFlags,
pub props: N::Props,
}
pub type AnyPod = Pod<Box<dyn AnyNode>>;
impl<N: DomNode> Pod<N> {
pub const fn new(node: N, props: N::Props, flags: PodFlags) -> Self {
Self { node, props, flags }
}
pub fn into_any_pod(mut pod: Self) -> AnyPod {
pod.apply_changes();
Pod {
node: Box::new(pod.node),
props: Box::new(pod.props),
flags: pod.flags,
}
}
pub(crate) fn apply_changes(&mut self) {
if self.flags.needs_update() {
self.node.apply_props(&mut self.props, &mut self.flags);
}
self.flags.clear();
}
}
impl AnyPod {
pub(crate) fn as_mut<'a>(
&'a mut self,
parent: impl Into<Option<&'a web_sys::Node>>,
was_removed: bool,
) -> PodMut<'a, Box<dyn AnyNode>> {
PodMut::new(
&mut self.node,
&mut self.props,
&mut self.flags,
parent.into(),
was_removed,
)
}
}
impl<N: DomNode> ViewElement for Pod<N> {
type Mut<'a> = PodMut<'a, N>;
}
impl<N: DomNode> SuperElement<Pod<N>, ViewCtx> for AnyPod {
fn upcast(_ctx: &mut ViewCtx, child: Pod<N>) -> Self {
Pod::into_any_pod(child)
}
fn with_downcast_val<R>(
mut this: Self::Mut<'_>,
f: impl FnOnce(PodMut<'_, N>) -> R,
) -> (Self::Mut<'_>, R) {
let downcast = this.downcast();
let ret = f(downcast);
(this, ret)
}
}
impl<N: DomNode> AnyElement<Pod<N>, ViewCtx> for AnyPod {
fn replace_inner(this: Self::Mut<'_>, mut child: Pod<N>) -> Self::Mut<'_> {
child.apply_changes();
if let Some(parent) = this.parent {
parent
.replace_child(child.node.as_ref(), this.node.as_ref())
.unwrap_throw();
}
*this.node = Box::new(child.node);
*this.props = Box::new(child.props);
*this.flags = child.flags;
this
}
}
pub struct PodMut<'a, N: DomNode> {
pub node: &'a mut N,
pub props: &'a mut N::Props,
pub flags: &'a mut PodFlags,
pub parent: Option<&'a web_sys::Node>,
pub was_removed: bool,
pub is_reborrow: bool,
}
impl<'a, N: DomNode> PodMut<'a, N> {
pub fn new(
node: &'a mut N,
props: &'a mut N::Props,
flags: &'a mut PodFlags,
parent: Option<&'a web_sys::Node>,
was_removed: bool,
) -> Self {
Self {
node,
props,
flags,
parent,
was_removed,
is_reborrow: false,
}
}
pub fn reborrow_mut(&mut self) -> PodMut<'_, N> {
PodMut {
node: self.node,
props: self.props,
flags: self.flags,
parent: self.parent,
was_removed: self.was_removed,
is_reborrow: true,
}
}
pub(crate) fn apply_changes(&mut self) {
if self.flags.needs_update() {
self.node.apply_props(self.props, self.flags);
}
self.flags.clear();
}
}
impl PodMut<'_, Box<dyn AnyNode>> {
fn downcast<N: DomNode>(&mut self) -> PodMut<'_, N> {
PodMut::new(
self.node.deref_mut().as_any_mut().downcast_mut().unwrap(),
self.props.downcast_mut().unwrap(),
self.flags,
self.parent,
false,
)
}
}
impl<N: DomNode> Drop for PodMut<'_, N> {
fn drop(&mut self) {
if self.is_reborrow || self.was_removed {
return;
}
self.apply_changes();
}
}
impl<T, N: AsRef<T> + DomNode> AsRef<T> for Pod<N> {
fn as_ref(&self) -> &T {
<N as AsRef<T>>::as_ref(&self.node)
}
}
impl<T, N: AsRef<T> + DomNode> AsRef<T> for PodMut<'_, N> {
fn as_ref(&self) -> &T {
<N as AsRef<T>>::as_ref(self.node)
}
}
pub struct PodFlags(u8);
impl PodFlags {
const IN_HYDRATION: u8 = 1 << 0;
const WAS_CREATED: u8 = 1 << 1;
const NEEDS_UPDATE: u8 = 1 << 2;
pub(crate) fn new(in_hydration: bool) -> Self {
if in_hydration {
Self(Self::WAS_CREATED | Self::IN_HYDRATION)
} else {
Self(Self::WAS_CREATED)
}
}
pub(crate) fn clear(&mut self) {
self.0 = 0;
}
pub fn was_created(&self) -> bool {
self.0 & Self::WAS_CREATED != 0
}
pub fn in_hydration(&self) -> bool {
self.0 & Self::IN_HYDRATION != 0
}
pub fn needs_update(&self) -> bool {
self.0 & Self::NEEDS_UPDATE != 0
}
pub fn set_needs_update(&mut self) {
self.0 |= Self::NEEDS_UPDATE;
}
}