mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-07-23 21:15:41 +00:00
serde::{Serialize, Deserialize} for BoundedVec (#11314)
* serde::{Serialize, Deserialize} for BoundedVec
* Apply suggestions from code review
* FMT
* 🤦
Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com>
Co-authored-by: Bastian Köcher <info@kchr.de>
This commit is contained in:
Generated
+1
@@ -2296,6 +2296,7 @@ dependencies = [
|
|||||||
"pretty_assertions",
|
"pretty_assertions",
|
||||||
"scale-info",
|
"scale-info",
|
||||||
"serde",
|
"serde",
|
||||||
|
"serde_json",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"sp-arithmetic",
|
"sp-arithmetic",
|
||||||
"sp-core",
|
"sp-core",
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ sp-core-hashing-proc-macro = { version = "5.0.0", path = "../../primitives/core/
|
|||||||
k256 = { version = "0.10.4", default-features = false, features = ["ecdsa"] }
|
k256 = { version = "0.10.4", default-features = false, features = ["ecdsa"] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
serde_json = "1.0.79"
|
||||||
assert_matches = "1.3.0"
|
assert_matches = "1.3.0"
|
||||||
pretty_assertions = "1.2.1"
|
pretty_assertions = "1.2.1"
|
||||||
frame-system = { version = "4.0.0-dev", path = "../system" }
|
frame-system = { version = "4.0.0-dev", path = "../system" }
|
||||||
|
|||||||
@@ -28,6 +28,11 @@ use core::{
|
|||||||
ops::{Deref, Index, IndexMut, RangeBounds},
|
ops::{Deref, Index, IndexMut, RangeBounds},
|
||||||
slice::SliceIndex,
|
slice::SliceIndex,
|
||||||
};
|
};
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
use serde::{
|
||||||
|
de::{Error, SeqAccess, Visitor},
|
||||||
|
Deserialize, Deserializer, Serialize,
|
||||||
|
};
|
||||||
use sp_std::{marker::PhantomData, prelude::*};
|
use sp_std::{marker::PhantomData, prelude::*};
|
||||||
|
|
||||||
/// A bounded vector.
|
/// A bounded vector.
|
||||||
@@ -37,9 +42,67 @@ use sp_std::{marker::PhantomData, prelude::*};
|
|||||||
///
|
///
|
||||||
/// As the name suggests, the length of the queue is always bounded. All internal operations ensure
|
/// As the name suggests, the length of the queue is always bounded. All internal operations ensure
|
||||||
/// this bound is respected.
|
/// this bound is respected.
|
||||||
|
#[cfg_attr(feature = "std", derive(Serialize), serde(transparent))]
|
||||||
#[derive(Encode, scale_info::TypeInfo)]
|
#[derive(Encode, scale_info::TypeInfo)]
|
||||||
#[scale_info(skip_type_params(S))]
|
#[scale_info(skip_type_params(S))]
|
||||||
pub struct BoundedVec<T, S>(Vec<T>, PhantomData<S>);
|
pub struct BoundedVec<T, S>(
|
||||||
|
Vec<T>,
|
||||||
|
#[cfg_attr(feature = "std", serde(skip_serializing))] PhantomData<S>,
|
||||||
|
);
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
impl<'de, T, S: Get<u32>> Deserialize<'de> for BoundedVec<T, S>
|
||||||
|
where
|
||||||
|
T: Deserialize<'de>,
|
||||||
|
{
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
struct VecVisitor<T, S: Get<u32>>(PhantomData<(T, S)>);
|
||||||
|
|
||||||
|
impl<'de, T, S: Get<u32>> Visitor<'de> for VecVisitor<T, S>
|
||||||
|
where
|
||||||
|
T: Deserialize<'de>,
|
||||||
|
{
|
||||||
|
type Value = Vec<T>;
|
||||||
|
|
||||||
|
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
|
formatter.write_str("a sequence")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
|
||||||
|
where
|
||||||
|
A: SeqAccess<'de>,
|
||||||
|
{
|
||||||
|
let size = seq.size_hint().unwrap_or(0);
|
||||||
|
let max = match usize::try_from(S::get()) {
|
||||||
|
Ok(n) => n,
|
||||||
|
Err(_) => return Err(A::Error::custom("can't convert to usize")),
|
||||||
|
};
|
||||||
|
if size > max {
|
||||||
|
Err(A::Error::custom("out of bounds"))
|
||||||
|
} else {
|
||||||
|
let mut values = Vec::with_capacity(size);
|
||||||
|
|
||||||
|
while let Some(value) = seq.next_element()? {
|
||||||
|
values.push(value);
|
||||||
|
if values.len() > max {
|
||||||
|
return Err(A::Error::custom("out of bounds"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(values)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let visitor: VecVisitor<T, S> = VecVisitor(PhantomData);
|
||||||
|
deserializer
|
||||||
|
.deserialize_seq(visitor)
|
||||||
|
.map(|v| BoundedVec::<T, S>::try_from(v).map_err(|_| Error::custom("out of bounds")))?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A bounded slice.
|
/// A bounded slice.
|
||||||
///
|
///
|
||||||
@@ -908,4 +971,31 @@ pub mod test {
|
|||||||
assert!(b.try_extend(vec![4, 5, 6].into_iter()).is_err());
|
assert!(b.try_extend(vec![4, 5, 6].into_iter()).is_err());
|
||||||
assert_eq!(*b, vec![1, 2, 3]);
|
assert_eq!(*b, vec![1, 2, 3]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_serializer() {
|
||||||
|
let c: BoundedVec<u32, ConstU32<6>> = bounded_vec![0, 1, 2];
|
||||||
|
assert_eq!(serde_json::json!(&c).to_string(), r#"[0,1,2]"#);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_deserializer() {
|
||||||
|
let c: BoundedVec<u32, ConstU32<6>> = serde_json::from_str(r#"[0,1,2]"#).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(c.len(), 3);
|
||||||
|
assert_eq!(c[0], 0);
|
||||||
|
assert_eq!(c[1], 1);
|
||||||
|
assert_eq!(c[2], 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_deserializer_failed() {
|
||||||
|
let c: Result<BoundedVec<u32, ConstU32<4>>, serde_json::error::Error> =
|
||||||
|
serde_json::from_str(r#"[0,1,2,3,4,5]"#);
|
||||||
|
|
||||||
|
match c {
|
||||||
|
Err(msg) => assert_eq!(msg.to_string(), "out of bounds at line 1 column 11"),
|
||||||
|
_ => unreachable!("deserializer must raise error"),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user