oxygengine_script_web/
scriptable.rs

1use 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}