From d5b6ff8d123d1c10a162727f8c537eda43272878 Mon Sep 17 00:00:00 2001 From: Dom Date: Sat, 5 Oct 2024 10:36:28 -0400 Subject: [PATCH 01/12] implementation --- bounded-collections/src/bounded_btree_map.rs | 126 ++++++++++++++++++- 1 file changed, 123 insertions(+), 3 deletions(-) diff --git a/bounded-collections/src/bounded_btree_map.rs b/bounded-collections/src/bounded_btree_map.rs index c3369c19..c1d09418 100644 --- a/bounded-collections/src/bounded_btree_map.rs +++ b/bounded-collections/src/bounded_btree_map.rs @@ -21,6 +21,11 @@ use crate::{Get, TryCollect}; use alloc::collections::BTreeMap; use codec::{Compact, Decode, Encode, MaxEncodedLen}; use core::{borrow::Borrow, marker::PhantomData, ops::Deref}; +#[cfg(feature = "serde")] +use serde::{ + de::{Error, MapAccess, Visitor}, + Deserialize, Deserializer, Serialize, +}; /// A bounded map based on a B-Tree. /// @@ -29,9 +34,74 @@ use core::{borrow::Borrow, marker::PhantomData, ops::Deref}; /// /// Unlike a standard `BTreeMap`, there is an enforced upper limit to the number of items in the /// map. All internal operations ensure this bound is respected. +#[cfg_attr(feature = "serde", derive(Serialize), serde(transparent))] #[derive(Encode, scale_info::TypeInfo)] #[scale_info(skip_type_params(S))] -pub struct BoundedBTreeMap(BTreeMap, PhantomData); +pub struct BoundedBTreeMap( + BTreeMap, + #[cfg_attr(feature = "serde", serde(skip_serializing))] PhantomData, +); + +#[cfg(feature = "serde")] +impl<'de, K, V, S: Get> Deserialize<'de> for BoundedBTreeMap +where + K: Deserialize<'de> + Ord, + V: Deserialize<'de>, + S: Clone, +{ + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + // Create a visitor to visit each element in the map + struct BTreeMapVisitor(PhantomData<(K, V, S)>); + + impl<'de, K, V, S> Visitor<'de> for BTreeMapVisitor + where + K: Deserialize<'de> + Ord, + V: Deserialize<'de>, + S: Get + Clone, + { + type Value = BTreeMap; + + fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result { + formatter.write_str("a map") + } + + fn visit_map(self, mut map: A) -> Result + where + A: MapAccess<'de>, + { + let size = map.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("map exceeds the size of the bounds")) + } else { + let mut values = BTreeMap::new(); + + while let Some(key) = map.next_key()? { + if values.len() >= max { + return Err(A::Error::custom("map exceeds the size of the bounds")); + } + let value = map.next_value()?; + values.insert(key, value); + } + + Ok(values) + } + } + } + + let visitor: BTreeMapVisitor = BTreeMapVisitor(PhantomData); + deserializer.deserialize_map(visitor).map(|v| { + BoundedBTreeMap::::try_from(v) + .map_err(|_| Error::custom("failed to create a BoundedBTreeMap from the provided map")) + })? + } +} impl Decode for BoundedBTreeMap where @@ -44,7 +114,7 @@ where // the len is too big. let len: u32 = >::decode(input)?.into(); if len > S::get() { - return Err("BoundedBTreeMap exceeds its limit".into()) + return Err("BoundedBTreeMap exceeds its limit".into()); } input.descend_ref()?; let inner = Result::from_iter((0..len).map(|_| Decode::decode(input)))?; @@ -403,7 +473,7 @@ where } } -#[cfg(test)] +#[cfg(all(test, feature = "std"))] mod test { use super::*; use crate::ConstU32; @@ -425,6 +495,56 @@ mod test { map_from_keys(keys).try_into().unwrap() } + #[test] + fn test_bounded_btreemap_serializer() { + let mut map = BoundedBTreeMap::>::new(); + map.try_insert(0, 100).unwrap(); + map.try_insert(1, 101).unwrap(); + map.try_insert(2, 102).unwrap(); + + let serialized = serde_json::to_string(&map).unwrap(); + assert_eq!(serialized, r#"{"0":100,"1":101,"2":102}"#); + } + + #[test] + fn test_bounded_btreemap_deserializer() { + let json_str = r#"{"0":100,"1":101,"2":102}"#; + let map: Result>, serde_json::Error> = serde_json::from_str(json_str); + assert!(map.is_ok()); + let map = map.unwrap(); + + assert_eq!(map.len(), 3); + assert_eq!(map.get(&0), Some(&100)); + assert_eq!(map.get(&1), Some(&101)); + assert_eq!(map.get(&2), Some(&102)); + } + + #[test] + fn test_bounded_btreemap_deserializer_bound() { + let json_str = r#"{"0":100,"1":101,"2":102}"#; + let map: Result>, serde_json::Error> = serde_json::from_str(json_str); + assert!(map.is_ok()); + let map = map.unwrap(); + + assert_eq!(map.len(), 3); + assert_eq!(map.get(&0), Some(&100)); + assert_eq!(map.get(&1), Some(&101)); + assert_eq!(map.get(&2), Some(&102)); + } + + #[test] + fn test_bounded_btreemap_deserializer_failed() { + let json_str = r#"{"0":100,"1":101,"2":102,"3":103,"4":104}"#; + let map: Result>, serde_json::Error> = serde_json::from_str(json_str); + + match map { + Err(e) => { + assert!(e.to_string().contains("map exceeds the size of the bounds")); + }, + _ => unreachable!("deserializer must raise error"), + } + } + #[test] fn encoding_same_as_unbounded_map() { let b = boundedmap_from_keys::>(&[1, 2, 3, 4, 5, 6]); From 9063c62f8a8dd9cecc4c5e7cc88a9a572b5e375b Mon Sep 17 00:00:00 2001 From: Dom Date: Sat, 5 Oct 2024 10:49:16 -0400 Subject: [PATCH 02/12] Changelog --- bounded-collections/CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/bounded-collections/CHANGELOG.md b/bounded-collections/CHANGELOG.md index 101c91ec..38a3a748 100644 --- a/bounded-collections/CHANGELOG.md +++ b/bounded-collections/CHANGELOG.md @@ -4,6 +4,9 @@ The format is based on [Keep a Changelog]. [Keep a Changelog]: http://keepachangelog.com/en/1.0.0/ +## [0.2.1] - 2024-10-05 +- Added `serde` support for `BoundedBTreeMap`. [#870](https://github.com/paritytech/parity-common/pull/870) + ## [0.2.0] - 2024-01-29 - Added `try_rotate_left` and `try_rotate_right` to `BoundedVec`. [#800](https://github.com/paritytech/parity-common/pull/800) From 257e18bd7226f59012f9265bd7bc55cd6c65bb5e Mon Sep 17 00:00:00 2001 From: Dom Date: Sat, 5 Oct 2024 10:49:23 -0400 Subject: [PATCH 03/12] Move tests down --- bounded-collections/src/bounded_btree_map.rs | 102 +++++++++---------- 1 file changed, 51 insertions(+), 51 deletions(-) diff --git a/bounded-collections/src/bounded_btree_map.rs b/bounded-collections/src/bounded_btree_map.rs index c1d09418..bb92e9f1 100644 --- a/bounded-collections/src/bounded_btree_map.rs +++ b/bounded-collections/src/bounded_btree_map.rs @@ -473,7 +473,7 @@ where } } -#[cfg(all(test, feature = "std"))] +#[cfg(test)] mod test { use super::*; use crate::ConstU32; @@ -495,56 +495,6 @@ mod test { map_from_keys(keys).try_into().unwrap() } - #[test] - fn test_bounded_btreemap_serializer() { - let mut map = BoundedBTreeMap::>::new(); - map.try_insert(0, 100).unwrap(); - map.try_insert(1, 101).unwrap(); - map.try_insert(2, 102).unwrap(); - - let serialized = serde_json::to_string(&map).unwrap(); - assert_eq!(serialized, r#"{"0":100,"1":101,"2":102}"#); - } - - #[test] - fn test_bounded_btreemap_deserializer() { - let json_str = r#"{"0":100,"1":101,"2":102}"#; - let map: Result>, serde_json::Error> = serde_json::from_str(json_str); - assert!(map.is_ok()); - let map = map.unwrap(); - - assert_eq!(map.len(), 3); - assert_eq!(map.get(&0), Some(&100)); - assert_eq!(map.get(&1), Some(&101)); - assert_eq!(map.get(&2), Some(&102)); - } - - #[test] - fn test_bounded_btreemap_deserializer_bound() { - let json_str = r#"{"0":100,"1":101,"2":102}"#; - let map: Result>, serde_json::Error> = serde_json::from_str(json_str); - assert!(map.is_ok()); - let map = map.unwrap(); - - assert_eq!(map.len(), 3); - assert_eq!(map.get(&0), Some(&100)); - assert_eq!(map.get(&1), Some(&101)); - assert_eq!(map.get(&2), Some(&102)); - } - - #[test] - fn test_bounded_btreemap_deserializer_failed() { - let json_str = r#"{"0":100,"1":101,"2":102,"3":103,"4":104}"#; - let map: Result>, serde_json::Error> = serde_json::from_str(json_str); - - match map { - Err(e) => { - assert!(e.to_string().contains("map exceeds the size of the bounds")); - }, - _ => unreachable!("deserializer must raise error"), - } - } - #[test] fn encoding_same_as_unbounded_map() { let b = boundedmap_from_keys::>(&[1, 2, 3, 4, 5, 6]); @@ -782,4 +732,54 @@ mod test { } let _foo = Foo::default(); } + + #[test] + fn test_bounded_btreemap_serializer() { + let mut map = BoundedBTreeMap::>::new(); + map.try_insert(0, 100).unwrap(); + map.try_insert(1, 101).unwrap(); + map.try_insert(2, 102).unwrap(); + + let serialized = serde_json::to_string(&map).unwrap(); + assert_eq!(serialized, r#"{"0":100,"1":101,"2":102}"#); + } + + #[test] + fn test_bounded_btreemap_deserializer() { + let json_str = r#"{"0":100,"1":101,"2":102}"#; + let map: Result>, serde_json::Error> = serde_json::from_str(json_str); + assert!(map.is_ok()); + let map = map.unwrap(); + + assert_eq!(map.len(), 3); + assert_eq!(map.get(&0), Some(&100)); + assert_eq!(map.get(&1), Some(&101)); + assert_eq!(map.get(&2), Some(&102)); + } + + #[test] + fn test_bounded_btreemap_deserializer_bound() { + let json_str = r#"{"0":100,"1":101,"2":102}"#; + let map: Result>, serde_json::Error> = serde_json::from_str(json_str); + assert!(map.is_ok()); + let map = map.unwrap(); + + assert_eq!(map.len(), 3); + assert_eq!(map.get(&0), Some(&100)); + assert_eq!(map.get(&1), Some(&101)); + assert_eq!(map.get(&2), Some(&102)); + } + + #[test] + fn test_bounded_btreemap_deserializer_failed() { + let json_str = r#"{"0":100,"1":101,"2":102,"3":103,"4":104}"#; + let map: Result>, serde_json::Error> = serde_json::from_str(json_str); + + match map { + Err(e) => { + assert!(e.to_string().contains("map exceeds the size of the bounds")); + }, + _ => unreachable!("deserializer must raise error"), + } + } } From f94330f942e9ef499d9d22ba95ec8c9a9d6ee6ce Mon Sep 17 00:00:00 2001 From: Dom Date: Sat, 5 Oct 2024 16:57:36 -0400 Subject: [PATCH 04/12] Remove clone bound --- bounded-collections/src/bounded_btree_map.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/bounded-collections/src/bounded_btree_map.rs b/bounded-collections/src/bounded_btree_map.rs index bb92e9f1..39323ab8 100644 --- a/bounded-collections/src/bounded_btree_map.rs +++ b/bounded-collections/src/bounded_btree_map.rs @@ -47,7 +47,6 @@ impl<'de, K, V, S: Get> Deserialize<'de> for BoundedBTreeMap where K: Deserialize<'de> + Ord, V: Deserialize<'de>, - S: Clone, { fn deserialize(deserializer: D) -> Result where @@ -60,7 +59,7 @@ where where K: Deserialize<'de> + Ord, V: Deserialize<'de>, - S: Get + Clone, + S: Get, { type Value = BTreeMap; From 6f77ce54bcc694d23886dfb0560b068b5ee19cd0 Mon Sep 17 00:00:00 2001 From: "polka.dom" Date: Sun, 6 Oct 2024 11:30:42 -0400 Subject: [PATCH 05/12] Set date to Monday Co-authored-by: ordian --- bounded-collections/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bounded-collections/CHANGELOG.md b/bounded-collections/CHANGELOG.md index 38a3a748..cee52405 100644 --- a/bounded-collections/CHANGELOG.md +++ b/bounded-collections/CHANGELOG.md @@ -4,7 +4,7 @@ The format is based on [Keep a Changelog]. [Keep a Changelog]: http://keepachangelog.com/en/1.0.0/ -## [0.2.1] - 2024-10-05 +## [0.2.1] - 2024-10-07 - Added `serde` support for `BoundedBTreeMap`. [#870](https://github.com/paritytech/parity-common/pull/870) ## [0.2.0] - 2024-01-29 From ea37e7ab877d737add8610e00b6881355dbdd892 Mon Sep 17 00:00:00 2001 From: "polka.dom" Date: Sun, 6 Oct 2024 11:43:43 -0400 Subject: [PATCH 06/12] Fix match that's always okay MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bastian Köcher --- bounded-collections/src/bounded_btree_map.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/bounded-collections/src/bounded_btree_map.rs b/bounded-collections/src/bounded_btree_map.rs index 39323ab8..25ba9840 100644 --- a/bounded-collections/src/bounded_btree_map.rs +++ b/bounded-collections/src/bounded_btree_map.rs @@ -72,10 +72,7 @@ where A: MapAccess<'de>, { let size = map.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")), - }; + let max = S::get() as usize; if size > max { Err(A::Error::custom("map exceeds the size of the bounds")) } else { From 512b7c448c30b4301b4ac69fa5c3f4175facdf19 Mon Sep 17 00:00:00 2001 From: Dom Date: Mon, 7 Oct 2024 10:06:18 -0400 Subject: [PATCH 07/12] Update Cargo.toml --- bounded-collections/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bounded-collections/Cargo.toml b/bounded-collections/Cargo.toml index f9496eb8..674b934f 100644 --- a/bounded-collections/Cargo.toml +++ b/bounded-collections/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "bounded-collections" -version = "0.2.0" +version = "0.2.1" authors = ["Parity Technologies "] license = "MIT OR Apache-2.0" homepage = "https://github.com/paritytech/parity-common" From 1f5c9d4b4631763aa569ebc48c5710b335f7429e Mon Sep 17 00:00:00 2001 From: Dom Date: Mon, 7 Oct 2024 16:27:40 -0400 Subject: [PATCH 08/12] Move serde tests behind feature flag --- bounded-collections/src/bounded_btree_map.rs | 99 ++++++++++---------- 1 file changed, 52 insertions(+), 47 deletions(-) diff --git a/bounded-collections/src/bounded_btree_map.rs b/bounded-collections/src/bounded_btree_map.rs index 25ba9840..f101f4aa 100644 --- a/bounded-collections/src/bounded_btree_map.rs +++ b/bounded-collections/src/bounded_btree_map.rs @@ -729,53 +729,58 @@ mod test { let _foo = Foo::default(); } - #[test] - fn test_bounded_btreemap_serializer() { - let mut map = BoundedBTreeMap::>::new(); - map.try_insert(0, 100).unwrap(); - map.try_insert(1, 101).unwrap(); - map.try_insert(2, 102).unwrap(); - - let serialized = serde_json::to_string(&map).unwrap(); - assert_eq!(serialized, r#"{"0":100,"1":101,"2":102}"#); - } - - #[test] - fn test_bounded_btreemap_deserializer() { - let json_str = r#"{"0":100,"1":101,"2":102}"#; - let map: Result>, serde_json::Error> = serde_json::from_str(json_str); - assert!(map.is_ok()); - let map = map.unwrap(); - - assert_eq!(map.len(), 3); - assert_eq!(map.get(&0), Some(&100)); - assert_eq!(map.get(&1), Some(&101)); - assert_eq!(map.get(&2), Some(&102)); - } - - #[test] - fn test_bounded_btreemap_deserializer_bound() { - let json_str = r#"{"0":100,"1":101,"2":102}"#; - let map: Result>, serde_json::Error> = serde_json::from_str(json_str); - assert!(map.is_ok()); - let map = map.unwrap(); - - assert_eq!(map.len(), 3); - assert_eq!(map.get(&0), Some(&100)); - assert_eq!(map.get(&1), Some(&101)); - assert_eq!(map.get(&2), Some(&102)); - } - - #[test] - fn test_bounded_btreemap_deserializer_failed() { - let json_str = r#"{"0":100,"1":101,"2":102,"3":103,"4":104}"#; - let map: Result>, serde_json::Error> = serde_json::from_str(json_str); - - match map { - Err(e) => { - assert!(e.to_string().contains("map exceeds the size of the bounds")); - }, - _ => unreachable!("deserializer must raise error"), + #[cfg(feature = "serde")] + mod serde { + use super::*; + + #[test] + fn test_bounded_btreemap_serializer() { + let mut map = BoundedBTreeMap::>::new(); + map.try_insert(0, 100).unwrap(); + map.try_insert(1, 101).unwrap(); + map.try_insert(2, 102).unwrap(); + + let serialized = serde_json::to_string(&map).unwrap(); + assert_eq!(serialized, r#"{"0":100,"1":101,"2":102}"#); + } + + #[test] + fn test_bounded_btreemap_deserializer() { + let json_str = r#"{"0":100,"1":101,"2":102}"#; + let map: Result>, serde_json::Error> = serde_json::from_str(json_str); + assert!(map.is_ok()); + let map = map.unwrap(); + + assert_eq!(map.len(), 3); + assert_eq!(map.get(&0), Some(&100)); + assert_eq!(map.get(&1), Some(&101)); + assert_eq!(map.get(&2), Some(&102)); + } + + #[test] + fn test_bounded_btreemap_deserializer_bound() { + let json_str = r#"{"0":100,"1":101,"2":102}"#; + let map: Result>, serde_json::Error> = serde_json::from_str(json_str); + assert!(map.is_ok()); + let map = map.unwrap(); + + assert_eq!(map.len(), 3); + assert_eq!(map.get(&0), Some(&100)); + assert_eq!(map.get(&1), Some(&101)); + assert_eq!(map.get(&2), Some(&102)); + } + + #[test] + fn test_bounded_btreemap_deserializer_failed() { + let json_str = r#"{"0":100,"1":101,"2":102,"3":103,"4":104}"#; + let map: Result>, serde_json::Error> = serde_json::from_str(json_str); + + match map { + Err(e) => { + assert!(e.to_string().contains("map exceeds the size of the bounds")); + }, + _ => unreachable!("deserializer must raise error"), + } } } } From 73b8e3f87b7ff6f5777719f8220cbf3e2d7e169b Mon Sep 17 00:00:00 2001 From: Dom Date: Mon, 7 Oct 2024 16:39:54 -0400 Subject: [PATCH 09/12] Add ToString --- bounded-collections/src/bounded_btree_map.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/bounded-collections/src/bounded_btree_map.rs b/bounded-collections/src/bounded_btree_map.rs index f101f4aa..fbbdccca 100644 --- a/bounded-collections/src/bounded_btree_map.rs +++ b/bounded-collections/src/bounded_btree_map.rs @@ -732,6 +732,7 @@ mod test { #[cfg(feature = "serde")] mod serde { use super::*; + use crate::alloc::string::ToString as _; #[test] fn test_bounded_btreemap_serializer() { From 2d075c60bb52ad18c775e60051643502d40f3686 Mon Sep 17 00:00:00 2001 From: Dom Date: Mon, 7 Oct 2024 16:43:47 -0400 Subject: [PATCH 10/12] Remove underscore --- bounded-collections/src/bounded_btree_map.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bounded-collections/src/bounded_btree_map.rs b/bounded-collections/src/bounded_btree_map.rs index fbbdccca..2808af5e 100644 --- a/bounded-collections/src/bounded_btree_map.rs +++ b/bounded-collections/src/bounded_btree_map.rs @@ -732,7 +732,7 @@ mod test { #[cfg(feature = "serde")] mod serde { use super::*; - use crate::alloc::string::ToString as _; + use crate::alloc::string::ToString; #[test] fn test_bounded_btreemap_serializer() { From db8dba41c3afe5b1274b812207e94aa2a69d2865 Mon Sep 17 00:00:00 2001 From: ordian Date: Tue, 8 Oct 2024 10:12:37 +0200 Subject: [PATCH 11/12] Update bounded-collections/CHANGELOG.md --- bounded-collections/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bounded-collections/CHANGELOG.md b/bounded-collections/CHANGELOG.md index cee52405..f6e01625 100644 --- a/bounded-collections/CHANGELOG.md +++ b/bounded-collections/CHANGELOG.md @@ -4,7 +4,7 @@ The format is based on [Keep a Changelog]. [Keep a Changelog]: http://keepachangelog.com/en/1.0.0/ -## [0.2.1] - 2024-10-07 +## [0.2.1] - 2024-10-08 - Added `serde` support for `BoundedBTreeMap`. [#870](https://github.com/paritytech/parity-common/pull/870) ## [0.2.0] - 2024-01-29 From 1ff996a49f2ee2bd8dc6c23d42cfed2bfda41d46 Mon Sep 17 00:00:00 2001 From: ordian Date: Tue, 8 Oct 2024 10:41:17 +0200 Subject: [PATCH 12/12] fmt --- bounded-collections/src/bounded_btree_map.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/bounded-collections/src/bounded_btree_map.rs b/bounded-collections/src/bounded_btree_map.rs index 2808af5e..6a441941 100644 --- a/bounded-collections/src/bounded_btree_map.rs +++ b/bounded-collections/src/bounded_btree_map.rs @@ -740,42 +740,42 @@ mod test { map.try_insert(0, 100).unwrap(); map.try_insert(1, 101).unwrap(); map.try_insert(2, 102).unwrap(); - + let serialized = serde_json::to_string(&map).unwrap(); assert_eq!(serialized, r#"{"0":100,"1":101,"2":102}"#); } - + #[test] fn test_bounded_btreemap_deserializer() { let json_str = r#"{"0":100,"1":101,"2":102}"#; let map: Result>, serde_json::Error> = serde_json::from_str(json_str); assert!(map.is_ok()); let map = map.unwrap(); - + assert_eq!(map.len(), 3); assert_eq!(map.get(&0), Some(&100)); assert_eq!(map.get(&1), Some(&101)); assert_eq!(map.get(&2), Some(&102)); } - + #[test] fn test_bounded_btreemap_deserializer_bound() { let json_str = r#"{"0":100,"1":101,"2":102}"#; let map: Result>, serde_json::Error> = serde_json::from_str(json_str); assert!(map.is_ok()); let map = map.unwrap(); - + assert_eq!(map.len(), 3); assert_eq!(map.get(&0), Some(&100)); assert_eq!(map.get(&1), Some(&101)); assert_eq!(map.get(&2), Some(&102)); } - + #[test] fn test_bounded_btreemap_deserializer_failed() { let json_str = r#"{"0":100,"1":101,"2":102,"3":103,"4":104}"#; let map: Result>, serde_json::Error> = serde_json::from_str(json_str); - + match map { Err(e) => { assert!(e.to_string().contains("map exceeds the size of the bounds"));