1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
//!
//! Helper trait for managing options struct which extends [Object](js_sys::Object)
//! ```
//! // create MyOptions struct
//!
//! #[wasm_bindgen]
//! extern "C" {
//!     #[wasm_bindgen(extends = js_sys::Object)]
//!     #[derive(Debug, Clone, PartialEq, Eq)]
//!     pub type MyOptions;
//! }
//!
//! impl workflow_wasm::options::OptionsTrait for MyOptions{}
//!
//! //impl methods as you need
//! impl MyOptions{
//!     /// Set title
//!     pub fn title(mut self, title:&str)->Self{
//!         self.set("title", JsValue::from(title))
//!     }
//!
//!     /// Set active
//!     pub fn active(mut self, active:bool)->Self{
//!         self.set("active", JsValue::from(active))
//!     }
//! }
//!
//! // use MyOptions
//!
//! let options = MyOptions::new()
//!     .title("title text")
//!     .active(true);
//!
//! ```
//!

use js_sys::Object;
use wasm_bindgen::prelude::*;

pub trait OptionsTrait {
    /// "Construct a new `Options`.
    ///
    fn new() -> Self
    where
        Self: wasm_bindgen::JsCast,
    {
        #[allow(unused_mut)]
        let mut ret: Self = ::wasm_bindgen::JsCast::unchecked_into(Object::new());
        ret = ret.initialize();
        ret
    }

    fn initialize(self) -> Self
    where
        Self: wasm_bindgen::JsCast,
    {
        self
    }

    fn set(self, mut key: &str, value: JsValue) -> Self
    where
        Self: wasm_bindgen::JsCast,
    {
        let mut target = self.as_ref().clone();

        if key.contains('.') {
            let mut name_parts: Vec<&str> = key.split('.').collect();
            key = name_parts.pop().unwrap();

            for name in name_parts {
                //log_info!("name: {}, target: {:?}", name, target);
                let r = ::js_sys::Reflect::get(&target, &JsValue::from(name));

                match r {
                    Ok(r) => {
                        if !r.is_undefined() {
                            target = r
                        } else {
                            let object = Object::new();
                            let new_target = JsValue::from(object);
                            //log_info!("new_target: {:?}", new_target);
                            let _ =
                                ::js_sys::Reflect::set(&target, &JsValue::from(name), &new_target);

                            target = new_target;
                        }
                    }
                    Err(err) => {
                        panic!(
                            "OptionsExt::set(): unable to find property `{name}`, err: {:?}",
                            err
                        );
                    }
                }
            }

            //log_info!("final: key: {}, target: {:?}", key, target);
        }

        let r = ::js_sys::Reflect::set(&target, &JsValue::from(key), &value);
        debug_assert!(
            r.is_ok(),
            "setting properties should never fail on our dictionary objects"
        );
        let _ = r;

        self
    }
}