From b2d0e93f8ce9705f627b8d1c68088bb1b6e92043 Mon Sep 17 00:00:00 2001 From: Gwilym Inzani Date: Wed, 30 Oct 2024 10:35:48 +0000 Subject: [PATCH] Add serde support to agb_hashmap::HashMap --- agb-hashmap/Cargo.toml | 2 ++ agb-hashmap/src/lib.rs | 3 ++ agb-hashmap/src/serde.rs | 76 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 81 insertions(+) create mode 100644 agb-hashmap/src/serde.rs diff --git a/agb-hashmap/Cargo.toml b/agb-hashmap/Cargo.toml index f4f91ac7..fba3c796 100644 --- a/agb-hashmap/Cargo.toml +++ b/agb-hashmap/Cargo.toml @@ -9,9 +9,11 @@ exclude = ["/benches"] [features] allocator_api = [] +serde = ["dep:serde"] [dependencies] rustc-hash = { version = "1", default-features = false } +serde = { version = "1", default-features = false, optional = true } [dev-dependencies] rand = { version = "0.8", default-features = false, features = ["small_rng"] } diff --git a/agb-hashmap/src/lib.rs b/agb-hashmap/src/lib.rs index 940df370..30c13fd0 100644 --- a/agb-hashmap/src/lib.rs +++ b/agb-hashmap/src/lib.rs @@ -44,6 +44,9 @@ mod allocate { pub(crate) use core::alloc::Allocator; } +#[cfg(feature = "serde")] +mod serde; + use core::{ borrow::Borrow, fmt::Debug, diff --git a/agb-hashmap/src/serde.rs b/agb-hashmap/src/serde.rs new file mode 100644 index 00000000..e014120f --- /dev/null +++ b/agb-hashmap/src/serde.rs @@ -0,0 +1,76 @@ +use core::{hash::Hash, marker::PhantomData}; +use serde::{ + de::{MapAccess, Visitor}, + ser::SerializeMap, + Deserialize, Serialize, +}; + +use crate::{ClonableAllocator, HashMap}; + +impl Serialize + for HashMap +{ + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut map = serializer.serialize_map(Some(self.len()))?; + + for (key, value) in self { + map.serialize_entry(key, value)?; + } + + map.end() + } +} + +impl<'de, K, V> Deserialize<'de> for HashMap +where + K: Deserialize<'de> + Hash + Eq, + V: Deserialize<'de>, +{ + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + deserializer.deserialize_map(HashMapVisitor::new()) + } +} + +#[derive(Default)] +struct HashMapVisitor { + _marker: PhantomData HashMap>, +} + +impl HashMapVisitor { + fn new() -> Self { + Self { + _marker: PhantomData, + } + } +} + +impl<'de, K, V> Visitor<'de> for HashMapVisitor +where + K: Deserialize<'de> + Hash + Eq, + V: Deserialize<'de>, +{ + type Value = HashMap; + + fn expecting(&self, formatter: &mut alloc::fmt::Formatter) -> alloc::fmt::Result { + formatter.write_str("an agb::HashMap") + } + + fn visit_map(self, mut access: M) -> Result + where + M: MapAccess<'de>, + { + let mut map = HashMap::with_capacity(access.size_hint().unwrap_or(8)); + + while let Some((key, value)) = access.next_entry()? { + map.insert(key, value); + } + + Ok(map) + } +}