convert_js/
option_like.rs

1use wasm_bindgen::{JsCast, JsValue};
2
3use crate::{FromJs, ToJs, WrapJsCast};
4
5macro_rules! def_option_like {
6    (
7        $name:ident {
8            None = $name_none:ident = $js_none_doc:literal $js_none:expr,
9            condition = |$from_js_ident:ident| $js_is_none:expr,
10            Some = $name_some:ident $(,)?
11        }
12    ) => {
13        #[doc = concat!("Corresponding to `T | ", $js_none_doc, "`")]
14        ///
15        /// # Convert as js
16        ///
17        #[doc = concat!("`", stringify!($name_none), "` => `", $js_none_doc, "`")]
18        ///
19        #[doc = concat!("`", stringify!($name_some), "(value)` => `value`")]
20        ///
21        /// # Convert as js property value
22        ///
23        /// Property is always set
24        #[derive(Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)]
25        pub enum $name<T> {
26            $name_none,
27            $name_some(T),
28        }
29
30        impl<T> $name<T> {
31            #[inline]
32            pub fn from_option(v: Option<T>) -> Self {
33                if let Some(v) = v {
34                    Self::$name_some(v)
35                } else {
36                    Self::$name_none
37                }
38            }
39
40            #[inline]
41            pub fn into_option(self) -> Option<T> {
42                match self {
43                    Self::$name_none => None,
44                    Self::$name_some(v) => Some(v),
45                }
46            }
47
48            #[inline]
49            pub fn as_ref(&self) -> $name<&T> {
50                match self {
51                    Self::$name_none => $name::$name_none,
52                    Self::$name_some(v) => $name::$name_some(v),
53                }
54            }
55        }
56
57        impl<T: Clone> Clone for $name<T> {
58            #[inline]
59            fn clone(&self) -> Self {
60                match self {
61                    Self::$name_some(x) => Self::$name_some(x.clone()),
62                    Self::$name_none => Self::$name_none,
63                }
64            }
65
66            #[inline]
67            fn clone_from(&mut self, source: &Self) {
68                match (self, source) {
69                    (Self::$name_some(to), Self::$name_some(from)) => to.clone_from(from),
70                    (to, from) => *to = from.clone(),
71                }
72            }
73        }
74
75        impl<T> Default for $name<T> {
76            #[doc = concat!("Returns [`", stringify!($name_none), "`][", stringify!($name), "::", stringify!($name_none), "]")]
77            #[inline]
78            fn default() -> $name<T> {
79                Self::$name_none
80            }
81        }
82
83        impl<T> From<Option<T>> for $name<T> {
84            #[inline]
85            fn from(v: Option<T>) -> Self {
86                Self::from_option(v)
87            }
88        }
89
90        impl<T> Into<Option<T>> for $name<T> {
91            #[inline]
92            fn into(self) -> Option<T> {
93                self.into_option()
94            }
95        }
96
97        impl<T: ToJs> ToJs for $name<T> {
98            fn to_js(&self) -> wasm_bindgen::JsValue {
99                match self {
100                    Self::$name_none => $js_none,
101                    Self::$name_some(v) => v.to_js(),
102                }
103            }
104        }
105
106        impl<T: FromJs> FromJs for $name<T> {
107            type Error = T::Error;
108            fn from_js($from_js_ident: wasm_bindgen::JsValue) -> Result<Self, Self::Error> {
109                if $js_is_none {
110                    Ok(Self::$name_none)
111                } else {
112                    T::from_js($from_js_ident).map(Self::$name_some)
113                }
114            }
115        }
116
117        impl<T: JsCast> $name<WrapJsCast<T>> {
118            pub fn wrap_js_cast(v: T) -> Self {
119                Self::$name_some(WrapJsCast(v))
120            }
121        }
122    };
123}
124
125def_option_like! {
126    Nullable {
127        None = Null = "null" JsValue::NULL,
128        condition = |v| v.is_null(),
129        Some = NonNull,
130    }
131}
132
133def_option_like! {
134    Maybe {
135        None = Undefined = "undefined" JsValue::UNDEFINED,
136        condition = |v| v.is_undefined(),
137        Some = Defined,
138    }
139}