// This file is part of ICU4X. For terms of use, please see the file // called LICENSE at the top level of the ICU4X source tree // (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ). use super::LiteMap; use crate::store::*; use core::fmt; use core::marker::PhantomData; use serde::de::{MapAccess, SeqAccess, Visitor}; use serde::{Deserialize, Deserializer}; #[cfg(feature = "serde")] use serde::{ser::SerializeMap, Serialize, Serializer}; #[cfg(feature = "serde")] impl Serialize for LiteMap where K: Serialize, V: Serialize, R: Store + Serialize, { fn serialize(&self, serializer: S) -> Result where S: Serializer, { // Many human-readable formats don't support values other // than numbers and strings as map keys. For them, we can serialize // as a vec of tuples instead if serializer.is_human_readable() { if let Some((ref k, _)) = self.values.lm_get(0) { if !super::serde_helpers::is_num_or_string(k) { return self.values.serialize(serializer); } // continue to regular serialization } } let mut map = serializer.serialize_map(Some(self.len()))?; let mut i = 0; while i < self.values.lm_len() { #[allow(clippy::unwrap_used)] // i is in range let (k, v) = self.values.lm_get(i).unwrap(); map.serialize_entry(k, v)?; i += 1; } map.end() } } /// Modified example from https://serde.rs/deserialize-map.html #[allow(clippy::type_complexity)] struct LiteMapVisitor { marker: PhantomData LiteMap>, } impl LiteMapVisitor { fn new() -> Self { Self { marker: PhantomData, } } } impl<'de, K, V, R> Visitor<'de> for LiteMapVisitor where K: Deserialize<'de> + Ord, V: Deserialize<'de>, R: StoreMut + StoreFromIterable, { type Value = LiteMap; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("a map produced by LiteMap") } fn visit_seq(self, mut access: S) -> Result where S: SeqAccess<'de>, { let mut map = LiteMap::with_capacity(access.size_hint().unwrap_or(0)); // While there are entries remaining in the input, add them // into our map. while let Some((key, value)) = access.next_element()? { // Try to append it at the end, hoping for a sorted map. // If not sorted, insert as usual. // This allows for arbitrary maps (e.g. from user JSON) // to be deserialized into LiteMap // without impacting performance in the case of deserializing // a serialized map that came from another LiteMap if let Some((key, value)) = map.try_append(key, value) { // Note: this effectively selection sorts the map, // which isn't efficient for large maps map.insert(key, value); } } Ok(map) } fn visit_map(self, mut access: M) -> Result where M: MapAccess<'de>, { let mut map = LiteMap::with_capacity(access.size_hint().unwrap_or(0)); // While there are entries remaining in the input, add them // into our map. while let Some((key, value)) = access.next_entry()? { // Try to append it at the end, hoping for a sorted map. // If not sorted, insert as usual. // This allows for arbitrary maps (e.g. from user JSON) // to be deserialized into LiteMap // without impacting performance in the case of deserializing // a serialized map that came from another LiteMap if let Some((key, value)) = map.try_append(key, value) { // Note: this effectively selection sorts the map, // which isn't efficient for large maps map.insert(key, value); } } Ok(map) } } impl<'de, K, V, R> Deserialize<'de> for LiteMap where K: Ord + Deserialize<'de>, V: Deserialize<'de>, R: StoreMut + StoreFromIterable, { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, { if deserializer.is_human_readable() { // deserialize_any only works on self-describing (human-readable) // formats deserializer.deserialize_any(LiteMapVisitor::new()) } else { deserializer.deserialize_map(LiteMapVisitor::new()) } } } #[cfg(test)] mod test { use crate::LiteMap; use alloc::borrow::ToOwned; use alloc::string::String; fn get_simple_map() -> LiteMap { [ (1, "one".to_owned()), (2, "two".to_owned()), (4, "four".to_owned()), (5, "five".to_owned()), ] .into_iter() .collect() } fn get_tuple_map() -> LiteMap<(u32, String), String> { [ ((1, "en".to_owned()), "one".to_owned()), ((1, "zh".to_owned()), "一".to_owned()), ((2, "en".to_owned()), "two".to_owned()), ((2, "zh".to_owned()), "二".to_owned()), ((4, "en".to_owned()), "four".to_owned()), ((5, "en".to_owned()), "five".to_owned()), ((5, "zh".to_owned()), "五".to_owned()), ((7, "zh".to_owned()), "七".to_owned()), ] .into_iter() .collect() } #[test] fn test_roundtrip_json() { let map = get_simple_map(); let json = serde_json::to_string(&map).unwrap(); assert_eq!( json, "{\"1\":\"one\",\"2\":\"two\",\"4\":\"four\",\"5\":\"five\"}" ); let deserialized: LiteMap = serde_json::from_str(&json).unwrap(); assert_eq!(map, deserialized); let map = get_tuple_map(); let json = serde_json::to_string(&map).unwrap(); assert_eq!( json, "[[[1,\"en\"],\"one\"],[[1,\"zh\"],\"一\"],[[2,\"en\"],\"two\"],\ [[2,\"zh\"],\"二\"],[[4,\"en\"],\"four\"],[[5,\"en\"],\"five\"],\ [[5,\"zh\"],\"五\"],[[7,\"zh\"],\"七\"]]" ); let deserialized: LiteMap<(u32, String), String> = serde_json::from_str(&json).unwrap(); assert_eq!(map, deserialized); } #[test] fn test_roundtrip_postcard() { let map = get_simple_map(); let postcard = postcard::to_stdvec(&map).unwrap(); let deserialized: LiteMap = postcard::from_bytes(&postcard).unwrap(); assert_eq!(map, deserialized); let map = get_tuple_map(); let postcard = postcard::to_stdvec(&map).unwrap(); let deserialized: LiteMap<(u32, String), String> = postcard::from_bytes(&postcard).unwrap(); assert_eq!(map, deserialized); } }