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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
use wasm_bindgen::{JsCast, JsValue};

use crate::{ToJs, WrapJsCast};

macro_rules! def_option_like {
    (
        $name:ident {
            None = $name_none:ident = $js_none_doc:literal $js_none:expr,
            Some = $name_some:ident $(,)?
        }
    ) => {
        #[doc = concat!("Corresponding to `T | ", $js_none_doc, "`")]
        ///
        /// # Convert as js
        ///
        #[doc = concat!("`", stringify!($name_none), "` => `", $js_none_doc, "`")]
        ///
        #[doc = concat!("`", stringify!($name_some), "(value)` => `value`")]
        ///
        /// # Convert as js property value
        ///
        /// Property is always set
        #[derive(Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)]
        pub enum $name<T> {
            $name_none,
            $name_some(T),
        }

        impl<T> $name<T> {
            #[inline]
            pub fn from_option(v: Option<T>) -> Self {
                if let Some(v) = v {
                    Self::$name_some(v)
                } else {
                    Self::$name_none
                }
            }

            #[inline]
            pub fn into_option(self) -> Option<T> {
                match self {
                    Self::$name_none => None,
                    Self::$name_some(v) => Some(v),
                }
            }

            #[inline]
            pub fn as_ref(&self) -> $name<&T> {
                match self {
                    Self::$name_none => $name::$name_none,
                    Self::$name_some(v) => $name::$name_some(v),
                }
            }
        }

        impl<T: Clone> Clone for $name<T> {
            #[inline]
            fn clone(&self) -> Self {
                match self {
                    Self::$name_some(x) => Self::$name_some(x.clone()),
                    Self::$name_none => Self::$name_none,
                }
            }

            #[inline]
            fn clone_from(&mut self, source: &Self) {
                match (self, source) {
                    (Self::$name_some(to), Self::$name_some(from)) => to.clone_from(from),
                    (to, from) => *to = from.clone(),
                }
            }
        }

        impl<T> Default for $name<T> {
            #[doc = concat!("Returns [`", stringify!($name_none), "`][", stringify!($name), "::", stringify!($name_none), "]")]
            #[inline]
            fn default() -> $name<T> {
                Self::$name_none
            }
        }

        impl<T> From<Option<T>> for $name<T> {
            #[inline]
            fn from(v: Option<T>) -> Self {
                Self::from_option(v)
            }
        }

        impl<T> Into<Option<T>> for $name<T> {
            #[inline]
            fn into(self) -> Option<T> {
                self.into_option()
            }
        }

        impl<T: ToJs> ToJs for $name<T> {
            fn to_js(&self) -> wasm_bindgen::JsValue {
                match self {
                    Self::$name_none => $js_none,
                    Self::$name_some(v) => v.to_js(),
                }
            }
        }

        impl<T: JsCast> $name<WrapJsCast<T>> {
            pub fn wrap_js_cast(v: T) -> Self {
                Self::$name_some(WrapJsCast(v))
            }
        }
    };
}

def_option_like! {
    Nullable {
        None = Null = "null" JsValue::NULL,
        Some = NonNull,
    }
}

def_option_like! {
    Maybe {
        None = Undefined = "undefined" JsValue::UNDEFINED,
        Some = Defined,
    }
}