diff --git a/zerompk/src/lib.rs b/zerompk/src/lib.rs index 2ac53cd..3d483cc 100644 --- a/zerompk/src/lib.rs +++ b/zerompk/src/lib.rs @@ -1,5 +1,8 @@ #![no_std] +#[cfg(test)] +extern crate self as zerompk; + mod consts; mod error; mod r#impl; diff --git a/zerompk/src/read.rs b/zerompk/src/read.rs index d56ba18..6ad361d 100644 --- a/zerompk/src/read.rs +++ b/zerompk/src/read.rs @@ -1477,7 +1477,31 @@ impl<'de, R: std::io::Read> Read<'de> for IOReader { #[cfg(test)] mod tests { - use super::*; + use crate::{Error, FromMessagePack, Read, read::IOReader, read::SliceReader}; + + #[allow(dead_code)] + #[derive(Debug, zerompk_derive::FromMessagePack)] + #[msgpack(map)] + struct DuplicateKeyMap { + x: u8, + y: u8, + } + + #[test] + fn derived_map_struct_duplicate_key_restores_reader_depth() { + let data = [ + 0x82, // fixmap with 2 entries + 0xa1, b'x', // fixstr of length 1: "x" + 0x01, // positive fixint: 1 + 0xa1, b'x', // fixstr of length 1: "x" (duplicate key) + 0x02, // positive fixint: 2 + ]; + let mut reader = SliceReader::new(&data); + + let err = DuplicateKeyMap::read(&mut reader).unwrap_err(); + + assert!(matches!(err, Error::KeyDuplicated(ref key) if key == "x")); + } #[test] fn slice_reader_decrement_depth_at_zero_does_not_underflow() { diff --git a/zerompk_derive/src/lib.rs b/zerompk_derive/src/lib.rs index 0e5d2bd..6d84214 100644 --- a/zerompk_derive/src/lib.rs +++ b/zerompk_derive/src/lib.rs @@ -1128,7 +1128,7 @@ fn expand_map_struct(data: &DataStruct) -> Result { quote! { #idx => { if #slot.is_some() { - return Err(::zerompk::Error::KeyDuplicated(#key_name.into())); + break '__zerompk_read_map Err(::zerompk::Error::KeyDuplicated(#key_name.into())); } #slot = ::core::option::Option::Some(#read_expr); } @@ -1167,6 +1167,7 @@ fn expand_map_struct(data: &DataStruct) -> Result { }; let read = quote! { + '__zerompk_read_map: { reader.check_map_len(#count)?; #( let mut #slots: ::core::option::Option<#tys> = ::core::option::Option::None; )* @@ -1189,7 +1190,8 @@ fn expand_map_struct(data: &DataStruct) -> Result { let #names = #slots.ok_or_else(|| ::zerompk::Error::KeyNotFound(#key_lits.into()))?; )* - Ok(Self { #( #init_fields ),* }) + break '__zerompk_read_map Ok(Self { #( #init_fields ),* }); + } }; Ok(ImplBody { write, read }) @@ -1690,7 +1692,7 @@ fn build_enum_variant_payload( quote! { #idx => { if #slot.is_some() { - return Err(::zerompk::Error::KeyDuplicated(#key_name.into())); + break '__zerompk_read_map Err(::zerompk::Error::KeyDuplicated(#key_name.into())); } #slot = ::core::option::Option::Some(#read_expr); } @@ -1734,6 +1736,7 @@ fn build_enum_variant_payload( }; let read_ctor = quote! { + '__zerompk_read_map: { reader.check_map_len(#count)?; #( let mut #slot_vars: ::core::option::Option<#active_tys> = ::core::option::Option::None; )* @@ -1756,7 +1759,8 @@ fn build_enum_variant_payload( let #active_names = #slot_vars.ok_or_else(|| ::zerompk::Error::KeyNotFound(#key_lits.into()))?; )* - Ok(Self::#v_ident { #( #init_fields ),* }) + break '__zerompk_read_map Ok(Self::#v_ident { #( #init_fields ),* }); + } }; Ok((