//! Serializable stand-in for `JsValue`, used to send `FrontendMessage`s from the native editor process to JS. //! Keeps the types JSON can't represent (`BigInt`, `Uint8Array`, `Map`, `undefined`, `NaN`) so the wasm side //! can reconstruct the same `JsValue`s that `serde_wasm_bindgen` produces on web. use serde::{Deserialize, Serialize}; #[cfg(feature = "editor")] use serde_json::Error; use wasm_bindgen::JsValue; #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub(crate) enum WasmValue { Undefined, Bool(bool), Number(f64), NonFinite(NonFinite), Int(i64), UInt(u64), String(String), Bytes(Vec), Seq(Vec), Object(Vec<(String, WasmValue)>), Map(Vec<(WasmValue, WasmValue)>), } #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] pub(crate) enum NonFinite { Nan, PositiveInfinity, NegativeInfinity, } impl From for f64 { fn from(value: NonFinite) -> Self { match value { NonFinite::Nan => f64::NAN, NonFinite::PositiveInfinity => f64::INFINITY, NonFinite::NegativeInfinity => f64::NEG_INFINITY, } } } impl From for JsValue { fn from(value: WasmValue) -> Self { match value { WasmValue::Undefined => JsValue::UNDEFINED, WasmValue::Bool(value) => JsValue::from_bool(value), WasmValue::Number(value) => JsValue::from_f64(value), WasmValue::NonFinite(value) => JsValue::from_f64(value.into()), WasmValue::Int(value) => JsValue::from(value), WasmValue::UInt(value) => JsValue::from(value), WasmValue::String(value) => JsValue::from_str(&value), WasmValue::Bytes(bytes) => js_sys::Uint8Array::from(bytes.as_slice()).into(), WasmValue::Seq(values) => { let array = js_sys::Array::new(); for value in values { array.push(&value.into()); } array.into() } WasmValue::Object(fields) => { let object = js_sys::Object::new(); for (key, value) in fields { let _ = js_sys::Reflect::set(&object, &JsValue::from_str(&key), &value.into()); } object.into() } WasmValue::Map(entries) => { let map = js_sys::Map::new(); for (key, value) in entries { map.set(&key.into(), &value.into()); } map.into() } } } } #[cfg(feature = "editor")] pub(crate) fn encode(value: &T) -> Result { serde_json::to_string(&ser::to_value(value)?) } #[cfg(feature = "editor")] mod ser { use super::*; use serde::ser::{self, Error as _}; pub(super) fn to_value(value: &T) -> Result { value.serialize(ValueSerializer) } struct ValueSerializer; impl ser::Serializer for ValueSerializer { type Ok = WasmValue; type Error = Error; type SerializeSeq = SeqSerializer; type SerializeTuple = SeqSerializer; type SerializeTupleStruct = SeqSerializer; type SerializeTupleVariant = VariantSeqSerializer; type SerializeMap = MapSerializer; type SerializeStruct = ObjectSerializer; type SerializeStructVariant = VariantObjectSerializer; fn serialize_bool(self, v: bool) -> Result { Ok(WasmValue::Bool(v)) } fn serialize_i8(self, v: i8) -> Result { Ok(WasmValue::Number(v as f64)) } fn serialize_i16(self, v: i16) -> Result { Ok(WasmValue::Number(v as f64)) } fn serialize_i32(self, v: i32) -> Result { Ok(WasmValue::Number(v as f64)) } fn serialize_i64(self, v: i64) -> Result { Ok(WasmValue::Int(v)) } fn serialize_i128(self, v: i128) -> Result { if let Ok(v) = i64::try_from(v) { Ok(WasmValue::Int(v)) } else { u64::try_from(v) .map(WasmValue::UInt) .map_err(|_| Error::custom(format!("i128 value {v} exceeds the wire BigInt range"))) } } fn serialize_u8(self, v: u8) -> Result { Ok(WasmValue::Number(v as f64)) } fn serialize_u16(self, v: u16) -> Result { Ok(WasmValue::Number(v as f64)) } fn serialize_u32(self, v: u32) -> Result { Ok(WasmValue::Number(v as f64)) } fn serialize_u64(self, v: u64) -> Result { Ok(WasmValue::UInt(v)) } fn serialize_u128(self, v: u128) -> Result { u64::try_from(v) .map(WasmValue::UInt) .map_err(|_| Error::custom(format!("u128 value {v} exceeds the wire BigInt range"))) } fn serialize_f32(self, v: f32) -> Result { self.serialize_f64(v as f64) } fn serialize_f64(self, v: f64) -> Result { Ok(if v.is_finite() { WasmValue::Number(v) } else if v.is_nan() { WasmValue::NonFinite(NonFinite::Nan) } else if v > 0. { WasmValue::NonFinite(NonFinite::PositiveInfinity) } else { WasmValue::NonFinite(NonFinite::NegativeInfinity) }) } fn serialize_char(self, v: char) -> Result { Ok(WasmValue::String(v.to_string())) } fn serialize_str(self, v: &str) -> Result { Ok(WasmValue::String(v.to_string())) } fn serialize_bytes(self, v: &[u8]) -> Result { Ok(WasmValue::Bytes(v.to_vec())) } fn serialize_none(self) -> Result { Ok(WasmValue::Undefined) } fn serialize_some(self, value: &T) -> Result { value.serialize(self) } fn serialize_unit(self) -> Result { Ok(WasmValue::Undefined) } fn serialize_unit_struct(self, _name: &'static str) -> Result { Ok(WasmValue::Undefined) } fn serialize_unit_variant(self, _name: &'static str, _variant_index: u32, variant: &'static str) -> Result { Ok(WasmValue::String(variant.to_string())) } fn serialize_newtype_struct(self, _name: &'static str, value: &T) -> Result { value.serialize(self) } fn serialize_newtype_variant(self, _name: &'static str, _variant_index: u32, variant: &'static str, value: &T) -> Result { Ok(WasmValue::Object(vec![(variant.to_string(), value.serialize(ValueSerializer)?)])) } fn serialize_seq(self, len: Option) -> Result { Ok(SeqSerializer(Vec::with_capacity(len.unwrap_or_default()))) } fn serialize_tuple(self, len: usize) -> Result { self.serialize_seq(Some(len)) } fn serialize_tuple_struct(self, _name: &'static str, len: usize) -> Result { self.serialize_seq(Some(len)) } fn serialize_tuple_variant(self, _name: &'static str, _variant_index: u32, variant: &'static str, len: usize) -> Result { Ok(VariantSeqSerializer { variant, elements: Vec::with_capacity(len), }) } fn serialize_map(self, len: Option) -> Result { Ok(MapSerializer { entries: Vec::with_capacity(len.unwrap_or_default()), pending_key: None, }) } fn serialize_struct(self, _name: &'static str, len: usize) -> Result { Ok(ObjectSerializer(Vec::with_capacity(len))) } fn serialize_struct_variant(self, _name: &'static str, _variant_index: u32, variant: &'static str, len: usize) -> Result { Ok(VariantObjectSerializer { variant, fields: Vec::with_capacity(len), }) } } struct SeqSerializer(Vec); impl ser::SerializeSeq for SeqSerializer { type Ok = WasmValue; type Error = Error; fn serialize_element(&mut self, value: &T) -> Result<(), Error> { self.0.push(value.serialize(ValueSerializer)?); Ok(()) } fn end(self) -> Result { Ok(WasmValue::Seq(self.0)) } } impl ser::SerializeTuple for SeqSerializer { type Ok = WasmValue; type Error = Error; fn serialize_element(&mut self, value: &T) -> Result<(), Error> { ser::SerializeSeq::serialize_element(self, value) } fn end(self) -> Result { ser::SerializeSeq::end(self) } } impl ser::SerializeTupleStruct for SeqSerializer { type Ok = WasmValue; type Error = Error; fn serialize_field(&mut self, value: &T) -> Result<(), Error> { ser::SerializeSeq::serialize_element(self, value) } fn end(self) -> Result { ser::SerializeSeq::end(self) } } struct VariantSeqSerializer { variant: &'static str, elements: Vec, } impl ser::SerializeTupleVariant for VariantSeqSerializer { type Ok = WasmValue; type Error = Error; fn serialize_field(&mut self, value: &T) -> Result<(), Error> { self.elements.push(value.serialize(ValueSerializer)?); Ok(()) } fn end(self) -> Result { Ok(WasmValue::Object(vec![(self.variant.to_string(), WasmValue::Seq(self.elements))])) } } struct MapSerializer { entries: Vec<(WasmValue, WasmValue)>, pending_key: Option, } impl ser::SerializeMap for MapSerializer { type Ok = WasmValue; type Error = Error; fn serialize_key(&mut self, key: &T) -> Result<(), Error> { self.pending_key = Some(key.serialize(ValueSerializer)?); Ok(()) } fn serialize_value(&mut self, value: &T) -> Result<(), Error> { let key = self.pending_key.take().ok_or_else(|| Error::custom("Map value serialized without a key"))?; self.entries.push((key, value.serialize(ValueSerializer)?)); Ok(()) } fn end(self) -> Result { Ok(WasmValue::Map(self.entries)) } } struct ObjectSerializer(Vec<(String, WasmValue)>); impl ser::SerializeStruct for ObjectSerializer { type Ok = WasmValue; type Error = Error; fn serialize_field(&mut self, key: &'static str, value: &T) -> Result<(), Error> { self.0.push((key.to_string(), value.serialize(ValueSerializer)?)); Ok(()) } fn end(self) -> Result { Ok(WasmValue::Object(self.0)) } } struct VariantObjectSerializer { variant: &'static str, fields: Vec<(String, WasmValue)>, } impl ser::SerializeStructVariant for VariantObjectSerializer { type Ok = WasmValue; type Error = Error; fn serialize_field(&mut self, key: &'static str, value: &T) -> Result<(), Error> { self.fields.push((key.to_string(), value.serialize(ValueSerializer)?)); Ok(()) } fn end(self) -> Result { Ok(WasmValue::Object(vec![(self.variant.to_string(), WasmValue::Object(self.fields))])) } } #[cfg(test)] mod tests { use super::*; use crate::wasm_value::encode; use serde::Serialize; #[derive(Serialize)] #[serde(rename_all = "camelCase")] enum TestMessage { UnitVariant, StructVariant { #[serde(rename = "nodeId")] node_id: u64, width: u32, scale: f64, min: f64, max: f64, label: Option, widths: std::collections::BTreeMap, flags: Vec, bytes: serde_bytes::ByteBuf, }, } #[test] fn mirrors_serde_wasm_bindgen_representations() { assert_eq!(to_value(&TestMessage::UnitVariant).unwrap(), WasmValue::String("unitVariant".into())); let message = TestMessage::StructVariant { node_id: u64::MAX, width: 8, scale: f64::NAN, min: f64::NEG_INFINITY, max: f64::INFINITY, label: None, widths: [(42, 7)].into(), flags: vec![true, false], bytes: serde_bytes::ByteBuf::from(vec![0, 255]), }; let expected = WasmValue::Object(vec![( "structVariant".into(), WasmValue::Object(vec![ ("nodeId".into(), WasmValue::UInt(u64::MAX)), ("width".into(), WasmValue::Number(8.)), ("scale".into(), WasmValue::NonFinite(NonFinite::Nan)), ("min".into(), WasmValue::NonFinite(NonFinite::NegativeInfinity)), ("max".into(), WasmValue::NonFinite(NonFinite::PositiveInfinity)), ("label".into(), WasmValue::Undefined), ("widths".into(), WasmValue::Map(vec![(WasmValue::UInt(42), WasmValue::Number(7.))])), ("flags".into(), WasmValue::Seq(vec![WasmValue::Bool(true), WasmValue::Bool(false)])), ("bytes".into(), WasmValue::Bytes(vec![0, 255])), ]), )]); assert_eq!(to_value(&message).unwrap(), expected); } #[test] fn encode_decode_roundtrip() { let message = TestMessage::StructVariant { node_id: 1, width: 2, scale: f64::NAN, min: f64::NEG_INFINITY, max: 1.5, label: Some("hi".into()), widths: [(3, 4)].into(), flags: vec![true], bytes: serde_bytes::ByteBuf::from(vec![9]), }; let json: &str = &encode(&message).unwrap(); let decoded: WasmValue = serde_json::from_str(json).unwrap(); assert_eq!(decoded, to_value(&message).unwrap()); } } }