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
use wasm_bindgen::prelude::*;
#[wasm_bindgen(typescript_custom_section)]
const TS_SECTION_VALUE: &str = r#"
type Value =
  {kind : "integer", value : bigint } |
  {kind : "double", value : number} |
  {kind : "bool", value : boolean } |
  {kind: "array", value : Value[] } |
  {kind: "string", value: string } |
  {kind: "duration", value: bigint } |
  {kind: "datetime", value: bigint }
"#;

#[wasm_bindgen(module = "/js/union.js")]
extern "C" {
    #[wasm_bindgen(js_name = "Value", typescript_type = "Value")]
    pub type UnionValue;

    #[wasm_bindgen(js_name = "asInt")]
    fn as_int(_: &UnionValue) -> Option<i64>;
    #[wasm_bindgen(js_name = "asDouble")]
    fn as_double(_: &UnionValue) -> Option<f64>;
    #[wasm_bindgen(js_name = "asBool")]
    fn as_bool(_: &UnionValue) -> Option<bool>;
    #[wasm_bindgen(js_name = "asArray")]
    fn as_array(_: &UnionValue) -> Option<Vec<UnionValue>>;
    #[wasm_bindgen(js_name = "asString")]
    fn as_string(_: &UnionValue) -> Option<std::string::String>;
    #[wasm_bindgen(js_name = "asDuration")]
    fn as_duration(_: &UnionValue) -> Option<i64>;
    #[wasm_bindgen(js_name = "asDateTime")]
    fn as_datetime(_: &UnionValue) -> Option<i64>;

    #[wasm_bindgen(js_name = "makeInt")]
    fn make_int(_: i64) -> UnionValue;
    #[wasm_bindgen(js_name = "makeDouble")]
    fn make_double(_: f64) -> UnionValue;
    #[wasm_bindgen(js_name = "makeBool")]
    fn make_bool(_: bool) -> UnionValue;
    #[wasm_bindgen(js_name = "makeArray")]
    fn make_array(_: Vec<UnionValue>) -> UnionValue;
    #[wasm_bindgen(js_name = "makeString")]
    fn make_string(_: std::string::String) -> UnionValue;
    #[wasm_bindgen(js_name = "makeDuration")]
    fn make_duration(_: i64) -> UnionValue;
    #[wasm_bindgen(js_name = "makeDateTime")]
    fn make_datetime(_: i64) -> UnionValue;
}

use crate::Value;
use Value::*;

impl From<UnionValue> for Value {
    fn from(v: UnionValue) -> Value {
        if let Some(v) = as_int(&v) {
            Integer(v)
        } else if let Some(v) = as_double(&v) {
            Double(v)
        } else if let Some(v) = as_bool(&v) {
            Bool(v)
        } else if let Some(vs) = as_array(&v) {
            let vs = vs.into_iter().map(Into::into).collect();
            Array(vs)
        } else if let Some(v) = as_string(&v) {
            String(v)
        } else if let Some(v) = as_duration(&v) {
            Duration(chrono::Duration::milliseconds(v))
        } else if let Some(v) = as_datetime(&v) {
            DateTime(chrono::DateTime::UNIX_EPOCH + chrono::Duration::milliseconds(v))
        } else {
            unreachable!()
        }
    }
}

impl From<Value> for UnionValue {
    fn from(v: Value) -> UnionValue {
        match v {
            Integer(v) => make_int(v),
            Double(v) => make_double(v),
            Bool(v) => make_bool(v),
            Array(vs) => {
                let vs = vs.into_iter().map(Into::into).collect();
                make_array(vs)
            }
            String(v) => make_string(v),
            Duration(v) => make_duration(v.num_milliseconds()),
            DateTime(v) => make_datetime(v.timestamp_millis()),
        }
    }
}