use std::mem::MaybeUninit;
use std::ops::{Deref, DerefMut};
use std::pin::Pin;
use wasm_bindgen::prelude::*;
use web_sys::Node;
use crate::View;
#[must_use]
#[repr(transparent)]
pub struct In<'a, T>(&'a mut MaybeUninit<T>);
#[repr(transparent)]
pub struct Out<'a, T>(&'a mut T);
impl<'a, T> Out<'a, T> {
pub unsafe fn from_raw(raw: *mut T) -> Self {
Out(&mut *raw)
}
pub unsafe fn cast<U>(self) -> Out<'a, U> {
Out(&mut *(self.0 as *mut T as *mut U))
}
}
impl<T> Deref for Out<'_, T> {
type Target = T;
fn deref(&self) -> &T {
self.0
}
}
impl<T> DerefMut for Out<'_, T> {
fn deref_mut(&mut self) -> &mut T {
self.0
}
}
impl<'a, T> In<'a, T> {
pub unsafe fn cast<U>(self) -> In<'a, U> {
In(&mut *(self.0 as *mut MaybeUninit<T> as *mut MaybeUninit<U>))
}
pub fn in_place<F>(self, f: F) -> Out<'a, T>
where
F: FnOnce(*mut T) -> Out<'a, T>,
{
f(self.0.as_mut_ptr())
}
pub unsafe fn raw<F>(raw: *mut T, f: F) -> Out<'a, T>
where
F: FnOnce(In<T>) -> Out<T>,
{
f(In(&mut *(raw as *mut MaybeUninit<T>)))
}
pub fn pinned<F>(pin: Pin<&'a mut MaybeUninit<T>>, f: F) -> Out<'a, T>
where
F: FnOnce(In<T>) -> Out<T>,
{
f(In(unsafe { pin.get_unchecked_mut() }))
}
pub fn replace<F>(at: &mut T, f: F) -> T
where
F: FnOnce(In<T>) -> Out<T>,
{
let at = unsafe { &mut *(at as *mut T as *mut MaybeUninit<T>) };
let old = unsafe { at.assume_init_read() };
let Out(_) = f(In(at));
old
}
pub fn put(self, val: T) -> Out<'a, T> {
Out(self.0.write(val))
}
}
#[macro_export]
macro_rules! init {
($p:ident.$field:ident @ $then:expr) => {
$crate::internal::In::raw(std::ptr::addr_of_mut!((*$p).$field), move |$p| $then)
};
($p:ident.$field:ident = $val:expr) => {
$crate::internal::In::raw(std::ptr::addr_of_mut!((*$p).$field), |$p| $p.put($val))
};
}
#[repr(transparent)]
pub struct Precompiled<F>(pub F);
#[inline]
pub const fn fn_type_hint<T, F: FnMut(T)>(f: F) -> F {
f
}
impl<F> View for Precompiled<F>
where
F: Fn() -> Node,
{
type Product = Node;
fn build(self, p: In<Node>) -> Out<Node> {
p.put(self.0())
}
fn update(self, _: &mut Node) {}
}
#[wasm_bindgen]
extern "C" {
#[wasm_bindgen(js_namespace = ["document", "body"], js_name = appendChild)]
pub(crate) fn append_body(node: &JsValue);
#[wasm_bindgen(js_namespace = document, js_name = createTextNode)]
pub(crate) fn text_node(t: &str) -> Node;
#[wasm_bindgen(js_namespace = document, js_name = createTextNode)]
pub(crate) fn text_node_num(t: f64) -> Node;
#[wasm_bindgen(js_namespace = document, js_name = createTextNode)]
pub(crate) fn text_node_bool(t: bool) -> Node;
}
mod hidden {
use super::wasm_bindgen;
#[wasm_bindgen(js_name = "koboldCallback")]
pub fn kobold_callback(event: web_sys::Event, closure: *mut (), vcall: usize) {
let vcall: fn(web_sys::Event, *mut ()) = unsafe { std::mem::transmute(vcall) };
vcall(event, closure);
}
}
#[wasm_bindgen(module = "/js/util.js")]
extern "C" {
#[wasm_bindgen(js_name = "appendChild")]
pub(crate) fn append_child(parent: &Node, child: &JsValue);
#[wasm_bindgen(js_name = "appendBefore")]
pub(crate) fn append_before(node: &Node, insert: &JsValue);
#[wasm_bindgen(js_name = "removeNode")]
pub(crate) fn unmount(node: &JsValue);
#[wasm_bindgen(js_name = "replaceNode")]
pub(crate) fn replace(old: &JsValue, new: &JsValue);
#[wasm_bindgen(js_name = "emptyNode")]
pub(crate) fn empty_node() -> Node;
#[wasm_bindgen(js_name = "fragment")]
pub(crate) fn fragment() -> Node;
#[wasm_bindgen(js_name = "fragmentDecorate")]
pub(crate) fn fragment_decorate(f: &Node) -> Node;
#[wasm_bindgen(js_name = "fragmentUnmount")]
pub(crate) fn fragment_unmount(f: &Node);
#[wasm_bindgen(js_name = "fragmentReplace")]
pub(crate) fn fragment_replace(f: &Node, new: &JsValue);
#[wasm_bindgen(js_name = "setTextContent")]
pub(crate) fn set_text(el: &Node, t: &str);
#[wasm_bindgen(js_name = "setTextContent")]
pub(crate) fn set_text_num(el: &Node, t: f64);
#[wasm_bindgen(js_name = "setTextContent")]
pub(crate) fn set_text_bool(el: &Node, t: bool);
#[wasm_bindgen(js_name = "setAttribute")]
pub(crate) fn set_attr(el: &JsValue, a: &str, v: &str);
#[wasm_bindgen(js_name = "setAttribute")]
pub(crate) fn set_attr_num(el: &JsValue, a: &str, v: f64);
#[wasm_bindgen(js_name = "setAttribute")]
pub(crate) fn set_attr_bool(el: &JsValue, a: &str, v: bool);
#[wasm_bindgen(js_name = "setChecked")]
pub(crate) fn checked(node: &Node, value: bool);
#[wasm_bindgen(js_name = "setClassName")]
pub(crate) fn class_name(node: &Node, value: &str);
#[wasm_bindgen(js_name = "setHref")]
pub(crate) fn href(node: &Node, value: &str);
#[wasm_bindgen(js_name = "setStyle")]
pub(crate) fn style(node: &Node, value: &str);
#[wasm_bindgen(js_name = "setValue")]
pub(crate) fn value(node: &Node, value: &str);
#[wasm_bindgen(js_name = "setValue")]
pub(crate) fn value_num(node: &Node, value: f64);
#[wasm_bindgen(js_name = "addClass")]
pub(crate) fn add_class(node: &Node, value: &str);
#[wasm_bindgen(js_name = "removeClass")]
pub(crate) fn remove_class(node: &Node, value: &str);
#[wasm_bindgen(js_name = "replaceClass")]
pub(crate) fn replace_class(node: &Node, old: &str, value: &str);
#[wasm_bindgen(js_name = "toggleClass")]
pub(crate) fn toggle_class(node: &Node, class: &str, value: bool);
#[wasm_bindgen(js_name = "makeEventHandler")]
pub(crate) fn make_event_handler(closure: *mut (), vcall: usize) -> JsValue;
#[wasm_bindgen(js_name = "checkEventHandler")]
pub(crate) fn check_event_handler();
}
#[cfg(test)]
mod test {
use super::*;
use std::pin::pin;
#[test]
fn pinned() {
let data = pin!(MaybeUninit::uninit());
let data = In::pinned(data, |p| p.put(42));
assert_eq!(*data, 42);
}
#[test]
fn build_in_place() {
fn meaning_builder(p: In<u32>) -> Out<u32> {
p.put(42)
}
struct Foo {
int: u32,
float: f64,
}
let foo = pin!(MaybeUninit::<Foo>::uninit());
let foo = In::pinned(foo, |p| {
p.in_place(|p| unsafe {
init!(p.int @ meaning_builder(p));
init!(p.float = 3.14);
Out::from_raw(p)
})
});
assert_eq!(foo.int, 42);
assert_eq!(foo.float, 3.14);
}
}