diff --git a/web-message-derive/src/lib.rs b/web-message-derive/src/lib.rs index 9074afa..e2f5e98 100644 --- a/web-message-derive/src/lib.rs +++ b/web-message-derive/src/lib.rs @@ -39,7 +39,7 @@ fn expand_struct(ident: &syn::Ident, fields: &Fields) -> proc_macro2::TokenStrea quote! { impl ::web_message::Message for #ident { fn from_message(message: ::web_sys::js_sys::wasm_bindgen::JsValue) -> Result { - let obj = web_sys::js_sys::Object::try_from(&message).ok_or(::web_message::Error::ExpectedUnitObject)?; + let obj = web_sys::js_sys::Object::try_from(&message).ok_or(::web_message::Error::UnexpectedType)?; Ok(Self { #(#field_inits),* }) @@ -58,7 +58,7 @@ fn expand_enum( enum_ident: &syn::Ident, variants: &syn::punctuated::Punctuated, ) -> proc_macro2::TokenStream { - let from_matches = variants.iter().map(|variant| { + let from_obj= variants.iter().filter_map(|variant| { let variant_ident = &variant.ident; let variant_str = variant_ident.to_string(); @@ -74,26 +74,21 @@ fn expand_enum( } }); - quote! { + Some(quote! { #variant_str => { Ok(#enum_ident::#variant_ident { #(#field_assignments),* }) } - } + }) } - Fields::Unit => { - quote! { - #variant_str if val.is_null() => Ok(#enum_ident::#variant_ident), - #variant_str => Err(::web_message::Error::ExpectedNull), - } - } + Fields::Unit => None, Fields::Unnamed(fields_unnamed) if fields_unnamed.unnamed.len() == 1 => { - quote! { + Some(quote! { #variant_str => Ok(#enum_ident::#variant_ident(::web_message::Message::from_message(val)?)), - } + }) } Fields::Unnamed(_) => { @@ -102,6 +97,19 @@ fn expand_enum( } }); + let from_string = variants.iter().filter_map(|variant| { + let variant_ident = &variant.ident; + let variant_str = variant_ident.to_string(); + + if let Fields::Unit = &variant.fields { + Some(quote! { + #variant_str => Ok(#enum_ident::#variant_ident), + }) + } else { + None + } + }); + let into_matches = variants.iter().map(|variant| { let variant_ident = &variant.ident; let variant_str = variant_ident.to_string(); @@ -121,22 +129,26 @@ fn expand_enum( quote! { #enum_ident::#variant_ident { #(#field_names),* } => { + let obj = ::web_sys::js_sys::Object::new(); let inner = ::web_sys::js_sys::Object::new(); #(#set_fields)* - ::web_sys::js_sys::Reflect::set(&obj, &#variant_str.into(), &inner.into()).unwrap() + ::web_sys::js_sys::Reflect::set(&obj, &#variant_str.into(), &inner.into()).unwrap(); + obj.into() } } } Fields::Unit => { quote! { - #enum_ident::#variant_ident => - ::web_sys::js_sys::Reflect::set(&obj, &#variant_str.into(), &::web_sys::js_sys::wasm_bindgen::JsValue::NULL).unwrap() + #enum_ident::#variant_ident => #variant_str.into() } } Fields::Unnamed(fields_unnamed) if fields_unnamed.unnamed.len() == 1 => { quote! { - #enum_ident::#variant_ident(v) => - ::web_sys::js_sys::Reflect::set(&obj, &#variant_str.into(), &v.into_message(_transferable)).unwrap() + #enum_ident::#variant_ident(v) => { + let obj = ::web_sys::js_sys::Object::new(); + ::web_sys::js_sys::Reflect::set(&obj, &#variant_str.into(), &v.into_message(_transferable)).unwrap(); + obj.into() + } } } Fields::Unnamed(_) => unimplemented!("web-message does not support tuple variants (yet)"), @@ -146,31 +158,35 @@ fn expand_enum( quote! { impl ::web_message::Message for #enum_ident { fn from_message(message: ::web_sys::js_sys::wasm_bindgen::JsValue) -> ::std::result::Result { - // Grab the single key from the object - let obj = web_sys::js_sys::Object::try_from(&message).ok_or(::web_message::Error::ExpectedUnitObject)?; - - let keys = web_sys::js_sys::Object::keys(&obj); - if keys.length() != 1 { - return Err(::web_message::Error::ExpectedUnitObject); - } + if let Some(s) = message.as_string() { + match s.as_str() { + #(#from_string)* + _ => Err(::web_message::Error::UnknownTag), + } + } else if let Some(obj) = web_sys::js_sys::Object::try_from(&message) { + let keys = web_sys::js_sys::Object::keys(&obj); + if keys.length() != 1 { + return Err(::web_message::Error::UnexpectedType); + } - let tag = keys.get(0); - let tag_str = tag.as_string().ok_or(::web_message::Error::ExpectedUnitObject)?; + let tag = keys.get(0); + let tag_str = tag.as_string().ok_or(::web_message::Error::UnexpectedType)?; - let val = ::web_sys::js_sys::Reflect::get(&obj, &tag).unwrap(); + let val = ::web_sys::js_sys::Reflect::get(&obj, &tag).unwrap(); - match tag_str.as_str() { - #(#from_matches)* - _ => Err(::web_message::Error::UnknownTag(tag_str)), + match tag_str.as_str() { + #(#from_obj)* + _ => Err(::web_message::Error::UnknownTag), + } + } else { + Err(::web_message::Error::UnexpectedType) } } fn into_message(self, _transferable: &mut ::web_sys::js_sys::Array) -> ::web_sys::js_sys::wasm_bindgen::JsValue { - let obj = ::web_sys::js_sys::Object::new(); match self { #(#into_matches),* - }; - obj.into() + } } } } diff --git a/web-message/src/error.rs b/web-message/src/error.rs index 24ecab6..e593877 100644 --- a/web-message/src/error.rs +++ b/web-message/src/error.rs @@ -6,20 +6,11 @@ pub enum Error { #[error("invalid '{0}' field")] InvalidField(&'static str), - #[error("expected object with a single (string) key")] - ExpectedUnitObject, + #[error("unexpected type")] + UnexpectedType, - #[error("expected null")] - ExpectedNull, - - #[error("invalid type: {0}")] - InvalidType(&'static str), - - #[error("expected string")] - ExpectedString, - - #[error("unknown tag: {0}")] - UnknownTag(String), + #[error("unknown tag")] + UnknownTag, #[cfg(feature = "url")] #[error("invalid URL: {0}")] diff --git a/web-message/src/message.rs b/web-message/src/message.rs index 810d968..1860b1d 100644 --- a/web-message/src/message.rs +++ b/web-message/src/message.rs @@ -22,7 +22,7 @@ macro_rules! upstream { } fn from_message(message: JsValue) -> Result { - Self::try_from(message).map_err(|_| Error::InvalidType(stringify!($t))) + Self::try_from(message).map_err(|_| Error::UnexpectedType) } } )* @@ -41,7 +41,7 @@ macro_rules! integer { } fn from_message(message: JsValue) -> Result { - Ok(message.as_f64().ok_or(Error::InvalidType(stringify!($t)))? as $t) + Ok(message.as_f64().ok_or(Error::UnexpectedType)? as $t) } } )* @@ -57,7 +57,7 @@ impl Message for bool { } fn from_message(message: JsValue) -> Result { - message.as_bool().ok_or(Error::InvalidType("bool")) + message.as_bool().ok_or(Error::UnexpectedType) } } @@ -88,7 +88,7 @@ impl Message for Vec { fn from_message(message: JsValue) -> Result { if !message.is_array() { - return Err(Error::InvalidType("Vec")); + return Err(Error::UnexpectedType); } let array = Array::from(&message); @@ -109,7 +109,7 @@ impl Message for js_sys::ArrayBuffer { fn from_message(message: JsValue) -> Result { message .dyn_into::() - .map_err(|_| Error::InvalidType("ArrayBuffer")) + .map_err(|_| Error::UnexpectedType) } } @@ -126,7 +126,7 @@ macro_rules! transferable_feature { fn from_message(message: JsValue) -> Result { message .dyn_into::() - .map_err(|_| Error::InvalidType(stringify!($t))) + .map_err(|_| Error::UnexpectedType) } } )* @@ -158,7 +158,7 @@ impl Message for url::Url { } fn from_message(message: JsValue) -> Result { - let str = message.as_string().ok_or(Error::ExpectedString)?; + let str = message.as_string().ok_or(Error::UnexpectedType)?; url::Url::parse(&str).map_err(Error::InvalidUrl) } }