diff --git a/blueprint-serde/src/error.rs b/blueprint-serde/src/error.rs index 01087eac..943ddfa0 100644 --- a/blueprint-serde/src/error.rs +++ b/blueprint-serde/src/error.rs @@ -48,6 +48,7 @@ pub enum Error { UnsupportedType(UnsupportedType), /// Attempting to deserialize a [`char`] from a [`Field::String`](crate::Field::String) BadCharLength(usize), + HeterogeneousTuple, FromUtf8Error(alloc::string::FromUtf8Error), Other(String), } @@ -79,6 +80,12 @@ impl Display for Error { Error::BadCharLength(len) => { write!(f, "String contains {len} characters, expected 1") } + Error::HeterogeneousTuple => { + write!( + f, + "Attempted to serialize heterogeneous tuple, not currently supported" + ) + } Error::FromUtf8Error(e) => write!(f, "{e}"), Error::Other(msg) => write!(f, "{}", msg), } diff --git a/blueprint-serde/src/lib.rs b/blueprint-serde/src/lib.rs index 2aedc1fd..c1df4d12 100644 --- a/blueprint-serde/src/lib.rs +++ b/blueprint-serde/src/lib.rs @@ -11,7 +11,7 @@ //! use gadget_blueprint_serde::{new_bounded_string, BoundedVec, Field}; //! use serde::{Deserialize, Serialize}; //! -//! #[derive(Serialize, Deserialize)] +//! #[derive(PartialEq, Debug, Serialize, Deserialize)] //! struct Person { //! name: String, //! age: u8, diff --git a/blueprint-serde/src/ser.rs b/blueprint-serde/src/ser.rs index 9f2d600c..04b02036 100644 --- a/blueprint-serde/src/ser.rs +++ b/blueprint-serde/src/ser.rs @@ -200,6 +200,52 @@ pub struct SerializeSeq<'a> { vec: Vec>, } +impl SerializeSeq<'_> { + fn is_homogeneous(&self) -> bool { + if self.vec.is_empty() { + return true; + } + + macro_rules! homogeneous_check { + ($($field:pat),+ $(,)?) => { + paste::paste! { + match &self.vec[0] { + $($field => { self.vec.iter().all(|f| matches!(f, $field)) },)+ + Field::Struct(name, fields) => { + self.vec.iter().all(|f| { + match f { + Field::Struct(n, f) => { + n == name && fields == f + }, + _ => false, + } + }) + } + } + } + } + } + + homogeneous_check!( + Field::None, + Field::Bool(_), + Field::Uint8(_), + Field::Int8(_), + Field::Uint16(_), + Field::Int16(_), + Field::Uint32(_), + Field::Int32(_), + Field::Uint64(_), + Field::Int64(_), + Field::String(_), + Field::Bytes(_), + Field::Array(_), + Field::List(_), + Field::AccountId(_), + ) + } +} + impl ser::SerializeSeq for SerializeSeq<'_> { type Ok = Field; type Error = crate::error::Error; @@ -232,7 +278,11 @@ impl ser::SerializeTuple for SerializeSeq<'_> { #[inline] fn end(self) -> Result { - as ser::SerializeSeq>::end(self) + if self.is_homogeneous() { + return Ok(Field::Array(BoundedVec(self.vec))); + } + + Err(crate::error::Error::HeterogeneousTuple) } } diff --git a/blueprint-serde/src/tests.rs b/blueprint-serde/src/tests.rs index 2f300bc2..e5cc58b8 100644 --- a/blueprint-serde/src/tests.rs +++ b/blueprint-serde/src/tests.rs @@ -451,3 +451,193 @@ mod primitives { i64 => 0, Token::I64, Field::Int64; ); } + +mod sequences { + use super::*; + use alloc::vec::Vec; + + fn expected_vec_field() -> Field { + Field::List(BoundedVec(vec![ + Field::Uint32(1), + Field::Uint32(2), + Field::Uint32(3), + ])) + } + + #[test] + fn test_ser_vec() { + let vec: Vec = vec![1, 2, 3]; + + assert_ser_tokens( + &vec, + &[ + Token::Seq { len: Some(3) }, + Token::U32(1), + Token::U32(2), + Token::U32(3), + Token::SeqEnd, + ], + ); + + let field = to_field(&vec).unwrap(); + assert_eq!(field, expected_vec_field()); + } + + #[test] + fn test_de_vec() { + let vec: Vec = vec![1, 2, 3]; + + assert_de_tokens( + &vec, + &[ + Token::Seq { len: Some(3) }, + Token::U32(1), + Token::U32(2), + Token::U32(3), + Token::SeqEnd, + ], + ); + + let vec_de: Vec = from_field(expected_vec_field()).unwrap(); + assert_eq!(vec, vec_de); + } + + fn expected_array_field() -> Field { + Field::Array(BoundedVec(vec![ + Field::Uint32(1), + Field::Uint32(2), + Field::Uint32(3), + ])) + } + + #[test] + fn test_ser_array() { + let array: [u32; 3] = [1, 2, 3]; + + assert_ser_tokens( + &array, + &[ + Token::Tuple { len: 3 }, + Token::U32(1), + Token::U32(2), + Token::U32(3), + Token::TupleEnd, + ], + ); + + let field = to_field(&array).unwrap(); + assert_eq!(field, expected_array_field()); + } + + #[test] + fn test_de_array() { + let array: [u32; 3] = [1, 2, 3]; + + assert_de_tokens( + &array, + &[ + Token::Tuple { len: 3 }, + Token::U32(1), + Token::U32(2), + Token::U32(3), + Token::TupleEnd, + ], + ); + + let array_de: [u32; 3] = from_field(expected_array_field()).unwrap(); + assert_eq!(array, array_de); + } + + fn expected_same_type_field() -> Field { + Field::Array(BoundedVec(vec![ + Field::Uint32(1u32), + Field::Uint32(2u32), + Field::Uint32(3u32), + ])) + } + + #[test] + fn test_ser_tuple_same_type() { + let tuple: (u32, u32, u32) = (1, 2, 3); + + assert_ser_tokens( + &tuple, + &[ + Token::Tuple { len: 3 }, + Token::U32(1), + Token::U32(2), + Token::U32(3), + Token::TupleEnd, + ], + ); + + let field = to_field(&tuple).unwrap(); + assert_eq!(field, expected_same_type_field()); + } + + #[test] + fn test_de_tuple_same_type() { + let tuple: (u32, u32, u32) = (1, 2, 3); + + assert_de_tokens( + &tuple, + &[ + Token::Tuple { len: 3 }, + Token::U32(1), + Token::U32(2), + Token::U32(3), + Token::TupleEnd, + ], + ); + + let tuple_de: (u32, u32, u32) = from_field(expected_same_type_field()).unwrap(); + assert_eq!(tuple, tuple_de); + } + + fn expected_different_type_field() -> Field { + Field::Array(BoundedVec(vec![ + Field::Uint32(1u32), + Field::Uint16(2u16), + Field::Uint8(3u8), + ])) + } + + #[test] + #[should_panic = "HeterogeneousTuple"] // TODO: Support heterogeneous tuples + fn test_ser_tuple_different_type() { + let tuple: (u32, u16, u8) = (1, 2, 3); + + assert_ser_tokens( + &tuple, + &[ + Token::Tuple { len: 3 }, + Token::U32(1), + Token::U16(2), + Token::U8(3), + Token::TupleEnd, + ], + ); + + let field = to_field(&tuple).unwrap(); + assert_eq!(field, expected_different_type_field()); + } + + #[test] + fn test_de_tuple_different_type() { + let tuple: (u32, u16, u8) = (1, 2, 3); + + assert_de_tokens( + &tuple, + &[ + Token::Tuple { len: 3 }, + Token::U32(1), + Token::U16(2), + Token::U8(3), + Token::TupleEnd, + ], + ); + + let tuple_de: (u32, u16, u8) = from_field(expected_different_type_field()).unwrap(); + assert_eq!(tuple, tuple_de); + } +}