use std::cell::UnsafeCell;
use std::mem::MaybeUninit;
use std::rc::Rc;
use wasm_bindgen::JsValue;
use web_sys::Node;
use crate::dom::Anchor;
use crate::internal::{In, Out};
use crate::{init, Mountable, View};
mod cell;
mod hook;
mod into_state;
mod product;
mod should_render;
use cell::WithCell;
use product::{Product, ProductHandler};
pub use hook::{Bound, Hook, Signal};
pub use into_state::IntoState;
pub use should_render::{ShouldRender, Then};
#[repr(C)]
struct Inner<S, P: ?Sized = dyn Product<S>> {
state: WithCell<S>,
prod: UnsafeCell<P>,
}
pub struct Stateful<S, F> {
state: S,
render: F,
}
pub struct StatefulProduct<S> {
inner: Rc<Inner<S>>,
}
pub fn stateful<'a, S, F, V>(
state: S,
render: F,
) -> Stateful<S, impl Fn(*const Hook<S::State>) -> V + 'static>
where
S: IntoState,
F: Fn(&'a Hook<S::State>) -> V + 'static,
V: View + 'a,
{
let render = move |hook: *const Hook<S::State>| render(unsafe { &*hook });
Stateful { state, render }
}
impl<S, P> Inner<S, MaybeUninit<P>> {
unsafe fn as_init(&self) -> &Inner<S, P> {
&*(self as *const _ as *const Inner<S, P>)
}
unsafe fn into_init(self: Rc<Self>) -> Rc<Inner<S, P>> {
std::mem::transmute(self)
}
}
impl<S> Inner<S> {
fn update(&self) {
unsafe { (*self.prod.get()).update(Hook::new(self)) }
}
}
impl<S, F, V> View for Stateful<S, F>
where
S: IntoState,
F: Fn(*const Hook<S::State>) -> V + 'static,
V: View,
{
type Product = StatefulProduct<S::State>;
fn build(self, p: In<Self::Product>) -> Out<Self::Product> {
let inner = Rc::new(Inner {
state: WithCell::new(self.state.init()),
prod: UnsafeCell::new(MaybeUninit::uninit()),
});
let view = (self.render)(Hook::new(unsafe { inner.as_init() }));
unsafe {
In::raw((*inner.prod.get()).as_mut_ptr(), |prod| {
ProductHandler::build(
move |hook, product: *mut V::Product| (self.render)(hook).update(&mut *product),
view,
prod,
)
});
}
p.put(StatefulProduct {
inner: unsafe { inner.into_init() },
})
}
fn update(self, p: &mut Self::Product) {
p.inner.state.with(|state| {
if self.state.update(state).should_render() {
p.inner.update();
}
})
}
}
impl<S> Mountable for StatefulProduct<S>
where
S: 'static,
{
type Js = Node;
fn js(&self) -> &JsValue {
unsafe { (*self.inner.prod.get()).js() }
}
fn unmount(&self) {
unsafe { (*self.inner.prod.get()).unmount() }
}
fn replace_with(&self, new: &JsValue) {
unsafe { (*self.inner.prod.get()).replace_with(new) }
}
}
impl<S, R> Stateful<S, R>
where
S: IntoState,
{
pub fn once<F, P>(self, handler: F) -> Once<S, R, F>
where
F: FnOnce(Signal<S::State>) -> P,
{
Once {
with_state: self,
handler,
}
}
}
pub struct Once<S, R, F> {
with_state: Stateful<S, R>,
handler: F,
}
pub struct OnceProduct<S, P> {
product: StatefulProduct<S>,
_no_drop: P,
}
impl<S, P> Anchor for OnceProduct<S, P>
where
StatefulProduct<S>: Mountable,
{
type Js = <StatefulProduct<S> as Mountable>::Js;
type Target = StatefulProduct<S>;
fn anchor(&self) -> &Self::Target {
&self.product
}
}
impl<S, R, F, P> View for Once<S, R, F>
where
S: IntoState,
F: FnOnce(Signal<S::State>) -> P,
P: 'static,
Stateful<S, R>: View<Product = StatefulProduct<S::State>>,
{
type Product = OnceProduct<S::State, P>;
fn build(self, p: In<Self::Product>) -> Out<Self::Product> {
p.in_place(|p| unsafe {
let product = init!(p.product @ self.with_state.build(p));
let signal = Signal {
weak: Rc::downgrade(&product.inner),
};
init!(p._no_drop = (self.handler)(signal));
Out::from_raw(p)
})
}
fn update(self, p: &mut Self::Product) {
self.with_state.update(&mut p.product);
}
}