From 6c74f6d2580ce7c532fdf8866cd0a50c22165d7e Mon Sep 17 00:00:00 2001 From: paq <89paku@gmail.com> Date: Tue, 12 May 2026 09:26:13 +0900 Subject: [PATCH] fix: support signed repr c_enum derive --- zerompk/tests/derive.rs | 18 ++++++++++ zerompk_derive/src/lib.rs | 72 +++++++++++++++++++++++++++++++++------ 2 files changed, 79 insertions(+), 11 deletions(-) diff --git a/zerompk/tests/derive.rs b/zerompk/tests/derive.rs index bb8b0f9..e8e5926 100644 --- a/zerompk/tests/derive.rs +++ b/zerompk/tests/derive.rs @@ -124,6 +124,14 @@ enum BasicLevel { High, } +#[derive(ToMessagePack, FromMessagePack, Debug, PartialEq)] +#[msgpack(c_enum)] +#[repr(i8)] +enum ReprI8Enum { + MinusOne = -1, + Zero = 0, +} + #[derive(ToMessagePack, FromMessagePack, Debug, PartialEq)] struct RecursiveNode { next: Option>, @@ -595,6 +603,16 @@ fn derive_c_enum_with_implicit_discriminant() { assert_eq!(decoded, value); } +#[test] +fn derive_c_enum_with_signed_discriminant() { + let value = ReprI8Enum::MinusOne; + let data = zerompk::to_msgpack_vec(&value).unwrap(); + assert_eq!(data, vec![0xff]); + + let decoded: ReprI8Enum = zerompk::from_msgpack(&data).unwrap(); + assert_eq!(decoded, value); +} + #[test] fn derive_c_enum_unknown_value_is_error() { let err = zerompk::from_msgpack::(&[0x03]).unwrap_err(); diff --git a/zerompk_derive/src/lib.rs b/zerompk_derive/src/lib.rs index f0d355d..9a63c98 100644 --- a/zerompk_derive/src/lib.rs +++ b/zerompk_derive/src/lib.rs @@ -43,12 +43,20 @@ struct TypeConfig { repr: Option, c_enum: bool, allow_unknown_fields: bool, + c_enum_repr: CEnumRepr, +} + +#[derive(Clone, Copy, PartialEq, Eq)] +enum CEnumRepr { + Signed, + Unsigned, } fn parse_type_config_from_attrs(attrs: &[syn::Attribute]) -> Result { let mut repr = None; let mut c_enum = false; let mut allow_unknown_fields = false; + let mut c_enum_repr = CEnumRepr::Unsigned; for attr in attrs { if !attr.path().is_ident("msgpack") { @@ -91,10 +99,31 @@ fn parse_type_config_from_attrs(attrs: &[syn::Attribute]) -> Result })?; } + if c_enum { + for attr in attrs { + if !attr.path().is_ident("repr") { + continue; + } + + attr.parse_nested_meta(|meta| { + if meta.path.is_ident("i8") + || meta.path.is_ident("i16") + || meta.path.is_ident("i32") + || meta.path.is_ident("i64") + || meta.path.is_ident("isize") + { + c_enum_repr = CEnumRepr::Signed; + } + Ok(()) + })?; + } + } + Ok(TypeConfig { repr, c_enum, allow_unknown_fields, + c_enum_repr, }) } @@ -851,7 +880,7 @@ fn expand(input: DeriveInput, kind: DeriveKind) -> Result Result Result { +fn expand_c_enum(data: &DataEnum, repr: CEnumRepr) -> Result { let mut write_arms = Vec::new(); let mut read_arms = Vec::new(); @@ -1355,16 +1384,32 @@ fn expand_c_enum(data: &DataEnum) -> Result { )); } - write_arms.push(quote! { - Self::#v_ident => { - writer.write_u64(Self::#v_ident as u64)?; - Ok(()) + match repr { + CEnumRepr::Signed => { + write_arms.push(quote! { + Self::#v_ident => { + writer.write_i64(Self::#v_ident as i64)?; + Ok(()) + } + }); + + read_arms.push(quote! { + __value if __value == (Self::#v_ident as i64) => Ok(Self::#v_ident) + }); } - }); + CEnumRepr::Unsigned => { + write_arms.push(quote! { + Self::#v_ident => { + writer.write_u64(Self::#v_ident as u64)?; + Ok(()) + } + }); - read_arms.push(quote! { - __value if __value == (Self::#v_ident as u64) => Ok(Self::#v_ident) - }); + read_arms.push(quote! { + __value if __value == (Self::#v_ident as u64) => Ok(Self::#v_ident) + }); + } + } } let write = quote! { @@ -1373,8 +1418,13 @@ fn expand_c_enum(data: &DataEnum) -> Result { } }; + let read_value = match repr { + CEnumRepr::Signed => quote! { reader.read_i64()? }, + CEnumRepr::Unsigned => quote! { reader.read_u64()? }, + }; + let read = quote! { - let __value = reader.read_u64()?; + let __value = #read_value; match __value { #( #read_arms, )* _ => Err(::zerompk::Error::InvalidMarker(0)),