From 40e7443ca097f4cc2ad84dc5200f23ba5cd05ea4 Mon Sep 17 00:00:00 2001 From: Luke Curley Date: Thu, 17 Apr 2025 18:00:50 -0700 Subject: [PATCH] Add support for unnamed tuples. --- web-message-derive/src/lib.rs | 52 +++++++++++++++++++++++++++++++---- web-message/src/derive.rs | 36 ++++++------------------ web-message/src/error.rs | 3 ++ 3 files changed, 58 insertions(+), 33 deletions(-) diff --git a/web-message-derive/src/lib.rs b/web-message-derive/src/lib.rs index e2f5e98..00947ea 100644 --- a/web-message-derive/src/lib.rs +++ b/web-message-derive/src/lib.rs @@ -75,11 +75,12 @@ fn expand_enum( }); Some(quote! { - #variant_str => { + #variant_str if val.is_object() => { Ok(#enum_ident::#variant_ident { #(#field_assignments),* }) } + #variant_str => Err(::web_message::Error::UnexpectedType), }) } @@ -89,10 +90,30 @@ fn expand_enum( Some(quote! { #variant_str => Ok(#enum_ident::#variant_ident(::web_message::Message::from_message(val)?)), }) - } + }, + + Fields::Unnamed(fields_unnamed) => { + let fields_count = fields_unnamed.unnamed.len() as u32; + let field_assignments = (0..fields_count).map(|i| { + quote! { + ::web_message::Message::from_message(arr.get(#i)) + .map_err(|_| ::web_message::Error::InvalidField(stringify!(#i)))? + } + }); + + Some(quote! { + #variant_str if val.is_array() => { + let arr = ::web_sys::js_sys::Array::from(&val); + if arr.length() != #fields_count { + return Err(::web_message::Error::UnexpectedLength); + } - Fields::Unnamed(_) => { - unimplemented!("web-message does not support multi-element tuple variants (yet?)"); + Ok(#enum_ident::#variant_ident ( + #(#field_assignments),* + )) + } + #variant_str => Err(::web_message::Error::UnexpectedType), + }) } } }); @@ -151,7 +172,28 @@ fn expand_enum( } } } - Fields::Unnamed(_) => unimplemented!("web-message does not support tuple variants (yet)"), + Fields::Unnamed(fields_unnamed) => { + let fields_count = fields_unnamed.unnamed.len(); + let field_idents: Vec<_> = (0..fields_count) + .map(|i| syn::Ident::new(&format!("field_{}", i), proc_macro2::Span::call_site())) + .collect(); + + let set_fields = field_idents.iter().map(|f| { + quote! { + inner.push(&#f.into_message(_transferable)); + } + }); + + quote! { + #enum_ident::#variant_ident(#(#field_idents),*) => { + let obj = ::web_sys::js_sys::Object::new(); + let inner = ::web_sys::js_sys::Array::new(); + #(#set_fields)* + ::web_sys::js_sys::Reflect::set(&obj, &#variant_str.into(), &inner.into()).unwrap(); + obj.into() + } + } + } } }); diff --git a/web-message/src/derive.rs b/web-message/src/derive.rs index f0d3055..64f0437 100644 --- a/web-message/src/derive.rs +++ b/web-message/src/derive.rs @@ -8,11 +8,19 @@ mod test { #[test] fn enum_msg() { + #[derive(Message, Clone, Debug, PartialEq, Eq)] + struct Config { + width: u32, + height: u32, + } + #[derive(Message, Clone, Debug, PartialEq, Eq)] enum Command { Connect { url: String }, Frame { name: Option, payload: ArrayBuffer }, + Config(Config), Close, + Dimensions(u32, u64), } let command = Command::Frame { @@ -50,32 +58,4 @@ mod test { assert_eq!(event, out); assert_eq!(transferable, [event.payload].iter().collect()); } - - #[test] - fn enum_variant() { - #[derive(Message, Clone, Debug, PartialEq, Eq)] - struct Config { - width: u32, - height: u32, - } - - #[derive(Message, Clone, Debug, PartialEq, Eq)] - enum Command { - Connect { url: String }, - Config(Config), - Close, - } - - let command = Command::Config(Config { - width: 100, - height: 100, - }); - - let mut transferable = Array::new(); - let obj = command.clone().into_message(&mut transferable); - let out = Command::from_message(obj).unwrap(); - - assert_eq!(command, out); - assert_eq!(transferable.length(), 1); - } } diff --git a/web-message/src/error.rs b/web-message/src/error.rs index e593877..3c2c582 100644 --- a/web-message/src/error.rs +++ b/web-message/src/error.rs @@ -6,6 +6,9 @@ pub enum Error { #[error("invalid '{0}' field")] InvalidField(&'static str), + #[error("unexpected length")] + UnexpectedLength, + #[error("unexpected type")] UnexpectedType,