From d20d2d8b9d9f6393d1770fc9df9e93b27511722a Mon Sep 17 00:00:00 2001 From: James Cape Date: Fri, 10 Mar 2023 17:04:08 -0800 Subject: [PATCH 1/9] Port to ciborium, move json_u64 module to own file. --- Cargo.toml | 23 +++++-- src/json_u64.rs | 79 ++++++++++++++++++++++ src/lib.rs | 170 ++++++++++++++++++------------------------------ 3 files changed, 158 insertions(+), 114 deletions(-) create mode 100644 src/json_u64.rs diff --git a/Cargo.toml b/Cargo.toml index 378fbfa..c5818cf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,7 @@ rust-version = "1.67.1" [profile.release] lto = "thin" -[metadata.release] +[package.metadata.release] shared-version = true dev-version-ext = "beta.0" consolidate-commits = true @@ -32,8 +32,9 @@ pre-release-replacements = [ ] [features] -std = ["serde/std", "serde_cbor/std", "serde_with"] -test_utils = ["protobuf"] +std = ["serde/std", "ciborium/std", "jsonu64"] +test_utils = ["dep:protobuf"] +jsonu64 = ["dep:serde_with"] [dependencies] prost = { version = "0.11", default-features = false, features = [ @@ -44,12 +45,22 @@ serde = { version = "1.0", default-features = false, features = [ "alloc", "derive", ] } -serde_cbor = { version = "0.11", default-features = false, features = [ - "alloc", -] } serde_with = { version = "2.0", default-features = false, features = [ "macros", ], optional = true } +# These dependencies locked because the interaction between cibiorum and ciborium-io is garbage. +# +# cibiorum::ser::into_writer(&Vec) is infallible, but only because of how cibiorum-io is +# implemented, in version 0.2.0, when the alloc feature (which is not exposed by ciborium) is +# enabled. +# +# If you are considering using serde for any reason, consider the choices you have made in your +# life, and then use protobufs (via prost) instead. +ciborium = { version = "=0.2.0", default-features = false } +ciborium-io = { version = "=0.2.0", default-features = false, features = [ + "alloc", +] } + [dev-dependencies] serde_json = "1.0" diff --git a/src/json_u64.rs b/src/json_u64.rs new file mode 100644 index 0000000..4197c2d --- /dev/null +++ b/src/json_u64.rs @@ -0,0 +1,79 @@ +// Copyright (c) 2018-2022 The MobileCoin Foundation + +//! Serialization and deserialization for a U64 in JSON + +use serde::{Deserialize, Serialize}; +use serde_with::{serde_as, DisplayFromStr}; + +/// Represents u64 using string, when serializing to Json +/// Javascript integers are not 64 bit, and so it is not really proper json. +/// Using string avoids issues with some json parsers not handling large +/// numbers well. +/// +/// This does not rely on the serde-json arbitrary precision feature, which +/// (we fear) might break other things (e.g. https://github.com/serde-rs/json/issues/505) +#[serde_as] +#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq, Hash, Serialize)] +#[serde(transparent)] +pub struct JsonU64(#[serde_as(as = "DisplayFromStr")] pub u64); + +impl From<&u64> for JsonU64 { + fn from(src: &u64) -> Self { + Self(*src) + } +} + +impl From<&JsonU64> for u64 { + fn from(src: &JsonU64) -> u64 { + src.0 + } +} + +impl From for u64 { + fn from(src: JsonU64) -> u64 { + src.0 + } +} + +impl AsRef for JsonU64 { + fn as_ref(&self) -> &u64 { + &self.0 + } +} + +#[cfg(test)] +mod tests { + use super::*; + use serde::{Deserialize, Serialize}; + + #[derive(PartialEq, Serialize, Deserialize, Debug)] + struct TestStruct { + nums: Vec, + block: JsonU64, + } + + #[test] + fn serialize_jsonu64_struct() { + let the_struct = TestStruct { + nums: [0, 1, 2, u64::MAX].iter().map(Into::into).collect(), + block: JsonU64(u64::MAX - 1), + }; + let serialized = crate::serialize(&the_struct).unwrap(); + let deserialized = + crate::deserialize::(&serialized).expect("Could not deserialize struct"); + assert_eq!(deserialized, the_struct); + + // Sanity that serde_as works as expected: it should accept and hand us back + // strings. + let expected_json = + r#"{"nums":["0","1","2","18446744073709551615"],"block":"18446744073709551614"}"#; + assert_eq!( + expected_json, + serde_json::to_string(&the_struct).expect("Could not convert struct to json string") + ); + assert_eq!( + the_struct, + serde_json::from_str(expected_json).expect("Could not convert json string to struct") + ); + } +} diff --git a/src/lib.rs b/src/lib.rs index 1054a3a..113cb28 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,123 +1,106 @@ // Copyright (c) 2018-2022 The MobileCoin Foundation +//! Serialization and deserialization utilities for MobileCoin. + #![no_std] +#![doc = include_str!("../README.md")] +#![deny(missing_docs, missing_debug_implementations, unsafe_code)] extern crate alloc; -use alloc::vec::Vec; - -pub extern crate prost; -pub use prost::{DecodeError, EncodeError, Message}; +use alloc::vec::Vec; +use core::fmt::{Display, Formatter, Result as FmtResult}; use serde::{Deserialize, Serialize}; -// We put a new-type around serde_cbor::Error in `mod decode` and `mod encode`, -// because this keeps us compatible with how rmp-serde was exporting its errors, -// and avoids unnecessary code changes. +pub use prost::{self, DecodeError, EncodeError, Message}; + +/// Decoding-specific types, here for backwards compatibility. pub mod decode { + use super::*; + use alloc::{format, string::String}; + use ciborium::de::Error as CiboriumError; + use core::fmt::Debug; + + /// An error structure for CBOR decoding #[derive(Debug)] - pub struct Error(serde_cbor::Error); + pub struct Error(String); - impl core::fmt::Display for Error { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - write!(f, "Cbor Decode Error: {}", self.0) + impl Display for Error { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + write!(f, "CBOR Decode Error: {:#?}", self.0) } } - impl From for Error { - fn from(src: serde_cbor::Error) -> Self { - Self(src) + impl From> for Error { + fn from(src: CiboriumError) -> Self { + let err_msg = match src { + CiboriumError::Io(underlying) => format!("IO Error: {underlying:#?}"), + CiboriumError::Syntax(offset) => format!("Syntax Error at byte {offset:#?}"), + CiboriumError::Semantic(offset, inner_msg) => { + format!("Semantic Error at byte {offset:#?}: {inner_msg:#?}") + } + CiboriumError::RecursionLimitExceeded => format!("Recursion limit exceeded"), + }; + + Self(err_msg) } } } +/// Encoding-specific types, here for backwards compatibility. pub mod encode { - #[derive(Debug)] - pub struct Error(serde_cbor::Error); + use super::*; - impl core::fmt::Display for Error { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - write!(f, "Cbor Encode Error: {}", self.0) - } - } + /// CBOR encoding errors (this shouldn't actually be seen in practice) + #[derive(Debug)] + pub struct Error; - impl From for Error { - fn from(src: serde_cbor::Error) -> Self { - Self(src) + impl Display for Error { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + write!(f, "CBOR Encode Error") } } } /// Serialize the given data structure. /// -/// Forward mc_util_serial::serialize to bincode::serialize(..., Infinite) -/// Serialization can fail if `T`'s implementation of `Serialize` decides to -/// fail. +/// This method produces CBOR-encoded bytes. pub fn serialize(value: &T) -> Result, encode::Error> where T: Serialize + Sized, { - Ok(serde_cbor::to_vec(value)?) + let mut writer = Vec::new(); + // NOTE: this depends on the precise behavior of ciborium-io 0.2: + // https://docs.rs/ciborium-io/0.2.0/src/ciborium_io/lib.rs.html#151 + ciborium::ser::into_writer(value, &mut writer) + .expect("into_writer with a Vec should be invallible"); + + Ok(writer) } /// Deserialize the given bytes to a data structure. /// -/// Forward mc_util_serial::deserialize to serde_cbor::from_slice +/// This method expects CBOR-encoded bytes. pub fn deserialize<'a, T>(bytes: &'a [u8]) -> Result where T: Deserialize<'a>, { - Ok(serde_cbor::from_slice(bytes)?) + Ok(ciborium::de::from_reader(bytes)?) } +/// Encode the give data structure to protobuf using prost::Message. pub fn encode(value: &T) -> Vec { value.encode_to_vec() } +/// Decode the protobuf-encoded bytes into a data structure using prost::Message. pub fn decode(buf: &[u8]) -> Result { T::decode(buf) } #[cfg(feature = "serde_with")] -mod json_u64 { - use super::*; - use serde_with::{serde_as, DisplayFromStr}; - - /// Represents u64 using string, when serializing to Json - /// Javascript integers are not 64 bit, and so it is not really proper json. - /// Using string avoids issues with some json parsers not handling large - /// numbers well. - /// - /// This does not rely on the serde-json arbitrary precision feature, which - /// (we fear) might break other things (e.g. https://github.com/serde-rs/json/issues/505) - #[serde_as] - #[derive(Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq, Hash, Serialize)] - #[serde(transparent)] - pub struct JsonU64(#[serde_as(as = "DisplayFromStr")] pub u64); - - impl From<&u64> for JsonU64 { - fn from(src: &u64) -> Self { - Self(*src) - } - } - - impl From<&JsonU64> for u64 { - fn from(src: &JsonU64) -> u64 { - src.0 - } - } - - impl From for u64 { - fn from(src: JsonU64) -> u64 { - src.0 - } - } - - impl AsRef for JsonU64 { - fn as_ref(&self) -> &u64 { - &self.0 - } - } -} +/// This module contains a JsonU64 type which is used to represent u64 values safely in JSON. +mod json_u64; /// JsonU64 is exported if it is available -- the serde_with crate which it /// depends on relies on std, so it must be optional. @@ -147,15 +130,16 @@ pub fn round_trip_message( #[cfg(test)] mod test { use super::*; - use alloc::vec; + use alloc::{string::String, vec}; use serde::{Deserialize, Serialize}; #[test] - fn test_serialize_string() { - let the_string = "There goes the baker with his tray, like always"; - let serialized = serialize(&the_string).unwrap(); - let deserialized: &str = deserialize(&serialized).unwrap(); - assert_eq!(deserialized, the_string); + fn serialize_string() { + const THE_STRING: &str = "There goes the baker with his tray, like always"; + + let serialized = serialize(&THE_STRING).unwrap(); + let deserialized = deserialize::(&serialized).unwrap(); + assert_eq!(&deserialized, THE_STRING); } #[derive(PartialEq, Serialize, Deserialize, Debug)] @@ -166,44 +150,14 @@ mod test { } #[test] - fn test_serialize_struct() { + fn serialize_struct() { let the_struct = TestStruct { vec: vec![233, 123, 0, 12], integer: 4_242_424_242, float: 1.2345, }; let serialized = serialize(&the_struct).unwrap(); - let deserialized: TestStruct = deserialize(&serialized).unwrap(); + let deserialized = deserialize::(&serialized).unwrap(); assert_eq!(deserialized, the_struct); } } - -#[cfg(all(test, feature = "serde_with"))] -mod json_u64_tests { - use super::*; - use serde::{Deserialize, Serialize}; - - #[derive(PartialEq, Serialize, Deserialize, Debug)] - struct TestStruct { - nums: Vec, - block: JsonU64, - } - - #[test] - fn test_serialize_jsonu64_struct() { - let the_struct = TestStruct { - nums: [0, 1, 2, u64::MAX].iter().map(Into::into).collect(), - block: JsonU64(u64::MAX - 1), - }; - let serialized = serialize(&the_struct).unwrap(); - let deserialized: TestStruct = deserialize(&serialized).unwrap(); - assert_eq!(deserialized, the_struct); - - // Sanity that serde_as works as expected: it should accept and hand us back - // strings. - let expected_json = - r#"{"nums":["0","1","2","18446744073709551615"],"block":"18446744073709551614"}"#; - assert_eq!(expected_json, serde_json::to_string(&the_struct).unwrap()); - assert_eq!(the_struct, serde_json::from_str(expected_json).unwrap()); - } -} From 66c85863de4fa47525cc9cceff81ca0ea35aa0e6 Mon Sep 17 00:00:00 2001 From: James Cape Date: Fri, 10 Mar 2023 17:12:53 -0800 Subject: [PATCH 2/9] Make jsonu64 feature turn on std, not the other way around. --- Cargo.toml | 4 ++-- src/lib.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c5818cf..bd9ce74 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,9 +32,9 @@ pre-release-replacements = [ ] [features] -std = ["serde/std", "ciborium/std", "jsonu64"] +std = ["serde/std", "ciborium/std"] test_utils = ["dep:protobuf"] -jsonu64 = ["dep:serde_with"] +jsonu64 = ["std", "dep:serde_with"] [dependencies] prost = { version = "0.11", default-features = false, features = [ diff --git a/src/lib.rs b/src/lib.rs index 113cb28..9a56e32 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -98,13 +98,13 @@ pub fn decode(buf: &[u8]) -> Result { T::decode(buf) } -#[cfg(feature = "serde_with")] +#[cfg(feature = "jsonu64")] /// This module contains a JsonU64 type which is used to represent u64 values safely in JSON. mod json_u64; /// JsonU64 is exported if it is available -- the serde_with crate which it /// depends on relies on std, so it must be optional. -#[cfg(feature = "serde_with")] +#[cfg(feature = "jsonu64")] pub use json_u64::JsonU64; /// Take a prost type and try to roundtrip it through a protobuf type From f19d7d7672c7df7b93e3ead8fb5e3194f4fdc0f7 Mon Sep 17 00:00:00 2001 From: James Cape Date: Fri, 10 Mar 2023 17:19:05 -0800 Subject: [PATCH 3/9] Cargo sort. --- Cargo.toml | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index bd9ce74..b631ad9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,18 +37,6 @@ test_utils = ["dep:protobuf"] jsonu64 = ["std", "dep:serde_with"] [dependencies] -prost = { version = "0.11", default-features = false, features = [ - "prost-derive", -] } -protobuf = { version = "2.27", optional = true } -serde = { version = "1.0", default-features = false, features = [ - "alloc", - "derive", -] } -serde_with = { version = "2.0", default-features = false, features = [ - "macros", -], optional = true } - # These dependencies locked because the interaction between cibiorum and ciborium-io is garbage. # # cibiorum::ser::into_writer(&Vec) is infallible, but only because of how cibiorum-io is @@ -62,5 +50,17 @@ ciborium-io = { version = "=0.2.0", default-features = false, features = [ "alloc", ] } +prost = { version = "0.11", default-features = false, features = [ + "prost-derive", +] } +protobuf = { version = "2.27", optional = true } +serde = { version = "1.0", default-features = false, features = [ + "alloc", + "derive", +] } +serde_with = { version = "2.0", default-features = false, features = [ + "macros", +], optional = true } + [dev-dependencies] serde_json = "1.0" From 1cc786469abbb1f626bdbb055a575f0ca02f8d8f Mon Sep 17 00:00:00 2001 From: James Cape Date: Fri, 10 Mar 2023 17:23:00 -0800 Subject: [PATCH 4/9] Fix clippy --- src/lib.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 9a56e32..59e1dbd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -17,7 +17,10 @@ pub use prost::{self, DecodeError, EncodeError, Message}; /// Decoding-specific types, here for backwards compatibility. pub mod decode { use super::*; - use alloc::{format, string::String}; + use alloc::{ + format, + string::{String, ToString}, + }; use ciborium::de::Error as CiboriumError; use core::fmt::Debug; @@ -39,7 +42,7 @@ pub mod decode { CiboriumError::Semantic(offset, inner_msg) => { format!("Semantic Error at byte {offset:#?}: {inner_msg:#?}") } - CiboriumError::RecursionLimitExceeded => format!("Recursion limit exceeded"), + CiboriumError::RecursionLimitExceeded => "Recursion limit exceeded".to_string(), }; Self(err_msg) From 67fb714d2e4ff97e5b633120f8fa86b49ba3c01e Mon Sep 17 00:00:00 2001 From: James Cape Date: Fri, 10 Mar 2023 17:25:05 -0800 Subject: [PATCH 5/9] Fix unit tests. --- src/json_u64.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/json_u64.rs b/src/json_u64.rs index 4197c2d..280bd15 100644 --- a/src/json_u64.rs +++ b/src/json_u64.rs @@ -44,6 +44,7 @@ impl AsRef for JsonU64 { #[cfg(test)] mod tests { use super::*; + use alloc::vec::Vec; use serde::{Deserialize, Serialize}; #[derive(PartialEq, Serialize, Deserialize, Debug)] @@ -55,7 +56,10 @@ mod tests { #[test] fn serialize_jsonu64_struct() { let the_struct = TestStruct { - nums: [0, 1, 2, u64::MAX].iter().map(Into::into).collect(), + nums: [0, 1, 2, u64::MAX] + .iter() + .map(Into::::into) + .collect(), block: JsonU64(u64::MAX - 1), }; let serialized = crate::serialize(&the_struct).unwrap(); From d19c4d97e690d740db8502abff34d5879567d428 Mon Sep 17 00:00:00 2001 From: James Cape Date: Sun, 12 Mar 2023 04:20:19 -0700 Subject: [PATCH 6/9] Update src/lib.rs Co-authored-by: Chris Beck <5683852+cbeck88@users.noreply.github.com> --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 59e1dbd..722bcba 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -76,7 +76,7 @@ where // NOTE: this depends on the precise behavior of ciborium-io 0.2: // https://docs.rs/ciborium-io/0.2.0/src/ciborium_io/lib.rs.html#151 ciborium::ser::into_writer(value, &mut writer) - .expect("into_writer with a Vec should be invallible"); + .expect("into_writer with a Vec should be infallible"); Ok(writer) } From d973e557d523f6dd6a7a9c2d9964f4a5dd425db4 Mon Sep 17 00:00:00 2001 From: James Cape Date: Mon, 13 Mar 2023 11:53:24 -0700 Subject: [PATCH 7/9] Address PR comments, add tests. --- src/lib.rs | 56 +++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 47 insertions(+), 9 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 722bcba..6282eea 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,7 +9,10 @@ extern crate alloc; use alloc::vec::Vec; -use core::fmt::{Display, Formatter, Result as FmtResult}; +use core::{ + fmt::{Display, Formatter, Result as FmtResult}, + mem, +}; use serde::{Deserialize, Serialize}; pub use prost::{self, DecodeError, EncodeError, Message}; @@ -72,7 +75,7 @@ pub fn serialize(value: &T) -> Result, encode::Error> where T: Serialize + Sized, { - let mut writer = Vec::new(); + let mut writer = Vec::with_capacity(2 * mem::size_of::()); // NOTE: this depends on the precise behavior of ciborium-io 0.2: // https://docs.rs/ciborium-io/0.2.0/src/ciborium_io/lib.rs.html#151 ciborium::ser::into_writer(value, &mut writer) @@ -145,15 +148,15 @@ mod test { assert_eq!(&deserialized, THE_STRING); } - #[derive(PartialEq, Serialize, Deserialize, Debug)] - struct TestStruct { - vec: Vec, - integer: u64, - float: f64, - } - #[test] fn serialize_struct() { + #[derive(PartialEq, Serialize, Deserialize, Debug)] + struct TestStruct { + vec: Vec, + integer: u64, + float: f64, + } + let the_struct = TestStruct { vec: vec![233, 123, 0, 12], integer: 4_242_424_242, @@ -163,4 +166,39 @@ mod test { let deserialized = deserialize::(&serialized).unwrap(); assert_eq!(deserialized, the_struct); } + + #[test] + fn serialize_array() { + let bytes = [0x55u8; 32]; + + let serialized = serialize(&bytes).expect("Could not serialize byte array."); + let deserialized = + deserialize::<[u8; 32]>(&serialized).expect("Could not deserialize byte array."); + + assert_eq!(deserialized, bytes); + } + + #[test] + fn serialize_array_in_struct() { + #[derive(Debug, Default, Deserialize, Serialize, PartialEq)] + struct ByteStruct { + bytes: [u8; 32], + } + + let value = ByteStruct::default(); + let serialized = serialize(&value).expect("Could not serialize byte struct."); + let deserialized = + deserialize::(&serialized).expect("Could not deserialize byte struct."); + + assert_eq!(deserialized, value); + + let value = ByteStruct { + bytes: [0x55u8; 32], + }; + let serialized = serialize(&value).expect("Could not serialize byte struct."); + let deserialized = + deserialize::(&serialized).expect("Could not deserialize byte struct."); + + assert_eq!(deserialized, value); + } } From 206da68fef8124ea64b5e91eff2bd2b166b23922 Mon Sep 17 00:00:00 2001 From: James Cape Date: Tue, 14 Mar 2023 11:13:46 -0700 Subject: [PATCH 8/9] Add dependencies, more struct tests. --- .github/settings.yml | 12 +++-- src/lib.rs | 109 ++++++++++++++++++++++++++++++++++--------- 2 files changed, 96 insertions(+), 25 deletions(-) diff --git a/.github/settings.yml b/.github/settings.yml index 06a5c3f..1c2b882 100644 --- a/.github/settings.yml +++ b/.github/settings.yml @@ -22,16 +22,20 @@ repository: labels: - name: github_actions color: '#000000' - description: Changes to github actions or dependencies + description: Pull requests that update github actions or dependencies - name: rust color: '#f74c00' - description: Changes to rust code or dependencies + description: Pull requests that update rust code or dependencies - name: python color: '#4584b6' - description: Changes to python code or dependencies + description: Pull requests that update python code or dependencies - name: go color: '#29beb0' - description: Changes to golang code or dependencies + description: Pull requests that update golang code or dependencies + + - name: dependencies + color: '#0366d6' + description: Pull requests that update a dependency file - name: size/XS color: '#00ed01' diff --git a/src/lib.rs b/src/lib.rs index 6282eea..e86c772 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -139,6 +139,14 @@ mod test { use alloc::{string::String, vec}; use serde::{Deserialize, Serialize}; + // A structure to test serialization and deserialization + #[derive(PartialEq, Serialize, Deserialize, Debug)] + struct TestStruct { + vec: Vec, + integer: u64, + float: f64, + } + #[test] fn serialize_string() { const THE_STRING: &str = "There goes the baker with his tray, like always"; @@ -150,13 +158,6 @@ mod test { #[test] fn serialize_struct() { - #[derive(PartialEq, Serialize, Deserialize, Debug)] - struct TestStruct { - vec: Vec, - integer: u64, - float: f64, - } - let the_struct = TestStruct { vec: vec![233, 123, 0, 12], integer: 4_242_424_242, @@ -178,27 +179,93 @@ mod test { assert_eq!(deserialized, bytes); } - #[test] - fn serialize_array_in_struct() { - #[derive(Debug, Default, Deserialize, Serialize, PartialEq)] - struct ByteStruct { - bytes: [u8; 32], - } + /// Single, named byte array in a struct + #[derive(Debug, Default, Deserialize, Serialize, PartialEq)] + struct ByteStruct { + bytes: [u8; 32], + } - let value = ByteStruct::default(); - let serialized = serialize(&value).expect("Could not serialize byte struct."); - let deserialized = + #[test] + fn struct_with_array() { + let expected = ByteStruct::default(); + let serialized = serialize(&expected).expect("Could not serialize byte struct."); + let actual = deserialize::(&serialized).expect("Could not deserialize byte struct."); - assert_eq!(deserialized, value); + assert_eq!(actual, expected); - let value = ByteStruct { + let expected = ByteStruct { bytes: [0x55u8; 32], }; - let serialized = serialize(&value).expect("Could not serialize byte struct."); - let deserialized = + let serialized = serialize(&expected).expect("Could not serialize byte struct."); + let actual = deserialize::(&serialized).expect("Could not deserialize byte struct."); - assert_eq!(deserialized, value); + assert_eq!(actual, expected); + } + + /// Unnamed byte array in structs + #[derive(Debug, Default, Deserialize, Serialize, PartialEq)] + struct NewtypeBytes([u8; 32]); + + #[test] + fn newtype_bytes() { + let expected = NewtypeBytes([0x55u8; 32]); + let serialized = serialize(&expected).expect("Could not serialize transparent struct."); + let actual = + deserialize::(&serialized).expect("Could not deserialize byte struct."); + assert_eq!(expected, actual); + } + + #[test] + fn newtype_bytes_vs_just_bytes() { + let expected = NewtypeBytes([0x55u8; 32]); + let serialized = serialize(&expected).expect("Could not serialize transparent struct."); + + let bytes = [0x55u8; 32]; + let bytes_serialized = serialize(&bytes).expect("Could not serialize byte array."); + assert_eq!(serialized, bytes_serialized); + + let actual = deserialize::(&bytes_serialized) + .expect("Could not deserialize byte array to struct."); + + assert_eq!(expected, actual); + } + + /// Tuple struct with unnamed byte arrays + #[derive(Debug, Default, Deserialize, Serialize, PartialEq)] + struct TupleStruct([u8; 32], [u8; 32]); + + #[test] + fn tuplestruct() { + let expected = TupleStruct([0x55u8; 32], [0xffu8; 32]); + let serialized = serialize(&expected).expect("Could not serialize transparent struct."); + let actual = + deserialize::(&serialized).expect("Could not deserialize byte struct."); + assert_eq!(expected, actual); + } + + #[derive(Debug, Default, Deserialize, Serialize, PartialEq)] + struct InnerStruct { + bytes: [u8; 32], + } + + #[derive(Debug, Default, Deserialize, Serialize, PartialEq)] + struct OuterStruct { + inner: InnerStruct, + } + + #[test] + fn nested_struct() { + let expected = OuterStruct { + inner: InnerStruct { + bytes: [0x55u8; 32], + }, + }; + let serialized = serialize(&expected).expect("Could not serialize transparent struct."); + let actual = + deserialize::(&serialized).expect("Could not deserialize byte struct."); + + assert_eq!(expected, actual); } } From 31fb1a985ef649d9a869f8ea1935e1cc9e89aa62 Mon Sep 17 00:00:00 2001 From: James Cape Date: Tue, 14 Mar 2023 11:15:53 -0700 Subject: [PATCH 9/9] Fix trailing space in dependencies label. --- .github/settings.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/settings.yml b/.github/settings.yml index 1c2b882..75a1d3d 100644 --- a/.github/settings.yml +++ b/.github/settings.yml @@ -35,7 +35,7 @@ labels: - name: dependencies color: '#0366d6' - description: Pull requests that update a dependency file + description: Pull requests that update a dependency file - name: size/XS color: '#00ed01'