2025-06-30 16:49:07 +00:00
|
|
|
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" {
|
2025-07-01 16:54:11 +00:00
|
|
|
#[wasm_bindgen(catch)]
|
|
|
|
async fn io (output: String) -> Result<JsValue, JsValue>;
|
2025-06-30 16:49:07 +00:00
|
|
|
}
|
|
|
|
|
2025-06-30 22:59:59 +00:00
|
|
|
#[wasm_bindgen]
|
|
|
|
extern "C" {
|
|
|
|
#[wasm_bindgen(js_namespace = console)]
|
|
|
|
fn log(s: String);
|
|
|
|
}
|
|
|
|
|
2025-07-04 00:22:11 +00:00
|
|
|
type Url = Value; // expect a string
|
|
|
|
type Commands = Value; // expect a list of command tuples
|
2025-06-30 16:49:07 +00:00
|
|
|
|
2025-07-04 00:22:11 +00:00
|
|
|
#[derive(Debug, Clone, PartialEq, Serialize)]
|
|
|
|
#[serde(tag = "verb", content = "data")]
|
2025-06-30 16:49:07 +00:00
|
|
|
pub enum MsgOut {
|
2025-07-03 03:47:02 +00:00
|
|
|
Console(Value),
|
2025-06-30 16:49:07 +00:00
|
|
|
Commands(Commands),
|
2025-07-01 22:52:03 +00:00
|
|
|
Fetch(Url),
|
2025-07-04 00:22:11 +00:00
|
|
|
Complete(Value),
|
|
|
|
Error(String),
|
2025-07-02 18:51:42 +00:00
|
|
|
Ready
|
2025-06-30 16:49:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2025-07-04 00:22:11 +00:00
|
|
|
// impl MsgOut {
|
|
|
|
// pub fn to_json(&self) -> String {
|
|
|
|
// match self {
|
|
|
|
// MsgOut::Complete(value) => match value {
|
|
|
|
// Ok(value) => {
|
|
|
|
// make_json_payload("Complete", serde_json::to_string(&value.show()).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::<Vec<_>>().join(",");
|
|
|
|
// let vals_json = format!("[{vals_json}]");
|
|
|
|
// make_json_payload("Commands", vals_json)
|
|
|
|
// }
|
|
|
|
// MsgOut::Fetch(value) => {
|
|
|
|
// // TODO: do parsing here?
|
|
|
|
// // Right now, defer to fetch
|
|
|
|
// let url = value.to_json().unwrap();
|
|
|
|
// make_json_payload("Fetch", url)
|
|
|
|
// }
|
|
|
|
// MsgOut::Console(lines) => {
|
|
|
|
// let lines = lines.as_list();
|
|
|
|
// let json_lines = lines.iter().map(|line| line.to_json().unwrap()).collect::<Vec<_>>().join("\\n");
|
|
|
|
// let json_lines = format!("\"{json_lines}\"");
|
|
|
|
// make_json_payload("Console", json_lines)
|
|
|
|
// }
|
|
|
|
// MsgOut::Ready => {
|
|
|
|
// make_json_payload("Ready", "\"null\"".to_string())
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
// }
|
2025-06-30 16:49:07 +00:00
|
|
|
|
|
|
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
|
|
|
#[serde(tag = "verb", content = "data")]
|
|
|
|
pub enum MsgIn {
|
|
|
|
Input(String),
|
2025-07-01 22:52:03 +00:00
|
|
|
Fetch(String, f64, String),
|
2025-06-30 16:49:07 +00:00
|
|
|
Kill,
|
|
|
|
Keyboard(Vec<String>),
|
|
|
|
}
|
|
|
|
|
2025-06-30 22:59:59 +00:00
|
|
|
impl std::fmt::Display for MsgIn {
|
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
|
|
|
match self {
|
2025-07-01 20:59:42 +00:00
|
|
|
MsgIn::Input(str) => write!(f, "Input: {str}"),
|
|
|
|
MsgIn::Kill => write!(f, "Kill"),
|
2025-07-01 22:52:03 +00:00
|
|
|
MsgIn::Fetch(url, code, text) => write!(f, "Fetch: {url} :: {code} ::\n{text}"),
|
2025-06-30 22:59:59 +00:00
|
|
|
_ => todo!()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-06-30 16:49:07 +00:00
|
|
|
impl MsgIn {
|
|
|
|
pub fn to_value(self) -> Value {
|
|
|
|
match self {
|
|
|
|
MsgIn::Input(str) => Value::string(str),
|
2025-07-01 22:52:03 +00:00
|
|
|
MsgIn::Fetch(url, status_f64, string) => {
|
2025-06-30 16:49:07 +00:00
|
|
|
let url = Value::string(url);
|
2025-07-01 22:52:03 +00:00
|
|
|
let status = Value::Number(status_f64);
|
|
|
|
let text = Value::string(string);
|
|
|
|
let result_tuple = if status_f64 == 200.0 {
|
|
|
|
Value::tuple(vec![Value::keyword("ok".to_string()), text])
|
|
|
|
} else {
|
|
|
|
Value::tuple(vec![Value::keyword("err".to_string()), status])
|
|
|
|
};
|
2025-06-30 16:49:07 +00:00
|
|
|
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))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-07-03 03:47:02 +00:00
|
|
|
pub async fn send_err_to_ludus_console(msg: String) {
|
|
|
|
log(msg.clone());
|
2025-07-04 00:22:11 +00:00
|
|
|
do_io(vec![MsgOut::Ready, MsgOut::Error(msg)]).await;
|
2025-07-03 03:47:02 +00:00
|
|
|
}
|
|
|
|
|
2025-06-30 16:49:07 +00:00
|
|
|
pub async fn do_io (msgs: Vec<MsgOut>) -> Vec<MsgIn> {
|
2025-07-04 00:22:11 +00:00
|
|
|
let json = serde_json::to_string(&msgs).unwrap();
|
|
|
|
let inbox = io (json).await;
|
2025-07-01 16:54:11 +00:00
|
|
|
// if our request dies, make sure we return back to the event loop
|
|
|
|
let inbox = match inbox {
|
|
|
|
Ok(msgs) => msgs,
|
|
|
|
Err(_) => return vec![]
|
|
|
|
};
|
2025-06-30 16:49:07 +00:00
|
|
|
let inbox = inbox.as_string().expect("response should be a string");
|
2025-07-01 23:04:38 +00:00
|
|
|
// log(format!("got a message: {inbox}"));
|
2025-06-30 16:49:07 +00:00
|
|
|
let inbox: Vec<MsgIn> = serde_json::from_str(inbox.as_str()).expect("response from js should be valid");
|
2025-06-30 22:59:59 +00:00
|
|
|
if !inbox.is_empty() {
|
2025-07-01 04:43:01 +00:00
|
|
|
log("ludus received messages".to_string());
|
2025-06-30 22:59:59 +00:00
|
|
|
for msg in inbox.iter() {
|
|
|
|
log(format!("{}", msg));
|
|
|
|
}
|
|
|
|
}
|
2025-06-30 16:49:07 +00:00
|
|
|
inbox
|
|
|
|
}
|
|
|
|
|