mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-16 14:58:05 +08:00
* Make wasm wrapper not depend on editor on native * Introduce pkg-native * Fix tests * Fix fmt * Correct span and make cargo fmt work inside editor_commands block * Review * Fix lint
449 lines
12 KiB
Rust
449 lines
12 KiB
Rust
//! 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<u8>),
|
|
Seq(Vec<WasmValue>),
|
|
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<NonFinite> for f64 {
|
|
fn from(value: NonFinite) -> Self {
|
|
match value {
|
|
NonFinite::Nan => f64::NAN,
|
|
NonFinite::PositiveInfinity => f64::INFINITY,
|
|
NonFinite::NegativeInfinity => f64::NEG_INFINITY,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<WasmValue> 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<T: Serialize + ?Sized>(value: &T) -> Result<String, serde_json::Error> {
|
|
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<T: Serialize + ?Sized>(value: &T) -> Result<WasmValue, Error> {
|
|
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<WasmValue, Error> {
|
|
Ok(WasmValue::Bool(v))
|
|
}
|
|
|
|
fn serialize_i8(self, v: i8) -> Result<WasmValue, Error> {
|
|
Ok(WasmValue::Number(v as f64))
|
|
}
|
|
|
|
fn serialize_i16(self, v: i16) -> Result<WasmValue, Error> {
|
|
Ok(WasmValue::Number(v as f64))
|
|
}
|
|
|
|
fn serialize_i32(self, v: i32) -> Result<WasmValue, Error> {
|
|
Ok(WasmValue::Number(v as f64))
|
|
}
|
|
|
|
fn serialize_i64(self, v: i64) -> Result<WasmValue, Error> {
|
|
Ok(WasmValue::Int(v))
|
|
}
|
|
|
|
fn serialize_i128(self, v: i128) -> Result<WasmValue, Error> {
|
|
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<WasmValue, Error> {
|
|
Ok(WasmValue::Number(v as f64))
|
|
}
|
|
|
|
fn serialize_u16(self, v: u16) -> Result<WasmValue, Error> {
|
|
Ok(WasmValue::Number(v as f64))
|
|
}
|
|
|
|
fn serialize_u32(self, v: u32) -> Result<WasmValue, Error> {
|
|
Ok(WasmValue::Number(v as f64))
|
|
}
|
|
|
|
fn serialize_u64(self, v: u64) -> Result<WasmValue, Error> {
|
|
Ok(WasmValue::UInt(v))
|
|
}
|
|
|
|
fn serialize_u128(self, v: u128) -> Result<WasmValue, Error> {
|
|
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<WasmValue, Error> {
|
|
self.serialize_f64(v as f64)
|
|
}
|
|
|
|
fn serialize_f64(self, v: f64) -> Result<WasmValue, Error> {
|
|
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<WasmValue, Error> {
|
|
Ok(WasmValue::String(v.to_string()))
|
|
}
|
|
|
|
fn serialize_str(self, v: &str) -> Result<WasmValue, Error> {
|
|
Ok(WasmValue::String(v.to_string()))
|
|
}
|
|
|
|
fn serialize_bytes(self, v: &[u8]) -> Result<WasmValue, Error> {
|
|
Ok(WasmValue::Bytes(v.to_vec()))
|
|
}
|
|
|
|
fn serialize_none(self) -> Result<WasmValue, Error> {
|
|
Ok(WasmValue::Undefined)
|
|
}
|
|
|
|
fn serialize_some<T: Serialize + ?Sized>(self, value: &T) -> Result<WasmValue, Error> {
|
|
value.serialize(self)
|
|
}
|
|
|
|
fn serialize_unit(self) -> Result<WasmValue, Error> {
|
|
Ok(WasmValue::Undefined)
|
|
}
|
|
|
|
fn serialize_unit_struct(self, _name: &'static str) -> Result<WasmValue, Error> {
|
|
Ok(WasmValue::Undefined)
|
|
}
|
|
|
|
fn serialize_unit_variant(self, _name: &'static str, _variant_index: u32, variant: &'static str) -> Result<WasmValue, Error> {
|
|
Ok(WasmValue::String(variant.to_string()))
|
|
}
|
|
|
|
fn serialize_newtype_struct<T: Serialize + ?Sized>(self, _name: &'static str, value: &T) -> Result<WasmValue, Error> {
|
|
value.serialize(self)
|
|
}
|
|
|
|
fn serialize_newtype_variant<T: Serialize + ?Sized>(self, _name: &'static str, _variant_index: u32, variant: &'static str, value: &T) -> Result<WasmValue, Error> {
|
|
Ok(WasmValue::Object(vec![(variant.to_string(), value.serialize(ValueSerializer)?)]))
|
|
}
|
|
|
|
fn serialize_seq(self, len: Option<usize>) -> Result<SeqSerializer, Error> {
|
|
Ok(SeqSerializer(Vec::with_capacity(len.unwrap_or_default())))
|
|
}
|
|
|
|
fn serialize_tuple(self, len: usize) -> Result<SeqSerializer, Error> {
|
|
self.serialize_seq(Some(len))
|
|
}
|
|
|
|
fn serialize_tuple_struct(self, _name: &'static str, len: usize) -> Result<SeqSerializer, Error> {
|
|
self.serialize_seq(Some(len))
|
|
}
|
|
|
|
fn serialize_tuple_variant(self, _name: &'static str, _variant_index: u32, variant: &'static str, len: usize) -> Result<VariantSeqSerializer, Error> {
|
|
Ok(VariantSeqSerializer {
|
|
variant,
|
|
elements: Vec::with_capacity(len),
|
|
})
|
|
}
|
|
|
|
fn serialize_map(self, len: Option<usize>) -> Result<MapSerializer, Error> {
|
|
Ok(MapSerializer {
|
|
entries: Vec::with_capacity(len.unwrap_or_default()),
|
|
pending_key: None,
|
|
})
|
|
}
|
|
|
|
fn serialize_struct(self, _name: &'static str, len: usize) -> Result<ObjectSerializer, Error> {
|
|
Ok(ObjectSerializer(Vec::with_capacity(len)))
|
|
}
|
|
|
|
fn serialize_struct_variant(self, _name: &'static str, _variant_index: u32, variant: &'static str, len: usize) -> Result<VariantObjectSerializer, Error> {
|
|
Ok(VariantObjectSerializer {
|
|
variant,
|
|
fields: Vec::with_capacity(len),
|
|
})
|
|
}
|
|
}
|
|
|
|
struct SeqSerializer(Vec<WasmValue>);
|
|
|
|
impl ser::SerializeSeq for SeqSerializer {
|
|
type Ok = WasmValue;
|
|
type Error = Error;
|
|
|
|
fn serialize_element<T: Serialize + ?Sized>(&mut self, value: &T) -> Result<(), Error> {
|
|
self.0.push(value.serialize(ValueSerializer)?);
|
|
Ok(())
|
|
}
|
|
|
|
fn end(self) -> Result<WasmValue, Error> {
|
|
Ok(WasmValue::Seq(self.0))
|
|
}
|
|
}
|
|
|
|
impl ser::SerializeTuple for SeqSerializer {
|
|
type Ok = WasmValue;
|
|
type Error = Error;
|
|
|
|
fn serialize_element<T: Serialize + ?Sized>(&mut self, value: &T) -> Result<(), Error> {
|
|
ser::SerializeSeq::serialize_element(self, value)
|
|
}
|
|
|
|
fn end(self) -> Result<WasmValue, Error> {
|
|
ser::SerializeSeq::end(self)
|
|
}
|
|
}
|
|
|
|
impl ser::SerializeTupleStruct for SeqSerializer {
|
|
type Ok = WasmValue;
|
|
type Error = Error;
|
|
|
|
fn serialize_field<T: Serialize + ?Sized>(&mut self, value: &T) -> Result<(), Error> {
|
|
ser::SerializeSeq::serialize_element(self, value)
|
|
}
|
|
|
|
fn end(self) -> Result<WasmValue, Error> {
|
|
ser::SerializeSeq::end(self)
|
|
}
|
|
}
|
|
|
|
struct VariantSeqSerializer {
|
|
variant: &'static str,
|
|
elements: Vec<WasmValue>,
|
|
}
|
|
|
|
impl ser::SerializeTupleVariant for VariantSeqSerializer {
|
|
type Ok = WasmValue;
|
|
type Error = Error;
|
|
|
|
fn serialize_field<T: Serialize + ?Sized>(&mut self, value: &T) -> Result<(), Error> {
|
|
self.elements.push(value.serialize(ValueSerializer)?);
|
|
Ok(())
|
|
}
|
|
|
|
fn end(self) -> Result<WasmValue, Error> {
|
|
Ok(WasmValue::Object(vec![(self.variant.to_string(), WasmValue::Seq(self.elements))]))
|
|
}
|
|
}
|
|
|
|
struct MapSerializer {
|
|
entries: Vec<(WasmValue, WasmValue)>,
|
|
pending_key: Option<WasmValue>,
|
|
}
|
|
|
|
impl ser::SerializeMap for MapSerializer {
|
|
type Ok = WasmValue;
|
|
type Error = Error;
|
|
|
|
fn serialize_key<T: Serialize + ?Sized>(&mut self, key: &T) -> Result<(), Error> {
|
|
self.pending_key = Some(key.serialize(ValueSerializer)?);
|
|
Ok(())
|
|
}
|
|
|
|
fn serialize_value<T: Serialize + ?Sized>(&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<WasmValue, Error> {
|
|
Ok(WasmValue::Map(self.entries))
|
|
}
|
|
}
|
|
|
|
struct ObjectSerializer(Vec<(String, WasmValue)>);
|
|
|
|
impl ser::SerializeStruct for ObjectSerializer {
|
|
type Ok = WasmValue;
|
|
type Error = Error;
|
|
|
|
fn serialize_field<T: Serialize + ?Sized>(&mut self, key: &'static str, value: &T) -> Result<(), Error> {
|
|
self.0.push((key.to_string(), value.serialize(ValueSerializer)?));
|
|
Ok(())
|
|
}
|
|
|
|
fn end(self) -> Result<WasmValue, Error> {
|
|
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<T: Serialize + ?Sized>(&mut self, key: &'static str, value: &T) -> Result<(), Error> {
|
|
self.fields.push((key.to_string(), value.serialize(ValueSerializer)?));
|
|
Ok(())
|
|
}
|
|
|
|
fn end(self) -> Result<WasmValue, Error> {
|
|
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<String>,
|
|
widths: std::collections::BTreeMap<u64, u32>,
|
|
flags: Vec<bool>,
|
|
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());
|
|
}
|
|
}
|
|
}
|