1use std::mem::MaybeUninit;
8use std::ops::{Deref, DerefMut};
9use std::pin::Pin;
10
11use wasm_bindgen::prelude::*;
12use web_sys::Node;
13
14use crate::View;
15
16#[must_use]
20#[repr(transparent)]
21pub struct In<'a, T>(&'a mut MaybeUninit<T>);
22
23#[repr(transparent)]
27pub struct Out<'a, T>(&'a mut T);
28
29impl<'a, T> Out<'a, T> {
30 pub unsafe fn from_raw(raw: *mut T) -> Self {
39 Out(&mut *raw)
40 }
41
42 pub unsafe fn cast<U>(self) -> Out<'a, U> {
51 Out(&mut *(self.0 as *mut T as *mut U))
52 }
53}
54
55impl<T> Deref for Out<'_, T> {
56 type Target = T;
57
58 fn deref(&self) -> &T {
59 self.0
60 }
61}
62
63impl<T> DerefMut for Out<'_, T> {
64 fn deref_mut(&mut self) -> &mut T {
65 self.0
66 }
67}
68
69impl<'a, T> In<'a, T> {
70 pub unsafe fn cast<U>(self) -> In<'a, U> {
79 In(&mut *(self.0 as *mut MaybeUninit<T> as *mut MaybeUninit<U>))
80 }
81
82 pub fn in_place<F>(self, f: F) -> Out<'a, T>
115 where
116 F: FnOnce(*mut T) -> Out<'a, T>,
117 {
118 f(self.0.as_mut_ptr())
119 }
120
121 pub unsafe fn raw<F>(raw: *mut T, f: F) -> Out<'a, T>
129 where
130 F: FnOnce(In<T>) -> Out<T>,
131 {
132 f(In(&mut *(raw as *mut MaybeUninit<T>)))
133 }
134
135 pub fn pinned<F>(pin: Pin<&'a mut MaybeUninit<T>>, f: F) -> Out<'a, T>
141 where
142 F: FnOnce(In<T>) -> Out<T>,
143 {
144 f(In(unsafe { pin.get_unchecked_mut() }))
145 }
146
147 pub fn replace<F>(at: &mut T, f: F) -> T
150 where
151 F: FnOnce(In<T>) -> Out<T>,
152 {
153 let at = unsafe { &mut *(at as *mut T as *mut MaybeUninit<T>) };
154 let old = unsafe { at.assume_init_read() };
155 let Out(_) = f(In(at));
156
157 old
158 }
159
160 pub fn put(self, val: T) -> Out<'a, T> {
162 Out(self.0.write(val))
163 }
164}
165
166#[macro_export]
168macro_rules! init {
169 ($p:ident.$field:ident @ $then:expr) => {
170 $crate::internal::In::raw(std::ptr::addr_of_mut!((*$p).$field), move |$p| $then)
171 };
172 ($p:ident.$field:ident = $val:expr) => {
173 $crate::internal::In::raw(std::ptr::addr_of_mut!((*$p).$field), |$p| $p.put($val))
174 };
175}
176
177#[repr(transparent)]
179pub struct Precompiled<F>(pub F);
180
181#[inline]
184pub const fn fn_type_hint<T, F: FnMut(T)>(f: F) -> F {
185 f
186}
187
188impl<F> View for Precompiled<F>
189where
190 F: Fn() -> Node,
191{
192 type Product = Node;
193
194 fn build(self, p: In<Node>) -> Out<Node> {
195 p.put(self.0())
196 }
197
198 fn update(self, _: &mut Node) {}
199}
200
201#[wasm_bindgen]
202extern "C" {
203 #[wasm_bindgen(js_namespace = ["document", "body"], js_name = appendChild)]
204 pub(crate) fn append_body(node: &JsValue);
205 #[wasm_bindgen(js_namespace = document, js_name = createTextNode)]
206 pub(crate) fn text_node(t: &str) -> Node;
207 #[wasm_bindgen(js_namespace = document, js_name = createTextNode)]
208 pub(crate) fn text_node_num(t: f64) -> Node;
209 #[wasm_bindgen(js_namespace = document, js_name = createTextNode)]
210 pub(crate) fn text_node_bool(t: bool) -> Node;
211}
212
213mod hidden {
214 use super::wasm_bindgen;
215
216 #[wasm_bindgen(js_name = "koboldCallback")]
217 pub fn kobold_callback(event: web_sys::Event, closure: *mut (), vcall: usize) {
218 let vcall: fn(web_sys::Event, *mut ()) = unsafe { std::mem::transmute(vcall) };
219
220 vcall(event, closure);
221 }
222}
223
224#[wasm_bindgen(module = "/js/util.js")]
225extern "C" {
226 #[wasm_bindgen(js_name = "appendChild")]
227 pub(crate) fn append_child(parent: &Node, child: &JsValue);
228 #[wasm_bindgen(js_name = "appendBefore")]
229 pub(crate) fn append_before(node: &Node, insert: &JsValue);
230 #[wasm_bindgen(js_name = "removeNode")]
231 pub(crate) fn unmount(node: &JsValue);
232 #[wasm_bindgen(js_name = "replaceNode")]
233 pub(crate) fn replace(old: &JsValue, new: &JsValue);
234
235 #[wasm_bindgen(js_name = "emptyNode")]
236 pub(crate) fn empty_node() -> Node;
237 #[wasm_bindgen(js_name = "fragment")]
238 pub(crate) fn fragment() -> Node;
239 #[wasm_bindgen(js_name = "fragmentDecorate")]
240 pub(crate) fn fragment_decorate(f: &Node) -> Node;
241 #[wasm_bindgen(js_name = "fragmentUnmount")]
242 pub(crate) fn fragment_unmount(f: &Node);
243 #[wasm_bindgen(js_name = "fragmentReplace")]
244 pub(crate) fn fragment_replace(f: &Node, new: &JsValue);
245
246 #[wasm_bindgen(js_name = "setTextContent")]
249 pub(crate) fn set_text(el: &Node, t: &str);
250 #[wasm_bindgen(js_name = "setTextContent")]
251 pub(crate) fn set_text_num(el: &Node, t: f64);
252 #[wasm_bindgen(js_name = "setTextContent")]
253 pub(crate) fn set_text_bool(el: &Node, t: bool);
254
255 #[wasm_bindgen(js_name = "setAttribute")]
258 pub(crate) fn set_attr(el: &JsValue, a: &str, v: &str);
259 #[wasm_bindgen(js_name = "setAttribute")]
260 pub(crate) fn set_attr_num(el: &JsValue, a: &str, v: f64);
261 #[wasm_bindgen(js_name = "setAttribute")]
262 pub(crate) fn set_attr_bool(el: &JsValue, a: &str, v: bool);
263
264 #[wasm_bindgen(js_name = "setChecked")]
267 pub(crate) fn checked(node: &Node, value: bool);
268 #[wasm_bindgen(js_name = "setClassName")]
269 pub(crate) fn class_name(node: &Node, value: &str);
270 #[wasm_bindgen(js_name = "setHref")]
271 pub(crate) fn href(node: &Node, value: &str);
272 #[wasm_bindgen(js_name = "setStyle")]
273 pub(crate) fn style(node: &Node, value: &str);
274 #[wasm_bindgen(js_name = "setValue")]
275 pub(crate) fn value(node: &Node, value: &str);
276 #[wasm_bindgen(js_name = "setValue")]
277 pub(crate) fn value_num(node: &Node, value: f64);
278
279 #[wasm_bindgen(js_name = "addClass")]
282 pub(crate) fn add_class(node: &Node, value: &str);
283 #[wasm_bindgen(js_name = "removeClass")]
284 pub(crate) fn remove_class(node: &Node, value: &str);
285 #[wasm_bindgen(js_name = "replaceClass")]
286 pub(crate) fn replace_class(node: &Node, old: &str, value: &str);
287 #[wasm_bindgen(js_name = "toggleClass")]
288 pub(crate) fn toggle_class(node: &Node, class: &str, value: bool);
289
290 #[wasm_bindgen(js_name = "makeEventHandler")]
293 pub(crate) fn make_event_handler(closure: *mut (), vcall: usize) -> JsValue;
294
295 #[wasm_bindgen(js_name = "checkEventHandler")]
296 pub(crate) fn check_event_handler();
297}
298
299#[cfg(test)]
300mod test {
301 use super::*;
302
303 use std::pin::pin;
304
305 #[test]
306 fn pinned() {
307 let data = pin!(MaybeUninit::uninit());
308 let data = In::pinned(data, |p| p.put(42));
309
310 assert_eq!(*data, 42);
311 }
312
313 #[test]
317 fn build_in_place() {
318 fn meaning_builder(p: In<u32>) -> Out<u32> {
319 p.put(42)
320 }
321
322 struct Foo {
323 int: u32,
324 float: f64,
325 }
326
327 let foo = pin!(MaybeUninit::<Foo>::uninit());
328 let foo = In::pinned(foo, |p| {
329 p.in_place(|p| unsafe {
330 init!(p.int @ meaning_builder(p));
331 init!(p.float = 3.14);
332
333 Out::from_raw(p)
334 })
335 });
336
337 assert_eq!(foo.int, 42);
338 assert_eq!(foo.float, 3.14);
339 }
340}