diff --git a/src/io.rs b/src/io.rs new file mode 100644 index 0000000..3a406ff --- /dev/null +++ b/src/io.rs @@ -0,0 +1,106 @@ +use wasm_bindgen::prelude::*; +use serde::{Serialize, Deserialize}; +use crate::value::Value; +use crate::vm::Panic; +use imbl::Vector; +use std::rc::Rc; + + +#[wasm_bindgen(module = "/pkg/worker.js")] +extern "C" { + async fn io (output: String) -> JsValue; +} + +type Lines = Value; // expect a list of values +type Commands = Value; // expect a list of values +type Url = Value; // expect a string representing a URL +type FinalValue = Result; + +fn make_json_payload(verb: &'static str, data: String) -> String { + format!("{{\"verb\":\"{verb}\",\"data\":{data}}}") +} + +#[derive(Debug, Clone, PartialEq)] +pub enum MsgOut { + Console(Lines), + Commands(Commands), + SlurpRequest(Url), + Complete(Result), +} + +impl std::fmt::Display for MsgOut { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{}", self.to_json()) + } +} + +impl MsgOut { + pub fn to_json(&self) -> String { + match self { + MsgOut::Complete(value) => match value { + Ok(value) => make_json_payload("complete", value.to_json().unwrap()), + Err(_) => make_json_payload("complete", "\"null\"".to_string()) + }, + MsgOut::Commands(commands) => { + let commands = commands.as_list(); + let vals_json = commands.iter().map(|v| v.to_json().unwrap()).collect::>().join(","); + let vals_json = format!("[{vals_json}]"); + make_json_payload("commands", vals_json) + } + MsgOut::SlurpRequest(value) => { + // TODO: do parsing here? + // Right now, defer to fetch + let url = value.to_json().unwrap(); + make_json_payload("slurp", url) + } + MsgOut::Console(lines) => { + let lines = lines.as_list(); + let json_lines = lines.iter().map(|line| line.stringify()).collect::>().join("\n"); + let json_lines = format!("\"{json_lines}\""); + make_json_payload("console", json_lines) + } + } + } +} + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(tag = "verb", content = "data")] +pub enum MsgIn { + Input(String), + SlurpResponse(String, String, String), + Kill, + Keyboard(Vec), +} + +impl MsgIn { + pub fn to_value(self) -> Value { + match self { + MsgIn::Input(str) => Value::string(str), + MsgIn::SlurpResponse(url, status, string) => { + let url = Value::string(url); + let status = Value::keyword(status); + let string = Value::string(string); + let result_tuple = Value::tuple(vec![status, string]); + Value::tuple(vec![url, result_tuple]) + } + MsgIn::Kill => Value::Nothing, + MsgIn::Keyboard(downkeys) => { + let mut vector = Vector::new(); + for key in downkeys { + vector.push_back(Value::String(Rc::new(key))); + } + Value::List(Box::new(vector)) + } + } + } +} + +pub async fn do_io (msgs: Vec) -> Vec { + let outbox = format!("[{}]", msgs.iter().map(|msg| msg.to_json()).collect::>().join(",")); + let inbox = io (outbox).await; + let inbox = inbox.as_string().expect("response should be a string"); + let inbox: Vec = serde_json::from_str(inbox.as_str()).expect("response from js should be valid"); + inbox + +} +