core_json_traits/
maps.rs

1use alloc::{string::String, collections::BTreeMap};
2#[cfg(feature = "std")]
3use std::collections::HashMap;
4
5use crate::{Read, Stack, JsonError, Value, JsonDeserialize, JsonStructure, JsonSerialize};
6
7type YieldedField<'read, T, R, S> = Result<(String, T), JsonError<'read, R, S>>;
8fn deserialize_map<'read, 'parent, T: JsonDeserialize, R: Read<'read>, S: Stack>(
9  value: Value<'read, 'parent, R, S>,
10) -> Result<impl Iterator<Item = YieldedField<'read, T, R, S>>, JsonError<'read, R, S>> {
11  let mut iter = value.fields()?;
12  Ok(core::iter::from_fn(move || {
13    let mut field = match iter.next()? {
14      Ok(value) => value,
15      Err(e) => return Some(Err(e)),
16    };
17    let key = match field.key().collect::<Result<String, _>>() {
18      Ok(key) => key,
19      Err(e) => return Some(Err(e)),
20    };
21    let value = field.value();
22    match T::deserialize(value) {
23      Ok(value) => Some(Ok((key, value))),
24      Err(e) => Some(Err(e)),
25    }
26  }))
27}
28
29fn serialize_field<'serializing>(
30  (key, value): (&'serializing str, &'serializing (impl 'serializing + JsonSerialize)),
31) -> impl Iterator<Item = char> {
32  key.serialize().chain(core::iter::once(':')).chain(value.serialize())
33}
34
35#[rustfmt::skip]
36fn serialize_map<'serializing>(
37  mut iter: impl Iterator<Item = (&'serializing str, &'serializing (impl 'serializing + JsonSerialize))>,
38) -> impl Iterator<Item = char> {
39  let fields = iter.next().map(|first_field| {
40    let first_field = serialize_field(first_field);
41    let next_fields =
42      iter.flat_map(|next_field| core::iter::once(',').chain(serialize_field(next_field)));
43    first_field.chain(next_fields)
44  });
45  core::iter::once('{').chain(fields.into_iter().flatten()).chain(core::iter::once('}'))
46}
47
48impl<T: JsonDeserialize> JsonDeserialize for BTreeMap<String, T> {
49  fn deserialize<'read, 'parent, R: Read<'read>, S: Stack>(
50    value: Value<'read, 'parent, R, S>,
51  ) -> Result<Self, JsonError<'read, R, S>> {
52    deserialize_map::<T, _, _>(value)?.collect()
53  }
54}
55impl<K: AsRef<str>, T: 'static + JsonSerialize> JsonSerialize for BTreeMap<K, T> {
56  fn serialize(&self) -> impl Iterator<Item = char> {
57    serialize_map(self.iter().map(|(key, value)| (key.as_ref(), value)))
58  }
59}
60impl<T: JsonDeserialize> JsonStructure for BTreeMap<String, T> {}
61
62#[cfg(feature = "std")]
63impl<T: JsonDeserialize> JsonDeserialize for HashMap<String, T> {
64  fn deserialize<'read, 'parent, R: Read<'read>, S: Stack>(
65    value: Value<'read, 'parent, R, S>,
66  ) -> Result<Self, JsonError<'read, R, S>> {
67    deserialize_map::<T, _, _>(value)?.collect()
68  }
69}
70#[cfg(feature = "std")]
71impl<K: AsRef<str>, T: 'static + JsonSerialize> JsonSerialize for HashMap<K, T> {
72  fn serialize(&self) -> impl Iterator<Item = char> {
73    serialize_map(self.iter().map(|(key, value)| (key.as_ref(), value)))
74  }
75}
76#[cfg(feature = "std")]
77impl<T: JsonDeserialize> JsonStructure for HashMap<String, T> {}
78
79#[cfg(feature = "alloc")]
80#[test]
81fn btree_map() {
82  assert_eq!(BTreeMap::<String, u16>::new().serialize().collect::<String>().as_str(), "{}");
83  let test_map = |map: BTreeMap<String, u16>| {
84    assert_eq!(
85      BTreeMap::<String, u16>::deserialize_structure::<_, crate::ConstStack<32>>(
86        map.serialize().collect::<String>().as_bytes()
87      )
88      .unwrap(),
89      map
90    );
91  };
92  test_map(BTreeMap::from([("key1".to_string(), 1)]));
93  test_map(BTreeMap::from([("key1".to_string(), 1), ("key2".to_string(), 2)]));
94}
95
96#[cfg(feature = "std")]
97#[test]
98fn hash_map() {
99  assert_eq!(HashMap::<String, u16>::new().serialize().collect::<String>().as_str(), "{}");
100  let test_map = |map: HashMap<String, u16>| {
101    assert_eq!(
102      HashMap::<String, u16>::deserialize_structure::<_, crate::ConstStack<32>>(
103        map.serialize().collect::<String>().as_bytes()
104      )
105      .unwrap(),
106      map
107    );
108  };
109  test_map(HashMap::from([("key1".to_string(), 1)]));
110  test_map(HashMap::from([("key1".to_string(), 1), ("key2".to_string(), 2)]));
111}