From c0e562a3aff3276a35fdc8921dbae98fa91bc184 Mon Sep 17 00:00:00 2001 From: Adrien Burgun Date: Sun, 23 Oct 2022 17:07:12 +0200 Subject: [PATCH] Backup commit --- stackline-wasm/Cargo.toml | 2 + stackline-wasm/src/lib-old.rs | 477 +++++++++++++++++++++++++++++++ stackline-wasm/src/lib.rs | 518 +++++++--------------------------- stackline/src/world.rs | 16 ++ 4 files changed, 601 insertions(+), 412 deletions(-) create mode 100644 stackline-wasm/src/lib-old.rs diff --git a/stackline-wasm/Cargo.toml b/stackline-wasm/Cargo.toml index a105efa..ad43068 100644 --- a/stackline-wasm/Cargo.toml +++ b/stackline-wasm/Cargo.toml @@ -16,6 +16,7 @@ serde_json = "1.0" serde = { version = "1", features = ["derive"] } stackline = { path = "../stackline", features = [], default-features = false } +veccell = { version = "0.4.0", features = ["serde"] } # The `console_error_panic_hook` crate provides better debugging of panics by # logging them with `console.error`. This is great for development, but requires @@ -32,6 +33,7 @@ wee_alloc = { version = "0.4.5", optional = true } web-sys = { version = "0.3", features = ["console"] } js-sys = "0.3" +serde-wasm-bindgen = "0.4.3" [dev-dependencies] wasm-bindgen-test = "0.3.13" diff --git a/stackline-wasm/src/lib-old.rs b/stackline-wasm/src/lib-old.rs new file mode 100644 index 0000000..3d9c365 --- /dev/null +++ b/stackline-wasm/src/lib-old.rs @@ -0,0 +1,477 @@ +mod utils; + +use js_sys::Function; +use serde::{Deserialize, Serialize}; +use wasm_bindgen::prelude::*; + +use stackline::pane::Pane as SLPane; +use stackline::signal::Signal as SLSignal; +use stackline::signal::Value; +use stackline::tile::AnyTile; +use stackline::tile::FullTile as SLFullTile; +use stackline::utils::Direction; +use stackline::world::World as SLWorld; +use stackline::text::TextSurface; + +// TODO: refactor: +// - make everything camelCase +// - clearly separate serialized properties/functions from non-serialized +// - add javascript code to the classes? + +// When the `wee_alloc` feature is enabled, use `wee_alloc` as the global +// allocator. +#[cfg(feature = "wee_alloc")] +#[global_allocator] +static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT; + +#[wasm_bindgen] +pub struct World(SLWorld); + +#[wasm_bindgen(start)] +pub fn set_panic() { + utils::set_panic_hook(); +} + +#[wasm_bindgen] +pub fn available_tiles() -> Vec { + AnyTile::available() + .iter() + .map(|name| JsValue::from_str(name)) + .collect() +} + +#[wasm_bindgen] +impl World { + /// Creates a new World instance + #[wasm_bindgen(constructor)] + pub fn new() -> Self { + Self(SLWorld::new()) + } + + pub fn deserialize(serialized: &JsValue) -> Option { + serialized.into_serde().map_err(|err| { + err!("Error while deserializing World: {:?}", err); + }).ok().map(|world| World(world)) + } + + pub fn serialize(&self) -> JsValue { + JsValue::from_serde(&self.0).map_err(|err| { + err!("Error while serializing World: {:?}", err); + }).unwrap_or(JsValue::NULL) + } + + /// Initializes the World, making it ready to run + pub fn init(&mut self) { + self.0.init(); + } + + #[inline] + pub fn step(&mut self) { + self.0.step(); + } + + pub fn run(&mut self, steps: usize) { + for _ in 0..steps { + self.step(); + } + } + + pub fn set_blink_duration(&mut self, blink_duration: f64) { + use std::time::Duration; + self.0 + .set_blink_duration(Duration::from_secs_f64(blink_duration)); + } + + #[allow(non_snake_case)] + pub fn toString(&self) -> String { + format!("{:#}", self.0) + } + + /// NOTE: We have to [`Clone`] the FullTile + pub fn get(&self, x: i32, y: i32) -> Option { + self.0.get((x, y)).map(|tile| FullTile((*tile).clone())) + } + + pub fn set(&mut self, x: i32, y: i32, tile: FullTile) { + if let Some(tile_ref) = self.0.get_mut((x, y)) { + *tile_ref = tile.0; + } else if cfg!(debug_assertions) { + err!("Index out of bound: {}:{}", x, y); + } + } + + pub fn get_pane(&self, name: String) -> Option { + self.0.get_pane(&name).map(|pane| Pane(pane.clone())) + } + + pub fn set_pane(&mut self, name: String, pane: Pane) { + self.0.set_pane(name, pane.0); + } + + pub fn panes(&self) -> Vec { + self.0.panes().keys().map(|key| { + JsValue::from_str(key) + }).collect() + } + + pub fn draw(&self, x: i32, y: i32, width: usize, height: usize) -> Vec { + let mut surface = TextSurface::new(width, height); + + self.0.draw(x, y, &mut surface); + + #[derive(Serialize)] + struct Char { + ch: char, + fg: (u8, u8, u8), + bg: Option<(u8, u8, u8)>, + } + + fn to_u32(red: u8, green: u8, blue: u8) -> u32 { + 0xff000000 | (red as u32).checked_shl(16).unwrap() | (green as u32).checked_shl(8).unwrap() | (blue as u32) + } + + surface.iter().map(|ch| { + vec![ + ch.ch as u32, + to_u32(ch.fg.red, ch.fg.green, ch.fg.blue), + ch.bg.map(|bg| to_u32(bg.red, bg.green, bg.blue)).unwrap_or(0) + ].into_iter() + }).flatten().collect::>() + } +} + +#[wasm_bindgen] +pub struct Pane(SLPane); + +#[wasm_bindgen] +impl Pane { + #[wasm_bindgen(constructor)] + pub fn empty(width: usize, height: usize) -> Option { + SLPane::empty(width, height).map(|pane| Self(pane)) + } + + #[allow(non_snake_case)] + pub fn toString(&self) -> String { + format!("{:#?}", self.0) + } + + #[wasm_bindgen(getter)] + pub fn width(&self) -> usize { + self.0.width().get() + } + + #[wasm_bindgen(setter)] + pub fn set_width(&mut self, width: usize) { + let height = self.0.height().get(); + + self.0.resize(width, height).unwrap_or_else(|| { + err!("Error while resizing Pane"); + }); + } + + #[wasm_bindgen(getter)] + pub fn height(&self) -> usize { + self.0.height().get() + } + + #[wasm_bindgen(setter)] + pub fn set_height(&mut self, height: usize) { + let width = self.0.width().get(); + + self.0.resize(width, height).unwrap_or_else(|| { + err!("Error while resizing Pane"); + }); + } + + #[wasm_bindgen(getter)] + pub fn position(&self) -> Vec { + let (x, y) = self.0.position(); + vec![x, y] + } + + #[wasm_bindgen(getter)] + pub fn x(&self) -> i32 { + self.0.position().0 + } + + #[wasm_bindgen(getter)] + pub fn y(&self) -> i32 { + self.0.position().1 + } + + #[wasm_bindgen(setter)] + pub fn set_x(&mut self, value: i32) { + let mut pos = self.0.position(); + pos.0 = value; + self.0.set_position(pos); + } + + #[wasm_bindgen(setter)] + pub fn set_y(&mut self, value: i32) { + let mut pos = self.0.position(); + pos.1 = value; + self.0.set_position(pos); + } + + #[wasm_bindgen(setter)] + pub fn set_position(&mut self, position: &[i32]) { + if let [x, y] = position[..] { + self.0.set_position((x, y)); + } + } + + pub fn get(&self, x: usize, y: usize) -> Option { + self.0.get((x, y)).map(|tile| FullTile((*tile).clone())) + } + + pub fn set(&mut self, x: usize, y: usize, tile: FullTile) { + if let Some(target) = self.0.get_mut((x, y)) { + *target = tile.0; + } + } + + pub fn serialize(&self) -> JsValue { + JsValue::from_serde(&self.0).map_err(|err| { + err!("Error serializing Pane: {:?}", err); + }).unwrap_or(JsValue::NULL) + } + + pub fn deserialize(value: JsValue) -> Option { + value.into_serde::().map_err(|err| { + err!("Error deserializing Pane: {:?}", err); + }).ok().map(|pane| Pane(pane)) + } + + /// NOTE: `self` must not be part of world + pub fn blit(&self, x: i32, y: i32, world: &mut World) { + for pane in world.0.panes().values() { + if pane as *const SLPane == &self.0 as *const SLPane { + panic!("Cannot blit to a World containing self"); + } + } + + for (dx, dy, tile) in self.0.tiles_iter() { + let x = x + dx as i32; + let y = y + dy as i32; + world.set(x, y, FullTile((*tile).clone())); + } + } + + pub fn to_text(&self) -> String { + let mut surface = TextSurface::new(self.0.width().get(), self.0.height().get()); + + self.0.draw(0, 0, &mut surface, stackline::utils::Blink::default()); + + format!("{:#}", surface) + } +} + +#[derive(Clone, Debug)] +#[wasm_bindgen] +pub struct FullTile(SLFullTile); + +#[wasm_bindgen] +impl FullTile { + pub fn empty() -> Self { + FullTile(SLFullTile::new(None)) + } + + #[wasm_bindgen(constructor)] + pub fn new(name: &str) -> Option { + AnyTile::new(name).map(|tile| FullTile(SLFullTile::new(Some(tile)))) + } + + pub fn clone(&self) -> FullTile { + ::clone(self) + } + + #[allow(non_snake_case)] + pub fn toString(&self) -> String { + format!("{:#?}", self.0) + } + + #[wasm_bindgen(getter)] + pub fn signal(&self) -> JsValue { + self.0.signal().and_then(|signal| JsValue::from_serde(signal).ok()).unwrap_or(JsValue::NULL) + } + + #[wasm_bindgen(setter)] + pub fn set_signal(&mut self, signal: JsValue) { + if signal.is_null() { + self.0.set_signal(None); + } else { + match signal.into_serde() { + Ok(signal) => { + self.0.set_signal(Some(signal)); + } + Err(err) => { + err!("Couldn't serialize Signal: {:?}", err); + } + } + } + } + + #[wasm_bindgen(getter)] + pub fn tile(&self) -> JsValue { + self.0 + .get() + .map(|tile| { + JsValue::from_serde(tile) + .map_err(|err| { + err!("Error while serializing AnyTile: {}", err); + }) + .ok() + }) + .flatten() + .unwrap_or(JsValue::UNDEFINED) + } + + #[wasm_bindgen(setter)] + pub fn set_tile(&mut self, tile: JsValue) { + if tile.is_null() || tile.is_undefined() { + self.0.set(None); + } else { + match tile.into_serde::() { + Ok(tile) => self.0.set(Some(tile)), + Err(err) => err!("Error while deserializing AnyTile: {}", err), + } + } + } + + #[wasm_bindgen(getter)] + pub fn state(&self) -> JsValue { + JsValue::from_serde(&self.0.state()).expect("Error while serializing State") + } + + #[wasm_bindgen(setter)] + pub fn set_state(&mut self, state: &JsValue) { + if let Ok(state) = state.into_serde() { + self.0.set_state(state); + } + } + + pub fn schema(&self) -> JsValue { + use serde_json::Value; + use stackline::tile::{Tile, TileSchema}; + + fn construct_value(schema: TileSchema) -> Value { + match schema { + TileSchema::Tuple(arr) => { + Value::Array(arr.into_iter().map(construct_value).collect()) + } + TileSchema::Map(map) => Value::Object( + map.into_iter() + .map(|(key, value)| (key, construct_value(value))) + .collect(), + ), + TileSchema::Value(label, ty) => Value::String(format!("{}:{}", label, ty)), + } + } + + if let Some(tile) = self.0.get() { + JsValue::from_serde(&construct_value(tile.schema())) + .expect("Error while serializing TileSchema") + } else { + JsValue::UNDEFINED + } + } + + pub fn map_tile(&mut self, callback: &Function) { + let res = callback + .call1(&JsValue::NULL, &self.tile()) + .expect("Error while calling javascript callback"); + self.set_tile(res); + } +} + +#[derive(Serialize, Deserialize, Clone)] +#[serde(untagged)] +enum UntaggedValue { + Number(f64), + String(String), +} + +impl From<&'_ Value> for UntaggedValue { + fn from(value: &'_ Value) -> Self { + match value { + Value::Number(x) => Self::Number(*x), + Value::String(x) => Self::String(x.clone()), + } + } +} + +impl From for UntaggedValue { + fn from(value: Value) -> Self { + match value { + Value::Number(x) => Self::Number(x), + Value::String(x) => Self::String(x), + } + } +} + +impl From for Value { + fn from(value: UntaggedValue) -> Self { + match value { + UntaggedValue::Number(x) => Self::Number(x), + UntaggedValue::String(x) => Self::String(x), + } + } +} + +#[derive(Clone, Debug)] +#[wasm_bindgen] +pub struct Signal(SLSignal); + +#[wasm_bindgen] +impl Signal { + #[wasm_bindgen(constructor)] + pub fn new() -> Self { + Self(SLSignal::empty(Direction::Up)) + } + + /// Returns a read-only array + #[wasm_bindgen(getter)] + pub fn stack(&self) -> JsValue { + JsValue::from_serde( + &self + .0 + .stack() + .iter() + .collect::>(), + ) + .unwrap() + } + + #[wasm_bindgen(getter)] + pub fn direction(&self) -> JsValue { + JsValue::from_serde(&self.0.direction()).expect("Couldn't serialize Direction") + } + + #[wasm_bindgen(setter)] + pub fn set_direction(&mut self, direction: JsValue) { + match direction.into_serde() { + Ok(dir) => { + self.0.set_direction(dir); + } + Err(err) => { + err!("Couldn't serialize Direction: {:?}", err); + } + } + } + + pub fn serialize(&self) -> JsValue { + JsValue::from_serde(&self.0).map_err(|err| { + err!("Error while serializing Signal: {:?}", err); + }).unwrap_or(JsValue::NULL) + } + + pub fn push(&mut self, value: JsValue) { + if let Some(num) = value.as_f64() { + self.0.push(Value::Number(num)); + } else if let Some(string) = value.as_string() { + self.0.push(Value::String(string)); + } else { + panic!("Invalid value: expected number or string, got {:?}", value); + } + } +} diff --git a/stackline-wasm/src/lib.rs b/stackline-wasm/src/lib.rs index 3d9c365..0ba0537 100644 --- a/stackline-wasm/src/lib.rs +++ b/stackline-wasm/src/lib.rs @@ -1,477 +1,171 @@ -mod utils; +#![allow(non_snake_case)] + +use std::cell::RefCell; +use std::rc::Rc; +use std::ops::Deref; -use js_sys::Function; -use serde::{Deserialize, Serialize}; use wasm_bindgen::prelude::*; -use stackline::pane::Pane as SLPane; -use stackline::signal::Signal as SLSignal; -use stackline::signal::Value; -use stackline::tile::AnyTile; -use stackline::tile::FullTile as SLFullTile; -use stackline::utils::Direction; -use stackline::world::World as SLWorld; -use stackline::text::TextSurface; +use stackline::{ + world::World as SLWorld, + signal::Signal as SLSignal, + signal::Value, + tile::AnyTile, + tile::FullTile as SLFullTile, +}; +use veccell::VecRef; -// TODO: refactor: -// - make everything camelCase -// - clearly separate serialized properties/functions from non-serialized -// - add javascript code to the classes? +mod utils; -// When the `wee_alloc` feature is enabled, use `wee_alloc` as the global -// allocator. -#[cfg(feature = "wee_alloc")] -#[global_allocator] -static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT; -#[wasm_bindgen] -pub struct World(SLWorld); +macro_rules! js_ser { + ( $type:expr, $value:expr ) => { + serde_wasm_bindgen::to_value($value).map_err(|err| { + err!(concat!("Error while serializing ", $type, ": {:?}"), err); + }).ok() + }; +} -#[wasm_bindgen(start)] -pub fn set_panic() { - utils::set_panic_hook(); +macro_rules! js_de { + ( $type:ty, $value:expr ) => { + serde_wasm_bindgen::from_value::<$type>($value).map_err(|err| { + err!(concat!( + "Error while deserializing ", + stringify!($type), + ": {:?}", + ), err); + }).ok() + }; } #[wasm_bindgen] -pub fn available_tiles() -> Vec { - AnyTile::available() - .iter() - .map(|name| JsValue::from_str(name)) - .collect() -} +#[derive(Clone)] +pub struct World(Rc>); #[wasm_bindgen] impl World { - /// Creates a new World instance #[wasm_bindgen(constructor)] pub fn new() -> Self { - Self(SLWorld::new()) + Self(Rc::new(RefCell::new(SLWorld::new()))) } pub fn deserialize(serialized: &JsValue) -> Option { serialized.into_serde().map_err(|err| { err!("Error while deserializing World: {:?}", err); - }).ok().map(|world| World(world)) + }).ok().map(|world| { + Self(Rc::new(RefCell::new(world))) + }) } pub fn serialize(&self) -> JsValue { - JsValue::from_serde(&self.0).map_err(|err| { - err!("Error while serializing World: {:?}", err); - }).unwrap_or(JsValue::NULL) + js_ser!("World", &*self.0.borrow()).unwrap_or(JsValue::NULL) } - /// Initializes the World, making it ready to run - pub fn init(&mut self) { - self.0.init(); + pub fn init(&self) { + self.0.borrow_mut().init(); } - #[inline] - pub fn step(&mut self) { - self.0.step(); + pub fn step(&self) { + self.0.borrow_mut().step(); } - pub fn run(&mut self, steps: usize) { + pub fn run(&self, steps: usize) { + let mut borrowed = self.0.borrow_mut(); + for _ in 0..steps { - self.step(); + borrowed.step(); } } - pub fn set_blink_duration(&mut self, blink_duration: f64) { + pub fn setBlinkDuration(&mut self, blink_duration: f64) { use std::time::Duration; + self.0 + .borrow_mut() .set_blink_duration(Duration::from_secs_f64(blink_duration)); } - #[allow(non_snake_case)] - pub fn toString(&self) -> String { - format!("{:#}", self.0) - } - - /// NOTE: We have to [`Clone`] the FullTile - pub fn get(&self, x: i32, y: i32) -> Option { - self.0.get((x, y)).map(|tile| FullTile((*tile).clone())) - } - - pub fn set(&mut self, x: i32, y: i32, tile: FullTile) { - if let Some(tile_ref) = self.0.get_mut((x, y)) { - *tile_ref = tile.0; - } else if cfg!(debug_assertions) { - err!("Index out of bound: {}:{}", x, y); - } - } - - pub fn get_pane(&self, name: String) -> Option { - self.0.get_pane(&name).map(|pane| Pane(pane.clone())) - } - - pub fn set_pane(&mut self, name: String, pane: Pane) { - self.0.set_pane(name, pane.0); - } - - pub fn panes(&self) -> Vec { - self.0.panes().keys().map(|key| { - JsValue::from_str(key) - }).collect() - } - - pub fn draw(&self, x: i32, y: i32, width: usize, height: usize) -> Vec { - let mut surface = TextSurface::new(width, height); - - self.0.draw(x, y, &mut surface); - - #[derive(Serialize)] - struct Char { - ch: char, - fg: (u8, u8, u8), - bg: Option<(u8, u8, u8)>, - } - - fn to_u32(red: u8, green: u8, blue: u8) -> u32 { - 0xff000000 | (red as u32).checked_shl(16).unwrap() | (green as u32).checked_shl(8).unwrap() | (blue as u32) - } - - surface.iter().map(|ch| { - vec![ - ch.ch as u32, - to_u32(ch.fg.red, ch.fg.green, ch.fg.blue), - ch.bg.map(|bg| to_u32(bg.red, bg.green, bg.blue)).unwrap_or(0) - ].into_iter() - }).flatten().collect::>() - } -} - -#[wasm_bindgen] -pub struct Pane(SLPane); - -#[wasm_bindgen] -impl Pane { - #[wasm_bindgen(constructor)] - pub fn empty(width: usize, height: usize) -> Option { - SLPane::empty(width, height).map(|pane| Self(pane)) - } - - #[allow(non_snake_case)] - pub fn toString(&self) -> String { - format!("{:#?}", self.0) - } - - #[wasm_bindgen(getter)] - pub fn width(&self) -> usize { - self.0.width().get() - } - - #[wasm_bindgen(setter)] - pub fn set_width(&mut self, width: usize) { - let height = self.0.height().get(); - - self.0.resize(width, height).unwrap_or_else(|| { - err!("Error while resizing Pane"); - }); - } - - #[wasm_bindgen(getter)] - pub fn height(&self) -> usize { - self.0.height().get() - } - - #[wasm_bindgen(setter)] - pub fn set_height(&mut self, height: usize) { - let width = self.0.width().get(); - - self.0.resize(width, height).unwrap_or_else(|| { - err!("Error while resizing Pane"); - }); - } - - #[wasm_bindgen(getter)] - pub fn position(&self) -> Vec { - let (x, y) = self.0.position(); - vec![x, y] - } - - #[wasm_bindgen(getter)] - pub fn x(&self) -> i32 { - self.0.position().0 - } - - #[wasm_bindgen(getter)] - pub fn y(&self) -> i32 { - self.0.position().1 - } - - #[wasm_bindgen(setter)] - pub fn set_x(&mut self, value: i32) { - let mut pos = self.0.position(); - pos.0 = value; - self.0.set_position(pos); - } - - #[wasm_bindgen(setter)] - pub fn set_y(&mut self, value: i32) { - let mut pos = self.0.position(); - pos.1 = value; - self.0.set_position(pos); - } - - #[wasm_bindgen(setter)] - pub fn set_position(&mut self, position: &[i32]) { - if let [x, y] = position[..] { - self.0.set_position((x, y)); - } - } - - pub fn get(&self, x: usize, y: usize) -> Option { - self.0.get((x, y)).map(|tile| FullTile((*tile).clone())) - } - - pub fn set(&mut self, x: usize, y: usize, tile: FullTile) { - if let Some(target) = self.0.get_mut((x, y)) { - *target = tile.0; - } - } - - pub fn serialize(&self) -> JsValue { - JsValue::from_serde(&self.0).map_err(|err| { - err!("Error serializing Pane: {:?}", err); - }).unwrap_or(JsValue::NULL) - } - - pub fn deserialize(value: JsValue) -> Option { - value.into_serde::().map_err(|err| { - err!("Error deserializing Pane: {:?}", err); - }).ok().map(|pane| Pane(pane)) - } - - /// NOTE: `self` must not be part of world - pub fn blit(&self, x: i32, y: i32, world: &mut World) { - for pane in world.0.panes().values() { - if pane as *const SLPane == &self.0 as *const SLPane { - panic!("Cannot blit to a World containing self"); - } - } - - for (dx, dy, tile) in self.0.tiles_iter() { - let x = x + dx as i32; - let y = y + dy as i32; - world.set(x, y, FullTile((*tile).clone())); - } - } - - pub fn to_text(&self) -> String { - let mut surface = TextSurface::new(self.0.width().get(), self.0.height().get()); - - self.0.draw(0, 0, &mut surface, stackline::utils::Blink::default()); - - format!("{:#}", surface) - } -} - -#[derive(Clone, Debug)] -#[wasm_bindgen] -pub struct FullTile(SLFullTile); - -#[wasm_bindgen] -impl FullTile { - pub fn empty() -> Self { - FullTile(SLFullTile::new(None)) - } - - #[wasm_bindgen(constructor)] - pub fn new(name: &str) -> Option { - AnyTile::new(name).map(|tile| FullTile(SLFullTile::new(Some(tile)))) - } - - pub fn clone(&self) -> FullTile { - ::clone(self) - } - - #[allow(non_snake_case)] - pub fn toString(&self) -> String { - format!("{:#?}", self.0) - } - - #[wasm_bindgen(getter)] - pub fn signal(&self) -> JsValue { - self.0.signal().and_then(|signal| JsValue::from_serde(signal).ok()).unwrap_or(JsValue::NULL) - } - - #[wasm_bindgen(setter)] - pub fn set_signal(&mut self, signal: JsValue) { - if signal.is_null() { - self.0.set_signal(None); - } else { - match signal.into_serde() { - Ok(signal) => { - self.0.set_signal(Some(signal)); - } - Err(err) => { - err!("Couldn't serialize Signal: {:?}", err); - } - } - } - } - - #[wasm_bindgen(getter)] - pub fn tile(&self) -> JsValue { + pub fn getSerialized(&self, x: i32, y: i32) -> JsValue { self.0 - .get() - .map(|tile| { - JsValue::from_serde(tile) - .map_err(|err| { - err!("Error while serializing AnyTile: {}", err); - }) - .ok() + .borrow() + .get((x, y)) + .and_then(|tile| { + js_ser!("FullTile", &*tile) }) - .flatten() - .unwrap_or(JsValue::UNDEFINED) + .unwrap_or(JsValue::NULL) } - #[wasm_bindgen(setter)] - pub fn set_tile(&mut self, tile: JsValue) { - if tile.is_null() || tile.is_undefined() { - self.0.set(None); - } else { - match tile.into_serde::() { - Ok(tile) => self.0.set(Some(tile)), - Err(err) => err!("Error while deserializing AnyTile: {}", err), - } - } - } - - #[wasm_bindgen(getter)] - pub fn state(&self) -> JsValue { - JsValue::from_serde(&self.0.state()).expect("Error while serializing State") - } + pub fn setSerialized(&mut self, x: i32, y: i32, value: JsValue) { + let deserialized = js_de!(SLFullTile, value).unwrap(); - #[wasm_bindgen(setter)] - pub fn set_state(&mut self, state: &JsValue) { - if let Ok(state) = state.into_serde() { - self.0.set_state(state); + if let Some(tile) = self.0 + .borrow_mut() + .get_mut((x, y)) { + *tile = deserialized; } } - pub fn schema(&self) -> JsValue { - use serde_json::Value; - use stackline::tile::{Tile, TileSchema}; - - fn construct_value(schema: TileSchema) -> Value { - match schema { - TileSchema::Tuple(arr) => { - Value::Array(arr.into_iter().map(construct_value).collect()) - } - TileSchema::Map(map) => Value::Object( - map.into_iter() - .map(|(key, value)| (key, construct_value(value))) - .collect(), - ), - TileSchema::Value(label, ty) => Value::String(format!("{}:{}", label, ty)), - } - } - - if let Some(tile) = self.0.get() { - JsValue::from_serde(&construct_value(tile.schema())) - .expect("Error while serializing TileSchema") + pub fn getRef(&self, x: i32, y: i32) -> Option { + if let Some((pane_name, _pane, x, y)) = self.0.borrow().get_pane_at((x, y)) { + Some(FullTileRef { + world: Rc::clone(&self.0), + pane: pane_name.to_string(), + x, + y, + }) } else { - JsValue::UNDEFINED + None } } - pub fn map_tile(&mut self, callback: &Function) { - let res = callback - .call1(&JsValue::NULL, &self.tile()) - .expect("Error while calling javascript callback"); - self.set_tile(res); - } -} - -#[derive(Serialize, Deserialize, Clone)] -#[serde(untagged)] -enum UntaggedValue { - Number(f64), - String(String), -} - -impl From<&'_ Value> for UntaggedValue { - fn from(value: &'_ Value) -> Self { - match value { - Value::Number(x) => Self::Number(*x), - Value::String(x) => Self::String(x.clone()), - } + pub fn toString(&self) -> String { + format!("{:#}", self.0.borrow()) } } -impl From for UntaggedValue { - fn from(value: Value) -> Self { - match value { - Value::Number(x) => Self::Number(x), - Value::String(x) => Self::String(x), - } - } -} +// impl Deref for World { +// type Target = SLWorld; -impl From for Value { - fn from(value: UntaggedValue) -> Self { - match value { - UntaggedValue::Number(x) => Self::Number(x), - UntaggedValue::String(x) => Self::String(x), - } - } -} +// fn deref(&self) -> &SLWorld { +// self.0.borrow() +// } +// } -#[derive(Clone, Debug)] #[wasm_bindgen] -pub struct Signal(SLSignal); +pub struct FullTileRef { + world: Rc>, + pane: String, + pub x: usize, + pub y: usize, +} -#[wasm_bindgen] -impl Signal { - #[wasm_bindgen(constructor)] - pub fn new() -> Self { - Self(SLSignal::empty(Direction::Up)) - } +impl FullTileRef { + fn with(&self, cb: F) { + let guard = self.world + .borrow(); + let reference = guard + .get_pane(&self.pane) + .expect("Invalid FullTile: no pane") + .get((self.x, self.y)) + .expect("Invalid FullTile: tile out of bounds"); - /// Returns a read-only array - #[wasm_bindgen(getter)] - pub fn stack(&self) -> JsValue { - JsValue::from_serde( - &self - .0 - .stack() - .iter() - .collect::>(), - ) - .unwrap() + cb(&*reference); } - #[wasm_bindgen(getter)] - pub fn direction(&self) -> JsValue { - JsValue::from_serde(&self.0.direction()).expect("Couldn't serialize Direction") - } + fn with_mut(&self, cb: F) { + let mut guard = self.world + .borrow_mut(); + let reference = guard + .get_pane_mut(&self.pane) + .expect("Invalid FullTile: no pane") + .get_mut((self.x, self.y)) + .expect("Invalid FullTile: tile out of bounds"); - #[wasm_bindgen(setter)] - pub fn set_direction(&mut self, direction: JsValue) { - match direction.into_serde() { - Ok(dir) => { - self.0.set_direction(dir); - } - Err(err) => { - err!("Couldn't serialize Direction: {:?}", err); - } - } + cb(&mut *reference); } +} - pub fn serialize(&self) -> JsValue { - JsValue::from_serde(&self.0).map_err(|err| { - err!("Error while serializing Signal: {:?}", err); - }).unwrap_or(JsValue::NULL) - } +#[wasm_bindgen] +impl FullTileRef { - pub fn push(&mut self, value: JsValue) { - if let Some(num) = value.as_f64() { - self.0.push(Value::Number(num)); - } else if let Some(string) = value.as_string() { - self.0.push(Value::String(string)); - } else { - panic!("Invalid value: expected number or string, got {:?}", value); - } - } } diff --git a/stackline/src/world.rs b/stackline/src/world.rs index 715b09c..77efbdd 100644 --- a/stackline/src/world.rs +++ b/stackline/src/world.rs @@ -73,6 +73,22 @@ impl World { // TODO: get_pane_at, get_pane_at_mut + pub fn get_pane_at(&self, (x, y): (i32, i32)) -> Option<(&String, &Pane, usize, usize)> { + for (name, pane) in &self.panes { + let x2 = x - pane.position().0; + let y2 = y - pane.position().1; + if x2 >= 0 + && x2 < pane.width().get() as i32 + && y2 >= 0 + && y2 < pane.height().get() as i32 + { + return Some((name, pane, x2 as usize, y2 as usize)); + } + } + + None + } + pub fn get(&self, (x, y): (i32, i32)) -> Option> { for pane in self.panes.values() { let x2 = x - pane.position().0;