use serde to serialize the things

This commit is contained in:
Scott Richmond 2025-07-03 20:22:11 -04:00
parent 9f9f59b33b
commit c6709bb2e8
10 changed files with 193 additions and 123 deletions

View File

@ -18,3 +18,4 @@ serde = {version = "1.0", features = ["derive"]}
serde_json = "1.0"
console_error_panic_hook = "0.1.7"
struct_scalpel = "0.1.1"
serde-wasm-bindgen = "0.6.5"

View File

@ -32,10 +32,18 @@ async function handle_messages (e) {
outbox = []
break
}
case "Error": {
console.log("Main: ludus errored with => ", msg.data)
running = false
ready = false
outbox = []
break
}
// TODO: do more than report these
case "Console": {
console.log("Main: ludus says => ", msg.data)
ludus_console = ludus_console + msg.data
let new_lines = msg.data.join("\n");
console.log("Main: ludus says => ", new_lines)
ludus_console = ludus_console + new_lines
break
}
case "Commands": {

4
pkg/rudus.d.ts vendored
View File

@ -14,8 +14,8 @@ export interface InitOutput {
readonly __wbindgen_malloc: (a: number, b: number) => number;
readonly __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number;
readonly __wbindgen_export_6: WebAssembly.Table;
readonly closure347_externref_shim: (a: number, b: number, c: any) => void;
readonly closure371_externref_shim: (a: number, b: number, c: any, d: any) => void;
readonly closure305_externref_shim: (a: number, b: number, c: any) => void;
readonly closure329_externref_shim: (a: number, b: number, c: any, d: any) => void;
readonly __wbindgen_start: () => void;
}

View File

@ -240,13 +240,13 @@ function _assertNum(n) {
function __wbg_adapter_20(arg0, arg1, arg2) {
_assertNum(arg0);
_assertNum(arg1);
wasm.closure347_externref_shim(arg0, arg1, arg2);
wasm.closure305_externref_shim(arg0, arg1, arg2);
}
function __wbg_adapter_50(arg0, arg1, arg2, arg3) {
_assertNum(arg0);
_assertNum(arg1);
wasm.closure371_externref_shim(arg0, arg1, arg2, arg3);
wasm.closure329_externref_shim(arg0, arg1, arg2, arg3);
}
async function __wbg_load(module, imports) {
@ -425,8 +425,8 @@ function __wbg_get_imports() {
_assertBoolean(ret);
return ret;
};
imports.wbg.__wbindgen_closure_wrapper7945 = function() { return logError(function (arg0, arg1, arg2) {
const ret = makeMutClosure(arg0, arg1, 348, __wbg_adapter_20);
imports.wbg.__wbindgen_closure_wrapper7885 = function() { return logError(function (arg0, arg1, arg2) {
const ret = makeMutClosure(arg0, arg1, 306, __wbg_adapter_20);
return ret;
}, arguments) };
imports.wbg.__wbindgen_debug_string = function(arg0, arg1) {

Binary file not shown.

View File

@ -9,6 +9,6 @@ export const __wbindgen_free: (a: number, b: number, c: number) => void;
export const __wbindgen_malloc: (a: number, b: number) => number;
export const __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number;
export const __wbindgen_export_6: WebAssembly.Table;
export const closure347_externref_shim: (a: number, b: number, c: any) => void;
export const closure371_externref_shim: (a: number, b: number, c: any, d: any) => void;
export const closure305_externref_shim: (a: number, b: number, c: any) => void;
export const closure329_externref_shim: (a: number, b: number, c: any, d: any) => void;
export const __wbindgen_start: () => void;

View File

@ -18,62 +18,54 @@ extern "C" {
fn log(s: String);
}
type Commands = Value; // expect a list of values
type Url = Value; // expect a string representing a URL
type FinalValue = Result<Value, Panic>;
type Url = Value; // expect a string
type Commands = Value; // expect a list of command tuples
fn make_json_payload(verb: &'static str, data: String) -> String {
format!("{{\"verb\":\"{verb}\",\"data\":{data}}}")
}
#[derive(Debug, Clone, PartialEq)]
#[derive(Debug, Clone, PartialEq, Serialize)]
#[serde(tag = "verb", content = "data")]
pub enum MsgOut {
Console(Value),
Commands(Commands),
Fetch(Url),
Complete(FinalValue),
Complete(Value),
Error(String),
Ready
}
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", 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())
}
}
}
}
// 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())
// }
// }
// }
// }
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(tag = "verb", content = "data")]
@ -124,18 +116,12 @@ impl MsgIn {
pub async fn send_err_to_ludus_console(msg: String) {
log(msg.clone());
let console_msg = Value::string(msg);
let mut console_vector = Vector::new();
console_vector.push_back(console_msg);
let console_list = Value::list(console_vector);
let console = MsgOut::Console(console_list);
let completion = MsgOut::Complete(Err(Panic::Str("")));
do_io(vec![MsgOut::Ready, console, completion]).await;
do_io(vec![MsgOut::Ready, MsgOut::Error(msg)]).await;
}
pub async fn do_io (msgs: Vec<MsgOut>) -> Vec<MsgIn> {
let outbox = format!("[{}]", msgs.iter().map(|msg| msg.to_json()).collect::<Vec<_>>().join(","));
let inbox = io (outbox).await;
let json = serde_json::to_string(&msgs).unwrap();
let inbox = io (json).await;
// if our request dies, make sure we return back to the event loop
let inbox = match inbox {
Ok(msgs) => msgs,

View File

@ -1,8 +1,6 @@
use rudus::value::Value;
use std::env;
use struct_scalpel::print_dissection_info;
pub fn main() {
env::set_var("RUST_BACKTRACE", "1");
print_dissection_info::<Value>();
}

View File

@ -1,9 +1,10 @@
use crate::base::BaseFn;
use crate::chunk::Chunk;
use imbl::{HashMap, Vector};
use serde::ser::{Serialize, SerializeMap, SerializeSeq, Serializer};
use std::cell::RefCell;
use std::rc::Rc;
use struct_scalpel::Dissectible;
use wasm_bindgen::JsValue;
#[derive(Clone, Debug)]
pub enum LFn {
@ -130,6 +131,19 @@ impl std::fmt::Display for Key {
}
}
impl Serialize for Key {
fn serialize<S>(&self, srlzr: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match self {
Key::Keyword(s) => srlzr.serialize_str(s),
Key::Interned(s) => srlzr.serialize_str(s),
Key::String(s) => srlzr.serialize_str(s.as_str()),
}
}
}
impl Key {
pub fn to_value(&self) -> Value {
match self {
@ -149,7 +163,7 @@ impl Key {
}
}
#[derive(Clone, Debug, Dissectible)]
#[derive(Clone, Debug)]
pub enum Value {
Nothing,
Nil,
@ -247,6 +261,51 @@ impl std::fmt::Display for Value {
}
}
impl Serialize for Value {
fn serialize<S>(&self, srlzr: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
use Value::*;
match self {
Nil => srlzr.serialize_none(),
True => srlzr.serialize_bool(true),
False => srlzr.serialize_bool(false),
Number(n) => srlzr.serialize_f64(*n),
Interned(s) => srlzr.serialize_str(s),
Keyword(k) => srlzr.serialize_str(k),
String(s) => srlzr.serialize_str(s.as_str()),
Tuple(t) => {
let mut seq = srlzr.serialize_seq(Some(t.len()))?;
for e in t.iter() {
seq.serialize_element(e)?;
}
seq.end()
}
List(l) => {
let mut seq = srlzr.serialize_seq(Some(l.len()))?;
for e in l.iter() {
seq.serialize_element(e)?;
}
seq.end()
}
Dict(d) => {
let mut map = srlzr.serialize_map(Some(d.len()))?;
for (k, v) in d.iter() {
map.serialize_entry(k, v)?;
}
map.end()
}
Box(b) => {
let boxed = b.borrow();
(*boxed).serialize(srlzr)
}
Fn(..) | BaseFn(..) | Partial(..) => unreachable!(),
Process | Nothing => unreachable!(),
}
}
}
impl Value {
pub fn show(&self) -> String {
use Value::*;
@ -292,57 +351,71 @@ impl Value {
}
}
pub fn to_json(&self) -> Option<String> {
use Value::*;
match self {
True | False | Number(..) => Some(self.show()),
String(string) => Some(string.escape_default().to_string()),
Interned(str) => Some(str.escape_default().to_string()),
Keyword(str) => Some(format!("\"{str}\"")),
List(members) => {
let mut joined = "".to_string();
let mut members = members.iter();
if let Some(member) = members.next() {
joined = member.to_json()?;
}
for member in members {
let json = member.to_json()?;
joined = format!("{joined},{json}");
}
Some(format!("[{joined}]"))
}
Tuple(members) => {
let mut joined = "".to_string();
let mut members = members.iter();
if let Some(member) = members.next() {
joined = member.to_json()?;
}
for member in members {
let json = member.to_json()?;
joined = format!("{joined},{json}");
}
Some(format!("[{joined}]"))
}
Dict(members) => {
let mut joined = "".to_string();
let mut members = members.iter();
if let Some((key, value)) = members.next() {
let json = value.to_json()?;
joined = format!("\"{key}\":{json}")
}
for (key, value) in members {
let json = value.to_json()?;
joined = format!("{joined},\"{key}\": {json}");
}
Some(format!("{{{joined}}}"))
}
not_serializable => {
println!("Cannot convert to json:");
dbg!(not_serializable);
None
}
}
}
// pub fn to_js(&self) -> JsValue {
// use Value::*;
// match self {
// Nil => JsValue::NULL,
// True => JsValue::TRUE,
// False => JsValue::FALSE,
// Number(n) => JsValue::from_f64(*n),
// Interned(s) => JsValue::from_str(s),
// String(s) => JsValue::from_str(s.as_str()),
// Keyword(k) => JsValue::from_str(k),
// _ => todo!(),
// }
// }
// pub fn to_json(&self) -> Option<String> {
// use Value::*;
// match self {
// True | False | Number(..) => Some(self.show()),
// String(string) => Some(string.escape_default().to_string()),
// Interned(str) => Some(str.escape_default().to_string()),
// Keyword(str) => Some(format!("\"{str}\"")),
// List(members) => {
// let mut joined = "".to_string();
// let mut members = members.iter();
// if let Some(member) = members.next() {
// joined = member.to_json()?;
// }
// for member in members {
// let json = member.to_json()?;
// joined = format!("{joined},{json}");
// }
// Some(format!("[{joined}]"))
// }
// Tuple(members) => {
// let mut joined = "".to_string();
// let mut members = members.iter();
// if let Some(member) = members.next() {
// joined = member.to_json()?;
// }
// for member in members {
// let json = member.to_json()?;
// joined = format!("{joined},{json}");
// }
// Some(format!("[{joined}]"))
// }
// Dict(members) => {
// let mut joined = "".to_string();
// let mut members = members.iter();
// if let Some((key, value)) = members.next() {
// let json = value.to_json()?;
// joined = format!("\"{key}\":{json}")
// }
// for (key, value) in members {
// let json = value.to_json()?;
// joined = format!("{joined},\"{key}\": {json}");
// }
// Some(format!("{{{joined}}}"))
// }
// not_serializable => {
// println!("Cannot convert to json:");
// dbg!(not_serializable);
// None
// }
// }
// }
pub fn stringify(&self) -> String {
use Value::*;

View File

@ -417,7 +417,11 @@ impl World {
// TODO: if we have a panic, actually add the panic message to the console
let result = self.active_result().clone().unwrap();
self.result = Some(result.clone());
outbox.push(MsgOut::Complete(result));
let result_msg = match result {
Ok(value) => MsgOut::Complete(Value::string(value.show())),
Err(_msg) => MsgOut::Error("Ludus panicked!".to_string())
};
outbox.push(result_msg);
outbox
}
@ -470,7 +474,7 @@ impl World {
self.maybe_do_io().await;
if self.kill_signal {
let mut outbox = self.flush_buffers();
outbox.push(MsgOut::Complete(Err(Panic::Str("ludus killed by user"))));
outbox.push(MsgOut::Error("Ludus killed by user".to_string()));
do_io(outbox).await;
return;
}