use super::{FromNapiValue, ToNapiValue, TypeName}; use crate::{ bindgen_runtime::{Null, Undefined}, sys, type_of, JsUndefined, NapiRaw, Status, ValueType, }; const ERROR_MSG: &str = "The return value of typeof(T) should not be equal in Either"; #[derive(Debug, Clone, Copy)] pub enum Either { A(A), B(B), } impl Either { /// # Safety /// Backward compatible with `Either` in **v1** pub unsafe fn raw(&self) -> sys::napi_value { match &self { Self::A(a) => unsafe { a.raw() }, Self::B(b) => unsafe { b.raw() }, } } } impl TypeName for Either { fn type_name() -> &'static str { "Either" } fn value_type() -> ValueType { ValueType::Unknown } } // Backwards compatibility with v1 impl From> for Option { fn from(value: Either) -> Option { match value { Either::A(v) => Some(v), Either::B(_) => None, } } } impl From> for Either { fn from(value: Option) -> Self { match value { Some(v) => Either::A(v), None => Either::B(()), } } } impl From> for Option { fn from(value: Either) -> Option { match value { Either::A(v) => Some(v), Either::B(_) => None, } } } impl FromNapiValue for Either { unsafe fn from_napi_value(env: sys::napi_env, napi_val: sys::napi_value) -> crate::Result { debug_assert!(A::value_type() != B::value_type(), "{}", ERROR_MSG); let js_type = type_of!(env, napi_val)?; if js_type == A::value_type() { unsafe { A::from_napi_value(env, napi_val).map(Self::A) } } else if js_type == B::value_type() { unsafe { B::from_napi_value(env, napi_val).map(Self::B) } } else { Err(crate::Error::new( Status::InvalidArg, format!( "Expect type {} or {}, but got {}", A::value_type(), B::value_type(), js_type ), )) } } } impl ToNapiValue for Either { unsafe fn to_napi_value( env: sys::napi_env, value: Self, ) -> crate::Result { match value { Self::A(a) => unsafe { A::to_napi_value(env, a) }, Self::B(b) => unsafe { B::to_napi_value(env, b) }, } } } macro_rules! count_idents { ( $( $idents:ident ),* $( , )* ) => { { #[allow(dead_code, non_camel_case_types)] enum Idents { $( $idents, )* __LastVariant } const COUNT: usize = Idents::__LastVariant as usize; COUNT } }; } macro_rules! either_n { ( $either_name:ident, $( $parameter:ident ),+ $( , )* ) => { #[derive(Debug, Clone, Copy)] pub enum $either_name< $( $parameter ),+ > { $( $parameter ( $parameter ) ),+ } impl< $( $parameter ),+ > TypeName for $either_name < $( $parameter ),+ > where $( $parameter: TypeName ),+ { fn type_name() -> &'static str { stringify!( $either_name ) } fn value_type() -> ValueType { ValueType::Unknown } } impl< $( $parameter ),+ > FromNapiValue for $either_name < $( $parameter ),+ > where $( $parameter: TypeName + FromNapiValue ),+ { unsafe fn from_napi_value(env: sys::napi_env, napi_val: sys::napi_value) -> crate::Result { debug_assert!( { let mut types = vec![ $( $parameter ::value_type() ),+ ]; types.dedup(); types.len() == count_idents!( $( $parameter ),+ ) }, "{}", ERROR_MSG ); let js_type = type_of!(env, napi_val)?; $( if js_type == $parameter::value_type() { unsafe { $parameter ::from_napi_value(env, napi_val).map(Self:: $parameter ) } } else )+ { Err(crate::Error::new( Status::InvalidArg, format!( concat!("Expect type ", $( "`{", stringify!( $parameter ), "}`, " ),+ , "but got {js_type}"), $( $parameter = $parameter::value_type(), )+ js_type = js_type, ), )) } } } impl< $( $parameter ),+ > ToNapiValue for $either_name < $( $parameter ),+ > where $( $parameter: ToNapiValue ),+ { unsafe fn to_napi_value( env: sys::napi_env, value: Self ) -> crate::Result { match value { $( Self:: $parameter (v) => unsafe { $parameter ::to_napi_value(env, v) } ),+ } } } }; } either_n!(Either3, A, B, C); either_n!(Either4, A, B, C, D); either_n!(Either5, A, B, C, D, E); either_n!(Either6, A, B, C, D, E, F); either_n!(Either7, A, B, C, D, E, F, G); either_n!(Either8, A, B, C, D, E, F, G, H); either_n!(Either9, A, B, C, D, E, F, G, H, I); either_n!(Either10, A, B, C, D, E, F, G, H, I, J); either_n!(Either11, A, B, C, D, E, F, G, H, I, J, K); either_n!(Either12, A, B, C, D, E, F, G, H, I, J, K, L); either_n!(Either13, A, B, C, D, E, F, G, H, I, J, K, L, M); either_n!(Either14, A, B, C, D, E, F, G, H, I, J, K, L, M, N); either_n!(Either15, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O); either_n!(Either16, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P); either_n!(Either17, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q); either_n!(Either18, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R); either_n!(Either19, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S); either_n!(Either20, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T); either_n!(Either21, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U); either_n!(Either22, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V); either_n!(Either23, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W); either_n!(Either24, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X); either_n!(Either25, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y); either_n!(Either26, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z);