mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-27 12:18:11 +08:00
Desktop: Stop compiling the editor into the wasm wrapper (#4348)
* 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
This commit is contained in:
@@ -0,0 +1,448 @@
|
||||
//! 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());
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user