#![cfg(target_arch = "wasm32")]
use std::{
cell::{Cell, UnsafeCell},
default::Default,
rc::Rc,
};
pub use serde_json::from_str;
pub use wasm_bindgen::JsCast;
pub use web_sys as web;
pub use yarte_derive::App;
pub use yarte_helpers::{helpers::big_num_32::*, recompile};
mod queue;
use self::queue::Queue;
pub trait App: Default + Sized + Unpin + 'static {
type BlackBox;
type Message: 'static;
#[doc(hidden)]
fn __render(&mut self, _addr: &Addr<Self>) {}
#[doc(hidden)]
fn __hydrate(&mut self, _addr: &Addr<Self>) {}
#[doc(hidden)]
fn __dispatch(&mut self, _msg: Self::Message, _addr: &Addr<Self>) {}
#[doc(hidden)]
fn __start(self) -> Addr<Self>
where
Self: App,
{
Addr(Rc::new(Context::new(self)))
}
fn start_default() -> Addr<Self>
where
Self: App,
{
Self::default().__start()
}
}
pub struct Addr<A: App>(Rc<Context<A>>);
impl<A: App> Addr<A> {
#[inline]
fn push(&self, msg: A::Message) {
self.0.q.push(msg);
self.update();
}
pub fn send(&self, msg: A::Message) {
self.push(msg);
}
#[inline]
fn update(&self) {
if self.0.ready.get() {
self.0.ready.replace(false);
while let Some(msg) = self.0.q.pop() {
unsafe {
self.0.app.get().as_mut().unwrap().__dispatch(msg, &self);
}
while let Some(msg) = self.0.q.pop() {
unsafe {
self.0.app.get().as_mut().unwrap().__dispatch(msg, &self);
}
}
unsafe {
self.0.app.get().as_mut().unwrap().__render(&self);
}
}
self.0.ready.replace(true);
}
}
#[inline]
pub unsafe fn hydrate(&self) {
debug_assert!(!self.0.ready.get());
self.0.app.get().as_mut().unwrap().__hydrate(&self);
self.0.ready.replace(true);
self.update();
}
}
impl<A: App> Clone for Addr<A> {
fn clone(&self) -> Self {
Addr(Rc::clone(&self.0))
}
}
pub struct Context<A: App> {
app: UnsafeCell<A>,
q: Queue<A::Message>,
ready: Cell<bool>,
}
impl<A: App> Context<A> {
fn new(app: A) -> Self {
Self {
app: UnsafeCell::new(app),
q: Queue::new(),
ready: Cell::new(false),
}
}
}
#[cfg(test)]
mod test {
use super::*;
use std::default::Default;
use wasm_bindgen_futures::spawn_local;
use wasm_bindgen_test::*;
#[derive(Default)]
struct Test {
c: Rc<Cell<usize>>,
any: usize,
it: Vec<usize>,
black_box: <Self as App>::BlackBox,
}
impl App for Test {
type BlackBox = BlackBox;
type Message = Msg;
fn __dispatch(&mut self, m: Self::Message, addr: &Addr<Self>) {
match m {
Msg::Msg(i) => msg(self, i, addr),
Msg::Reset => reset(self, addr),
Msg::Tree(i) => msg_tree(self, i, addr),
Msg::Fut(i) => msg_fut(self, i, addr),
}
}
}
#[derive(Debug, PartialEq)]
struct BlackBox {
t_root: u8,
t_children_0: Vec<bool>,
}
impl Default for BlackBox {
fn default() -> BlackBox {
BlackBox {
t_root: 0xFF,
t_children_0: vec![],
}
}
}
impl BlackBox {
fn set_zero(&mut self) {
self.t_root = 0;
for child in self.t_children_0.iter_mut() {
*child = false;
}
}
}
#[macro_export]
macro_rules! set_any {
($app:ident, $value:expr) => {
$app.black_box.t_root |= 1 << 1;
$app.any = $value;
};
}
#[macro_export]
macro_rules! set_it {
($app:ident, $value:expr) => {
let value = $value;
$app.black_box.t_root |= 1 << 2;
$app.black_box.t_children_0 = vec![true; value.len()];
$app.it = value;
};
}
#[macro_export]
macro_rules! push_it {
($app:ident, $value:expr) => {
$app.black_box.t_root |= 1 << 2;
$app.black_box.t_children_0.push(true);
$app.it.push($value);
};
}
#[macro_export]
macro_rules! pop_it {
($app:ident) => {{
$app.black_box.t_root |= 1 << 2;
$app.black_box.t_children_0.pop();
$app.it.pop()
}};
}
#[macro_export]
macro_rules! set_it_index {
($app:ident [$i:expr] $value:expr) => {
$app.black_box.t_root |= 1 << 2;
$app.black_box
.t_children_0
.get_mut($i)
.map(|x| *x = true)
.and_then(|_| $app.it.get_mut($i).map(|x| *x = $value))
};
}
enum Msg {
Msg(usize),
Reset,
Tree(usize),
Fut(usize),
}
#[inline]
fn msg_tree(app: &mut Test, msg: usize, _addr: &Addr<Test>) {
app.black_box.set_zero();
let expected = BlackBox {
t_root: 0,
t_children_0: vec![],
};
assert_eq!(app.black_box, expected);
set_any!(app, msg);
set_it!(app, vec![1, 2, 3, 4]);
let expected = BlackBox {
t_root: 6,
t_children_0: vec![true, true, true, true],
};
assert_eq!(app.black_box, expected);
app.black_box.set_zero();
push_it!(app, 5);
let expected = BlackBox {
t_root: 4,
t_children_0: vec![false, false, false, false, true],
};
assert_eq!(app.black_box, expected);
app.black_box.set_zero();
let _ = pop_it!(app);
let expected = BlackBox {
t_root: 4,
t_children_0: vec![false, false, false, false],
};
assert_eq!(app.black_box, expected);
app.black_box.set_zero();
let expected = BlackBox {
t_root: 0,
t_children_0: vec![false, false, false, false],
};
assert_eq!(app.black_box, expected);
set_it_index!(app [1] 6);
let expected = BlackBox {
t_root: 4,
t_children_0: vec![false, true, false, false],
};
assert_eq!(app.black_box, expected)
}
#[inline]
fn msg(app: &mut Test, msg: usize, _addr: &Addr<Test>) {
app.c.replace(msg);
}
#[inline]
fn reset(_app: &mut Test, addr: &Addr<Test>) {
addr.send(Msg::Msg(0));
}
#[inline]
fn msg_fut(_app: &mut Test, msg: usize, addr: &Addr<Test>) {
addr.send(Msg::Reset);
let mb = addr.clone();
let work = unsafe {
async_timer::Timed::platform_new_unchecked(
async move { mb.send(Msg::Msg(msg)) },
core::time::Duration::from_secs(1),
)
};
spawn_local(async move {
work.await.unwrap();
});
}
#[wasm_bindgen_test]
fn test() {
let c = Rc::new(Cell::new(0));
let c2 = Rc::clone(&c);
let app = Test {
c,
..Default::default()
};
let addr = app.__start();
unsafe {
addr.hydrate();
}
let addr2 = addr.clone();
addr.send(Msg::Msg(2));
assert_eq!(c2.get(), 2);
addr2.send(Msg::Msg(3));
addr.send(Msg::Msg(1));
assert_eq!(c2.get(), 1);
addr.send(Msg::Msg(1));
addr2.send(Msg::Msg(3));
assert_eq!(c2.get(), 3);
addr2.send(Msg::Reset);
assert_eq!(c2.get(), 0);
addr2.send(Msg::Msg(3));
assert_eq!(c2.get(), 3);
addr2.send(Msg::Fut(7));
assert_eq!(c2.get(), 0);
let c3 = Rc::clone(&c2);
let work = unsafe {
async_timer::Timed::platform_new_unchecked(async {}, core::time::Duration::from_secs(1))
};
spawn_local(async move {
work.await.unwrap();
assert_eq!(c3.get(), 7);
addr2.send(Msg::Reset);
assert_eq!(c3.get(), 0);
});
addr.send(Msg::Reset);
assert_eq!(c2.get(), 0);
addr.send(Msg::Tree(0))
}
}