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*/