Skip to main content

workflow_wasm/
options.rs

1//!
2//! Helper trait for managing options struct which extends [Object](js_sys::Object)
3//! ```ignore
4//! # use wasm_bindgen::JsValue;
5//! # use crate::workflow_wasm::options::OptionsTrait;
6//! // create MyOptions struct
7//!
8//! #[wasm_bindgen]
9//! extern "C" {
10//!     #[derive(Debug, Clone, PartialEq, Eq)]
11//!     #[wasm_bindgen(extends = js_sys::Object)]
12//!     pub type MyOptions;
13//! }
14//!
15//!
16//! impl OptionsTrait for MyOptions{}
17//!
18//! //impl methods as you need
19//! impl MyOptions{
20//!     /// Set title
21//!     pub fn title(self, title:&str)->Self{
22//!         self.set("title", JsValue::from(title))
23//!     }
24//!
25//!     /// Set active
26//!     pub fn active(self, active:bool)->Self{
27//!         self.set("active", JsValue::from(active))
28//!     }
29//! }
30//!
31//! // use MyOptions
32//!
33//! let options = MyOptions::new()
34//!     .title("title text")
35//!     .active(true);
36//!
37//! ```
38//!
39
40use js_sys::Object;
41use wasm_bindgen::prelude::*;
42
43/// Trait for building JavaScript options objects, providing convenience
44/// methods to construct an empty object and set (optionally nested) values
45/// on it in a builder style.
46pub trait OptionsTrait {
47    /// "Construct a new `Options`.
48    ///
49    fn new() -> Self
50    where
51        Self: wasm_bindgen::JsCast,
52    {
53        #[allow(unused_mut)]
54        let mut ret: Self = ::wasm_bindgen::JsCast::unchecked_into(Object::new());
55        ret = ret.initialize();
56        ret
57    }
58
59    /// Hook for initializing a freshly constructed options object. The
60    /// default implementation returns `self` unchanged; implementors may
61    /// override it to set default values.
62    fn initialize(self) -> Self
63    where
64        Self: wasm_bindgen::JsCast,
65    {
66        self
67    }
68
69    /// Sets a value on the options object under the given key. Dot-separated
70    /// keys (e.g. `"a.b.c"`) traverse and create nested objects as needed.
71    /// Returns `self` to allow builder-style chaining.
72    fn set(self, mut key: &str, value: JsValue) -> Self
73    where
74        Self: wasm_bindgen::JsCast,
75    {
76        let mut target = self.as_ref().clone();
77
78        if key.contains('.') {
79            let mut name_parts: Vec<&str> = key.split('.').collect();
80            key = name_parts.pop().unwrap();
81
82            for name in name_parts {
83                //log_info!("name: {}, target: {:?}", name, target);
84                let r = ::js_sys::Reflect::get(&target, &JsValue::from(name));
85
86                match r {
87                    Ok(r) => {
88                        if !r.is_undefined() {
89                            target = r
90                        } else {
91                            let object = Object::new();
92                            let new_target = JsValue::from(object);
93                            //log_info!("new_target: {:?}", new_target);
94                            let _ =
95                                ::js_sys::Reflect::set(&target, &JsValue::from(name), &new_target);
96
97                            target = new_target;
98                        }
99                    }
100                    Err(err) => {
101                        panic!("OptionsExt::set(): unable to find property `{name}`, err: {err:?}");
102                    }
103                }
104            }
105
106            //log_info!("final: key: {}, target: {:?}", key, target);
107        }
108
109        let r = ::js_sys::Reflect::set(&target, &JsValue::from(key), &value);
110        debug_assert!(
111            r.is_ok(),
112            "setting properties should never fail on our dictionary objects"
113        );
114        let _ = r;
115
116        self
117    }
118}
119
120/*
121#[cfg(test)]
122mod test{
123    use super::*;
124    use crate as workflow_wasm;
125    #[test]
126    fn test(){
127        #[wasm_bindgen]
128        extern "C" {
129            #[wasm_bindgen(extends = js_sys::Object)]
130            #[derive(Debug, Clone, PartialEq, Eq)]
131            pub type MyOptions;
132        }
133
134        impl workflow_wasm::options::OptionsTrait for MyOptions{}
135
136        //impl methods as you need
137        impl MyOptions{
138            /// Set title
139            pub fn title(self, title:&str)->Self{
140                self.set("title", JsValue::from(title))
141            }
142
143            /// Set active
144            pub fn active(self, active:bool)->Self{
145                self.set("active", JsValue::from(active))
146            }
147        }
148
149        // use MyOptions
150
151        let options = MyOptions::new()
152            .title("title text")
153            .active(true);
154    }
155}
156
157*/