Backup commit

tmp
Shad Amethyst 2 years ago
parent 2597a7e1b7
commit c0e562a3af

@ -16,6 +16,7 @@ serde_json = "1.0"
serde = { version = "1", features = ["derive"] } serde = { version = "1", features = ["derive"] }
stackline = { path = "../stackline", features = [], default-features = false } 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 # The `console_error_panic_hook` crate provides better debugging of panics by
# logging them with `console.error`. This is great for development, but requires # 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"] } web-sys = { version = "0.3", features = ["console"] }
js-sys = "0.3" js-sys = "0.3"
serde-wasm-bindgen = "0.4.3"
[dev-dependencies] [dev-dependencies]
wasm-bindgen-test = "0.3.13" wasm-bindgen-test = "0.3.13"

@ -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<JsValue> {
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<World> {
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<FullTile> {
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<Pane> {
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<JsValue> {
self.0.panes().keys().map(|key| {
JsValue::from_str(key)
}).collect()
}
pub fn draw(&self, x: i32, y: i32, width: usize, height: usize) -> Vec<u32> {
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::<Vec<_>>()
}
}
#[wasm_bindgen]
pub struct Pane(SLPane);
#[wasm_bindgen]
impl Pane {
#[wasm_bindgen(constructor)]
pub fn empty(width: usize, height: usize) -> Option<Pane> {
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<i32> {
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<FullTile> {
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<Pane> {
value.into_serde::<SLPane>().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<FullTile> {
AnyTile::new(name).map(|tile| FullTile(SLFullTile::new(Some(tile))))
}
pub fn clone(&self) -> FullTile {
<Self as Clone>::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::<AnyTile>() {
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<Value> for UntaggedValue {
fn from(value: Value) -> Self {
match value {
Value::Number(x) => Self::Number(x),
Value::String(x) => Self::String(x),
}
}
}
impl From<UntaggedValue> 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::<Vec<_>>(),
)
.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);
}
}
}

@ -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 wasm_bindgen::prelude::*;
use stackline::pane::Pane as SLPane; use stackline::{
use stackline::signal::Signal as SLSignal; world::World as SLWorld,
use stackline::signal::Value; signal::Signal as SLSignal,
use stackline::tile::AnyTile; signal::Value,
use stackline::tile::FullTile as SLFullTile; tile::AnyTile,
use stackline::utils::Direction; tile::FullTile as SLFullTile,
use stackline::world::World as SLWorld; };
use stackline::text::TextSurface; use veccell::VecRef;
// TODO: refactor: mod utils;
// - 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] macro_rules! js_ser {
pub struct World(SLWorld); ( $type:expr, $value:expr ) => {
serde_wasm_bindgen::to_value($value).map_err(|err| {
err!(concat!("Error while serializing ", $type, ": {:?}"), err);
}).ok()
};
}
#[wasm_bindgen(start)] macro_rules! js_de {
pub fn set_panic() { ( $type:ty, $value:expr ) => {
utils::set_panic_hook(); serde_wasm_bindgen::from_value::<$type>($value).map_err(|err| {
err!(concat!(
"Error while deserializing ",
stringify!($type),
": {:?}",
), err);
}).ok()
};
} }
#[wasm_bindgen] #[wasm_bindgen]
pub fn available_tiles() -> Vec<JsValue> { #[derive(Clone)]
AnyTile::available() pub struct World(Rc<RefCell<SLWorld>>);
.iter()
.map(|name| JsValue::from_str(name))
.collect()
}
#[wasm_bindgen] #[wasm_bindgen]
impl World { impl World {
/// Creates a new World instance
#[wasm_bindgen(constructor)] #[wasm_bindgen(constructor)]
pub fn new() -> Self { pub fn new() -> Self {
Self(SLWorld::new()) Self(Rc::new(RefCell::new(SLWorld::new())))
} }
pub fn deserialize(serialized: &JsValue) -> Option<World> { pub fn deserialize(serialized: &JsValue) -> Option<World> {
serialized.into_serde().map_err(|err| { serialized.into_serde().map_err(|err| {
err!("Error while deserializing World: {:?}", 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 { pub fn serialize(&self) -> JsValue {
JsValue::from_serde(&self.0).map_err(|err| { js_ser!("World", &*self.0.borrow()).unwrap_or(JsValue::NULL)
err!("Error while serializing World: {:?}", err);
}).unwrap_or(JsValue::NULL)
} }
/// Initializes the World, making it ready to run pub fn init(&self) {
pub fn init(&mut self) { self.0.borrow_mut().init();
self.0.init();
} }
#[inline] pub fn step(&self) {
pub fn step(&mut self) { self.0.borrow_mut().step();
self.0.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 { 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; use std::time::Duration;
self.0 self.0
.borrow_mut()
.set_blink_duration(Duration::from_secs_f64(blink_duration)); .set_blink_duration(Duration::from_secs_f64(blink_duration));
} }
#[allow(non_snake_case)] pub fn getSerialized(&self, x: i32, y: i32) -> JsValue {
pub fn toString(&self) -> String {
format!("{:#}", self.0)
}
/// NOTE: We have to [`Clone`] the FullTile
pub fn get(&self, x: i32, y: i32) -> Option<FullTile> {
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<Pane> {
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<JsValue> {
self.0.panes().keys().map(|key| {
JsValue::from_str(key)
}).collect()
}
pub fn draw(&self, x: i32, y: i32, width: usize, height: usize) -> Vec<u32> {
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::<Vec<_>>()
}
}
#[wasm_bindgen]
pub struct Pane(SLPane);
#[wasm_bindgen]
impl Pane {
#[wasm_bindgen(constructor)]
pub fn empty(width: usize, height: usize) -> Option<Pane> {
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<i32> {
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<FullTile> {
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<Pane> {
value.into_serde::<SLPane>().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<FullTile> {
AnyTile::new(name).map(|tile| FullTile(SLFullTile::new(Some(tile))))
}
pub fn clone(&self) -> FullTile {
<Self as Clone>::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 self.0
.get() .borrow()
.map(|tile| { .get((x, y))
JsValue::from_serde(tile) .and_then(|tile| {
.map_err(|err| { js_ser!("FullTile", &*tile)
err!("Error while serializing AnyTile: {}", err);
})
.ok()
}) })
.flatten() .unwrap_or(JsValue::NULL)
.unwrap_or(JsValue::UNDEFINED)
} }
#[wasm_bindgen(setter)] pub fn setSerialized(&mut self, x: i32, y: i32, value: JsValue) {
pub fn set_tile(&mut self, tile: JsValue) { let deserialized = js_de!(SLFullTile, value).unwrap();
if tile.is_null() || tile.is_undefined() {
self.0.set(None);
} else {
match tile.into_serde::<AnyTile>() {
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)] if let Some(tile) = self.0
pub fn set_state(&mut self, state: &JsValue) { .borrow_mut()
if let Ok(state) = state.into_serde() { .get_mut((x, y)) {
self.0.set_state(state); *tile = deserialized;
} }
} }
pub fn schema(&self) -> JsValue { pub fn getRef(&self, x: i32, y: i32) -> Option<FullTileRef> {
use serde_json::Value; if let Some((pane_name, _pane, x, y)) = self.0.borrow().get_pane_at((x, y)) {
use stackline::tile::{Tile, TileSchema}; Some(FullTileRef {
world: Rc::clone(&self.0),
fn construct_value(schema: TileSchema) -> Value { pane: pane_name.to_string(),
match schema { x,
TileSchema::Tuple(arr) => { y,
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 { } else {
JsValue::UNDEFINED None
} }
} }
pub fn map_tile(&mut self, callback: &Function) { pub fn toString(&self) -> String {
let res = callback format!("{:#}", self.0.borrow())
.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<Value> for UntaggedValue { // impl Deref for World {
fn from(value: Value) -> Self { // type Target = SLWorld;
match value {
Value::Number(x) => Self::Number(x),
Value::String(x) => Self::String(x),
}
}
}
impl From<UntaggedValue> for Value { // fn deref(&self) -> &SLWorld {
fn from(value: UntaggedValue) -> Self { // self.0.borrow()
match value { // }
UntaggedValue::Number(x) => Self::Number(x), // }
UntaggedValue::String(x) => Self::String(x),
}
}
}
#[derive(Clone, Debug)]
#[wasm_bindgen] #[wasm_bindgen]
pub struct Signal(SLSignal); pub struct FullTileRef {
world: Rc<RefCell<SLWorld>>,
pane: String,
pub x: usize,
pub y: usize,
}
#[wasm_bindgen] impl FullTileRef {
impl Signal { fn with<F: FnOnce(&SLFullTile)>(&self, cb: F) {
#[wasm_bindgen(constructor)] let guard = self.world
pub fn new() -> Self { .borrow();
Self(SLSignal::empty(Direction::Up)) 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 cb(&*reference);
#[wasm_bindgen(getter)]
pub fn stack(&self) -> JsValue {
JsValue::from_serde(
&self
.0
.stack()
.iter()
.collect::<Vec<_>>(),
)
.unwrap()
} }
#[wasm_bindgen(getter)] fn with_mut<F: FnOnce(&mut SLFullTile)>(&self, cb: F) {
pub fn direction(&self) -> JsValue { let mut guard = self.world
JsValue::from_serde(&self.0.direction()).expect("Couldn't serialize Direction") .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)] cb(&mut *reference);
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 { #[wasm_bindgen]
JsValue::from_serde(&self.0).map_err(|err| { impl FullTileRef {
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);
}
}
} }

@ -73,6 +73,22 @@ impl World {
// TODO: get_pane_at, get_pane_at_mut // 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<VecRef<'_, FullTile>> { pub fn get(&self, (x, y): (i32, i32)) -> Option<VecRef<'_, FullTile>> {
for pane in self.panes.values() { for pane in self.panes.values() {
let x2 = x - pane.position().0; let x2 = x - pane.position().0;

Loading…
Cancel
Save