oxygengine_script_web/
scriptable.rs1use js_sys::{Array, Object, Reflect};
2use serde::{de::DeserializeOwned, Serialize};
3use wasm_bindgen::JsValue;
4
5pub use serde_json::{
6 json, map::Map as ScriptableMap, value::Value as ScriptableValue, Number as ScriptableNumber,
7};
8
9#[derive(Debug, Clone)]
10pub enum ScriptableError {
11 CouldNotSerialize,
12 CouldNotDeserialize,
13}
14
15pub trait Scriptable {
16 fn from_scriptable(_data: &ScriptableValue) -> Result<Self, ScriptableError>
17 where
18 Self: Sized,
19 {
20 Err(ScriptableError::CouldNotDeserialize)
21 }
22
23 fn to_scriptable(&self) -> Result<ScriptableValue, ScriptableError> {
24 Err(ScriptableError::CouldNotSerialize)
25 }
26
27 fn from_js(js: JsValue) -> Result<Self, ScriptableError>
28 where
29 Self: Sized,
30 {
31 let value = scriptable_js_to_value(js)?;
32 Self::from_scriptable(&value)
33 }
34
35 fn to_js(&self) -> Result<JsValue, ScriptableError> {
36 if let Ok(value) = self.to_scriptable() {
37 scriptable_value_to_js(&value)
38 } else {
39 Err(ScriptableError::CouldNotSerialize)
40 }
41 }
42}
43
44impl<T> Scriptable for T
45where
46 T: Serialize + DeserializeOwned + Clone,
47{
48 fn from_scriptable(data: &ScriptableValue) -> Result<Self, ScriptableError> {
49 if let Ok(r) = serde_json::from_value::<T>(data.clone()) {
50 Ok(r)
51 } else {
52 Err(ScriptableError::CouldNotDeserialize)
53 }
54 }
55
56 fn to_scriptable(&self) -> Result<ScriptableValue, ScriptableError> {
57 if let Ok(v) = serde_json::to_value::<T>(self.clone()) {
58 Ok(v)
59 } else {
60 Err(ScriptableError::CouldNotSerialize)
61 }
62 }
63}
64
65pub fn scriptable_value_to_js(value: &ScriptableValue) -> Result<JsValue, ScriptableError> {
66 match value {
67 ScriptableValue::Null => Ok(JsValue::NULL),
68 ScriptableValue::Bool(v) => Ok(JsValue::from_bool(*v)),
69 ScriptableValue::Number(v) => {
70 if let Some(v) = v.as_f64() {
71 Ok(JsValue::from_f64(v))
72 } else {
73 Err(ScriptableError::CouldNotSerialize)
74 }
75 }
76 ScriptableValue::String(v) => Ok(JsValue::from_str(v)),
77 ScriptableValue::Array(v) => scriptable_array_to_js(v),
78 ScriptableValue::Object(v) => scriptable_map_to_js(v),
79 }
80}
81
82pub fn scriptable_js_to_value(js: JsValue) -> Result<ScriptableValue, ScriptableError> {
83 if js.is_null() || js.is_undefined() {
84 Ok(ScriptableValue::Null)
85 } else if let Some(v) = js.as_bool() {
86 Ok(ScriptableValue::Bool(v))
87 } else if let Some(v) = js.as_f64() {
88 if v.fract().abs() > 1e-6 {
89 if let Some(v) = ScriptableNumber::from_f64(v) {
90 Ok(ScriptableValue::Number(v))
91 } else {
92 Err(ScriptableError::CouldNotDeserialize)
93 }
94 } else if let Some(v) = num::NumCast::from(v) {
95 let v: u64 = v;
96 Ok(ScriptableValue::Number(v.into()))
97 } else if let Some(v) = num::NumCast::from(v) {
98 let v: i64 = v;
99 Ok(ScriptableValue::Number(v.into()))
100 } else if let Some(v) = ScriptableNumber::from_f64(v) {
101 Ok(ScriptableValue::Number(v))
102 } else {
103 Err(ScriptableError::CouldNotDeserialize)
104 }
105 } else if let Some(v) = js.as_string() {
106 Ok(ScriptableValue::String(v))
107 } else if Array::is_array(&js) {
108 scriptable_js_to_array(js)
109 } else if js.is_object() {
110 scriptable_js_to_map(js)
111 } else {
112 Err(ScriptableError::CouldNotDeserialize)
113 }
114}
115
116pub fn scriptable_value_merge(a: &ScriptableValue, b: &ScriptableValue) -> ScriptableValue {
117 if a.is_object() && b.is_object() {
118 let a = a.as_object().unwrap();
119 let b = b.as_object().unwrap();
120 let mut r = a.clone();
121 for (k, v) in b {
122 if let Some(x) = a.get(k) {
123 let v = scriptable_value_merge(x, v);
124 r.insert(k.clone(), v);
125 } else {
126 r.insert(k.clone(), v.clone());
127 }
128 }
129 ScriptableValue::Object(r)
130 } else {
131 b.clone()
132 }
133}
134
135fn scriptable_array_to_js(value: &[ScriptableValue]) -> Result<JsValue, ScriptableError> {
136 let result = Array::new_with_length(value.len() as u32);
137 for (i, item) in value.iter().enumerate() {
138 result.set(i as u32, scriptable_value_to_js(item)?);
139 }
140 Ok(result.into())
141}
142
143fn scriptable_js_to_array(js: JsValue) -> Result<ScriptableValue, ScriptableError> {
144 let items = Array::from(&js)
145 .iter()
146 .map(scriptable_js_to_value)
147 .collect::<Result<Vec<_>, _>>()?;
148 Ok(ScriptableValue::Array(items))
149}
150
151fn scriptable_map_to_js(
152 value: &ScriptableMap<String, ScriptableValue>,
153) -> Result<JsValue, ScriptableError> {
154 let result: JsValue = Object::new().into();
155 for (key, value) in value {
156 if Reflect::set(
157 &result,
158 &JsValue::from_str(key),
159 &scriptable_value_to_js(value)?,
160 )
161 .is_err()
162 {
163 return Err(ScriptableError::CouldNotSerialize);
164 }
165 }
166 Ok(result)
167}
168
169fn scriptable_js_to_map(js: JsValue) -> Result<ScriptableValue, ScriptableError> {
170 if let Ok(keys) = Reflect::own_keys(&js) {
171 let items = keys
172 .iter()
173 .map(|key| {
174 if let Ok(v) = Reflect::get(&js, &key) {
175 Ok((key.as_string().unwrap(), scriptable_js_to_value(v)?))
176 } else {
177 Err(ScriptableError::CouldNotDeserialize)
178 }
179 })
180 .collect::<Result<ScriptableMap<_, _>, _>>()?;
181 Ok(ScriptableValue::Object(items))
182 } else {
183 Err(ScriptableError::CouldNotDeserialize)
184 }
185}