Skip to main content

co_primitives/library/
serde_map_as_list.rs

1// SPDX-License-Identifier: AGPL-3.0-only
2// Copyright (C) 2026 1io BRANDGUARDIAN GmbH
3
4use serde::{
5	de::{SeqAccess, Visitor},
6	ser::SerializeSeq,
7	Deserialize, Deserializer, Serialize, Serializer,
8};
9use std::{iter::FromIterator, marker::PhantomData};
10
11pub fn serialize<S, K, V, M>(map: &M, serializer: S) -> Result<S::Ok, S::Error>
12where
13	S: Serializer,
14	K: Serialize,
15	V: Serialize,
16	for<'a> &'a M: IntoIterator<Item = (&'a K, &'a V)>,
17{
18	let iter = map.into_iter();
19	let (len, _) = iter.size_hint();
20	let mut seq = serializer.serialize_seq(Some(len))?;
21	for (k, v) in iter {
22		seq.serialize_element(&(&k, v))?;
23	}
24	seq.end()
25}
26
27pub fn deserialize<'de, D, K, V, M>(deserializer: D) -> Result<M, D::Error>
28where
29	D: Deserializer<'de>,
30	K: Deserialize<'de>,
31	V: Deserialize<'de>,
32	M: FromIterator<(K, V)> + Default + Extend<(K, V)>,
33{
34	struct MapAsListVisitor<K, V, M> {
35		marker: PhantomData<(K, V, M)>,
36	}
37
38	impl<'de, K, V, M> Visitor<'de> for MapAsListVisitor<K, V, M>
39	where
40		K: Deserialize<'de>,
41		V: Deserialize<'de>,
42		M: Default + Extend<(K, V)>,
43	{
44		type Value = M;
45
46		fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
47			f.write_str("a sequence of [key, value] pairs")
48		}
49
50		fn visit_seq<A>(self, mut seq: A) -> Result<M, A::Error>
51		where
52			A: SeqAccess<'de>,
53		{
54			let mut map = M::default();
55
56			while let Some((k, v)) = seq.next_element::<(K, V)>()? {
57				// extend with a single element; cheap, no big Vec
58				map.extend(std::iter::once((k, v)));
59			}
60
61			Ok(map)
62		}
63	}
64
65	deserializer.deserialize_seq(MapAsListVisitor { marker: PhantomData::<(K, V, M)> })
66}
67
68#[cfg(test)]
69mod tests {
70	use crate::{from_cbor, serde_map_as_list, to_cbor};
71	use serde::{Deserialize, Serialize};
72	use std::collections::BTreeMap;
73
74	#[derive(Debug, Serialize, Deserialize)]
75	#[serde(transparent)]
76	struct Wrapper(#[serde(with = "serde_map_as_list")] BTreeMap<u32, String>);
77
78	#[test]
79	fn test_list_btreemap() {
80		let mut map = BTreeMap::new();
81		map.insert(1, "one".to_owned());
82		map.insert(2, "two".to_owned());
83
84		let wrapper = Wrapper(map);
85
86		let json = serde_json::to_string(&wrapper).unwrap();
87		// check JSON as BTreeMap gives deterministic order
88		assert_eq!(json, r#"[[1,"one"],[2,"two"]]"#);
89	}
90
91	#[test]
92	fn test_roundtrip_btreemap_dagcbor() {
93		let mut map = BTreeMap::new();
94		map.insert(42, "forty-two".to_owned());
95		map.insert(7, "seven".to_owned());
96
97		let wrapper = Wrapper(map.clone());
98
99		let bytes = to_cbor(&wrapper).unwrap();
100		let decoded: Wrapper = from_cbor(&bytes).unwrap();
101
102		assert_eq!(decoded.0, map);
103	}
104
105	#[test]
106	fn test_roundtrip_btreemap_empty() {
107		let map: BTreeMap<u32, String> = BTreeMap::new();
108		let wrapper = Wrapper(map.clone());
109
110		let bytes = to_cbor(&wrapper).unwrap();
111		let decoded: Wrapper = from_cbor(&bytes).unwrap();
112
113		assert!(decoded.0.is_empty());
114		assert_eq!(decoded.0, map);
115	}
116}