Skip to main content

workflow_wasm/extensions/
object.rs

1//!
2//! Js [`Object`] property access utilities
3//!
4
5use crate::convert::*;
6use crate::error::Error;
7use crate::extensions::jsvalue::JsValueExtension;
8use crate::utils::*;
9use js_sys::{Object, Reflect};
10use wasm_bindgen::prelude::*;
11
12/// Custom trait implementing simplified property accessor functions for [`Object`].
13pub trait ObjectExtension {
14    /// Get a type that implements [`TryFrom<JsValue>`] from a property of the [`Object`].
15    fn get<T>(&self, prop: &str) -> Result<T, Error>
16    where
17        T: TryFrom<JsValue>,
18        <T as TryFrom<wasm_bindgen::JsValue>>::Error: std::fmt::Display;
19
20    /// Try to get a type that implements [`TryFrom<JsValue>`] from a property of the [`Object`].
21    /// Returns `Ok(None)` if the property does not exist.
22    fn try_get<T>(&self, prop: &str) -> Result<Option<T>, Error>
23    where
24        T: TryFrom<JsValue>,
25        <T as TryFrom<wasm_bindgen::JsValue>>::Error: std::fmt::Display;
26
27    /// Obtain a [`Cast`] from a property of the [`Object`].
28    fn cast_into<T>(&self, prop: &str) -> Result<T, Error>
29    where
30        T: TryCastFromJs,
31        <T as TryCastFromJs>::Error: std::fmt::Display;
32
33    /// Obtain an owned-reference [`Cast`] captured from a property of the [`Object`].
34    fn cast_from<T>(&self, prop: &str) -> Result<Cast<'static, T>, Error>
35    where
36        T: TryCastFromJs,
37        <T as TryCastFromJs>::Error: std::fmt::Display;
38
39    /// Try to obtain a [`Cast`] from a property of the [`Object`].
40    /// Returns `Ok(None)` if the property does not exist (`null` or `undefined`).
41    fn try_cast_into<T>(&self, prop: &str) -> Result<Option<T>, Error>
42    where
43        T: TryCastFromJs,
44        <T as TryCastFromJs>::Error: std::fmt::Display;
45
46    /// Try to obtain an owned-reference [`Cast`] captured from a property of the [`Object`].
47    /// Returns `Ok(None)` if the property does not exist.
48    fn try_cast_from<T>(&self, prop: &str) -> Result<Option<Cast<'static, T>>, Error>
49    where
50        T: TryCastFromJs,
51        <T as TryCastFromJs>::Error: std::fmt::Display;
52
53    /// Get `JsValue` property
54    fn get_value(&self, prop: &str) -> Result<JsValue, Error>;
55    /// Get `Object` property
56    fn get_object(&self, prop: &str) -> Result<Object, Error>;
57    /// Get `Object` property
58    fn try_get_object(&self, prop: &str) -> Result<Option<Object>, Error>;
59    /// Try Get `JsValue` property
60    fn try_get_value(&self, prop: &str) -> Result<Option<JsValue>, Error>;
61    /// get `String` property
62    fn get_string(&self, prop: &str) -> Result<String, Error>;
63    /// get `String` property
64    fn try_get_string(&self, prop: &str) -> Result<Option<String>, Error>;
65    /// get `Number` property as `u8`
66    fn get_u8(&self, prop: &str) -> Result<u8, Error>;
67    /// get `Number` property as `u16`
68    fn get_u16(&self, prop: &str) -> Result<u16, Error>;
69    /// get `Number` property as `u32`
70    fn get_u32(&self, prop: &str) -> Result<u32, Error>;
71    /// get `Number` property as `u64`
72    fn get_u64(&self, prop: &str) -> Result<u64, Error>;
73    /// get `Number` property as `f64`
74    fn get_f64(&self, prop: &str) -> Result<f64, Error>;
75    /// get `Boolean` property as `bool`
76    fn get_bool(&self, prop: &str) -> Result<bool, Error>;
77    /// try get `Boolean` property as `bool`, returning `None` if absent or not a boolean
78    fn try_get_bool(&self, prop: &str) -> Result<Option<bool>, Error>;
79    /// get property as `Vec<JsValue>`
80    fn get_vec(&self, prop: &str) -> Result<Vec<JsValue>, Error>;
81    /// get `Vec<u8>` property from a hex string or an `Array`
82    fn get_vec_u8(&self, prop: &str) -> Result<Vec<u8>, Error>;
83    /// get `Uint8Array` property as `Vec<u8>`
84    fn get_vec_u8_from_number_array(&self, prop: &str) -> Result<Vec<u8>, Error>;
85    /// get `Uint8Array` property as `Vec<u8>`
86    fn get_vec_u8_from_uint8_array(&self, prop: &str) -> Result<Vec<u8>, Error>;
87    /// set `JsValue` property
88    fn set(&self, prop: &str, value: &JsValue) -> Result<bool, Error>;
89    /// set `Array` property from `&[JsValue]`
90    fn set_vec(&self, prop: &str, values: &[JsValue]) -> Result<bool, Error>;
91    /// set multiple `JsValue` properties
92    fn set_properties(&self, props: &[(&str, &JsValue)]) -> Result<(), Error>;
93    /// delete property
94    fn delete(&self, prop: &str) -> Result<bool, Error>;
95}
96
97impl ObjectExtension for Object {
98    fn get<T>(&self, prop: &str) -> Result<T, Error>
99    where
100        T: TryFrom<JsValue>,
101        <T as TryFrom<wasm_bindgen::JsValue>>::Error: std::fmt::Display,
102    {
103        let js_value = Reflect::get(self, &JsValue::from(prop))?;
104        T::try_from(js_value).map_err(Error::custom)
105    }
106
107    fn try_get<T>(&self, prop: &str) -> Result<Option<T>, Error>
108    where
109        T: TryFrom<JsValue>,
110        <T as TryFrom<wasm_bindgen::JsValue>>::Error: std::fmt::Display,
111    {
112        let js_value = Reflect::get(self, &JsValue::from(prop))?;
113        if js_value.is_undefined() {
114            Ok(None)
115        } else {
116            Ok(Some(T::try_from(js_value).map_err(Error::custom)?))
117        }
118    }
119
120    fn cast_into<T>(&self, prop: &str) -> Result<T, Error>
121    where
122        T: TryCastFromJs,
123        <T as TryCastFromJs>::Error: std::fmt::Display,
124    {
125        let js_value = Reflect::get(self, &JsValue::from(prop))?;
126        T::try_owned_from(&js_value).map_err(Error::custom)
127    }
128
129    fn cast_from<T>(&self, prop: &str) -> Result<Cast<'static, T>, Error>
130    where
131        T: TryCastFromJs,
132        <T as TryCastFromJs>::Error: std::fmt::Display,
133    {
134        let js_value = Reflect::get(self, &JsValue::from(prop))?;
135        T::try_captured_cast_from(&js_value).map_err(Error::custom)
136    }
137
138    fn try_cast_into<T>(&self, prop: &str) -> Result<Option<T>, Error>
139    where
140        T: TryCastFromJs,
141        <T as TryCastFromJs>::Error: std::fmt::Display,
142    {
143        let js_value = Reflect::get(self, &JsValue::from(prop))?;
144        if js_value.is_undefined() {
145            Ok(None)
146        } else {
147            Ok(Some(T::try_owned_from(js_value).map_err(Error::custom)?))
148        }
149    }
150
151    fn try_cast_from<T>(&self, prop: &str) -> Result<Option<Cast<'static, T>>, Error>
152    where
153        T: TryCastFromJs,
154        <T as TryCastFromJs>::Error: std::fmt::Display,
155    {
156        let js_value = Reflect::get(self, &JsValue::from(prop))?;
157        if js_value.is_undefined() {
158            Ok(None)
159        } else {
160            Ok(Some(
161                T::try_captured_cast_from(js_value).map_err(Error::custom)?,
162            ))
163        }
164    }
165
166    fn get_value(&self, prop: &str) -> Result<JsValue, Error> {
167        Ok(Reflect::get(self, &JsValue::from(prop))?)
168    }
169
170    fn get_object(&self, prop: &str) -> Result<Object, Error> {
171        let value = Reflect::get(self, &JsValue::from(prop))?;
172        let object = Object::try_from(&value).ok_or(Error::MissingProperty(prop.to_string()))?;
173        Ok(object.clone())
174    }
175
176    fn try_get_object(&self, prop: &str) -> Result<Option<Object>, Error> {
177        let value = Reflect::get(self, &JsValue::from(prop))?;
178        Ok(Object::try_from(&value).cloned())
179    }
180
181    fn try_get_value(&self, prop: &str) -> Result<Option<JsValue>, Error> {
182        let js_value = Reflect::get(self, &JsValue::from(prop))?;
183        if js_value == JsValue::UNDEFINED {
184            Ok(None)
185        } else {
186            Ok(Some(js_value))
187        }
188    }
189
190    fn get_string(&self, prop: &str) -> Result<String, Error> {
191        try_get_string_from_prop(self, prop)
192    }
193
194    fn try_get_string(&self, prop: &str) -> Result<Option<String>, Error> {
195        Ok(self.get_value(prop)?.as_string())
196    }
197
198    fn get_bool(&self, prop: &str) -> Result<bool, Error> {
199        try_get_bool_from_prop(self, prop)
200    }
201
202    fn try_get_bool(&self, prop: &str) -> Result<Option<bool>, Error> {
203        Ok(self.get_value(prop)?.as_bool())
204    }
205
206    fn get_u8(&self, prop: &str) -> Result<u8, Error> {
207        try_get_u8_from_prop(self, prop)
208    }
209
210    fn get_u16(&self, prop: &str) -> Result<u16, Error> {
211        try_get_u16_from_prop(self, prop)
212    }
213
214    fn get_u32(&self, prop: &str) -> Result<u32, Error> {
215        try_get_u32_from_prop(self, prop)
216    }
217
218    fn get_u64(&self, prop: &str) -> Result<u64, Error> {
219        try_get_u64_from_prop(self, prop)
220    }
221
222    fn get_vec(&self, prop: &str) -> Result<Vec<JsValue>, Error> {
223        try_get_vec_from_prop(self, prop)
224    }
225
226    fn get_vec_u8(&self, prop: &str) -> Result<Vec<u8>, Error> {
227        let v = Reflect::get(self, &JsValue::from(prop))?;
228        v.try_as_vec_u8()
229    }
230
231    fn get_vec_u8_from_number_array(&self, prop: &str) -> Result<Vec<u8>, Error> {
232        try_get_vec_u8_from_number_array_prop(self, prop)
233    }
234
235    fn get_vec_u8_from_uint8_array(&self, prop: &str) -> Result<Vec<u8>, Error> {
236        try_get_vec_u8_from_uint8_array_prop(self, prop)
237    }
238
239    fn get_f64(&self, prop: &str) -> Result<f64, Error> {
240        try_get_f64_from_prop(self, prop)
241    }
242
243    fn set(&self, prop: &str, value: &JsValue) -> Result<bool, Error> {
244        Ok(Reflect::set(self, &JsValue::from(prop), value)?)
245    }
246
247    fn set_vec(&self, prop: &str, values: &[JsValue]) -> Result<bool, Error> {
248        let array = js_sys::Array::new();
249        for v in values {
250            array.push(v);
251        }
252        Ok(Reflect::set(self, &JsValue::from(prop), &array)?)
253    }
254
255    fn set_properties(&self, props: &[(&str, &JsValue)]) -> Result<(), Error> {
256        for (k, v) in props.iter() {
257            Reflect::set(self, &JsValue::from(*k), v)?;
258        }
259        Ok(())
260    }
261
262    fn delete(&self, prop: &str) -> Result<bool, Error> {
263        Ok(Reflect::delete_property(self, &JsValue::from(prop))?)
264    }
265}