Skip to content

Commit

Permalink
feat(gadget-blueprint-serde)!: change array/tuple serialization
Browse files Browse the repository at this point in the history
Previously, `Vec<T>`, `[T; N]`, and `(T, T, T)` would all serialize to `Field::List`. Now the following happens:

* `Vec<T>` -> `Field::List`
* `[T; N]` **OR** `(T, T, T)` -> `Field::Array`
  * The major change is that homogeneous tuples are treated as arrays now
* `(X, Y, Z)` -> Error (for now), waiting for tuple support in the runtime.
  • Loading branch information
Serial-ATA committed Nov 13, 2024
1 parent aaa8577 commit a073a49
Show file tree
Hide file tree
Showing 4 changed files with 249 additions and 2 deletions.
7 changes: 7 additions & 0 deletions blueprint-serde/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
}
Expand Down Expand Up @@ -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),
}
Expand Down
2 changes: 1 addition & 1 deletion blueprint-serde/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
52 changes: 51 additions & 1 deletion blueprint-serde/src/ser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,52 @@ pub struct SerializeSeq<'a> {
vec: Vec<Field<AccountId32>>,
}

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<AccountId32>;
type Error = crate::error::Error;
Expand Down Expand Up @@ -232,7 +278,11 @@ impl ser::SerializeTuple for SerializeSeq<'_> {

#[inline]
fn end(self) -> Result<Self::Ok> {
<SerializeSeq<'_> as ser::SerializeSeq>::end(self)
if self.is_homogeneous() {
return Ok(Field::Array(BoundedVec(self.vec)));
}

Err(crate::error::Error::HeterogeneousTuple)
}
}

Expand Down
190 changes: 190 additions & 0 deletions blueprint-serde/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<AccountId32> {
Field::List(BoundedVec(vec![
Field::Uint32(1),
Field::Uint32(2),
Field::Uint32(3),
]))
}

#[test]
fn test_ser_vec() {
let vec: Vec<u32> = 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<u32> = 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<u32> = from_field(expected_vec_field()).unwrap();
assert_eq!(vec, vec_de);
}

fn expected_array_field() -> Field<AccountId32> {
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<AccountId32> {
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<AccountId32> {
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);
}
}

0 comments on commit a073a49

Please sign in to comment.