esexpr/codecs/
btreemap.rs

1use alloc::borrow::{Cow, ToOwned};
2use alloc::boxed::Box;
3use alloc::collections::BTreeMap;
4use alloc::string::String;
5use core::ops::Deref;
6
7use crate::cowstr::CowStr;
8use crate::{DecodeError, DecodeErrorPath, DecodeErrorType, ESExpr, ESExprCodec, ESExprConstructor, ESExprDictCodec, ESExprEncodedEq, ESExprTag, ESExprTagSet};
9
10impl<K: Ord, A: ESExprEncodedEq> ESExprEncodedEq for BTreeMap<K, A> {
11	fn is_encoded_eq(&self, other: &Self) -> bool {
12		self.len() == other.len() &&
13			self.iter()
14				.zip(other.iter())
15				.all(|((k1, v1), (k2, v2))|
16					 k1 == k2 && v1.is_encoded_eq(v2)
17				)
18	}
19}
20
21impl<'a, A: ESExprCodec<'a>> ESExprCodec<'a> for BTreeMap<String, A> {
22	const TAGS: ESExprTagSet = ESExprTagSet::Tags(&[ESExprTag::Constructor(CowStr::Static("dict"))]);
23
24	fn encode_esexpr(&'a self) -> ESExpr<'a> {
25		ESExpr::constructor(
26			"dict",
27			[],
28			self.iter()
29				.map(|(k, v)| (CowStr::Borrowed(k), v.encode_esexpr()))
30				.collect::<BTreeMap<_, _>>(),
31		)
32	}
33
34	fn decode_esexpr(expr: ESExpr<'a>) -> Result<Self, DecodeError> {
35		match expr {
36			ESExpr::Constructor(ESExprConstructor { name, args, kwargs }) if name == "dict" => {
37				if !args.is_empty() {
38					return Err(DecodeError::new(
39						DecodeErrorType::OutOfRange("Dict must not have positional arguments".to_owned()),
40						DecodeErrorPath::Constructor(name.deref().to_owned()),
41					));
42				}
43
44				let mut dict = BTreeMap::default();
45
46				for (k, v) in kwargs {
47					dict.insert(k.into_string(), A::decode_esexpr(v)?);
48				}
49
50				Ok(dict)
51			},
52			_ => Err(DecodeError::new(
53				DecodeErrorType::UnexpectedExpr {
54					expected_tags: <Self as ESExprCodec>::TAGS,
55					actual_tag: expr.tag().into_owned(),
56				},
57				DecodeErrorPath::Current,
58			)),
59		}
60	}
61}
62
63impl<'a, A: ESExprCodec<'a>> ESExprCodec<'a> for BTreeMap<Cow<'a, str>, A> {
64	const TAGS: ESExprTagSet = ESExprTagSet::Tags(&[ESExprTag::Constructor(CowStr::Static("dict"))]);
65
66	fn encode_esexpr(&'a self) -> ESExpr<'a> {
67		let mut kwargs = BTreeMap::new();
68
69		for (k, v) in self {
70			kwargs.insert(CowStr::Borrowed(k.as_ref()), v.encode_esexpr());
71		}
72
73		ESExpr::constructor(
74			"dict",
75			[],
76			self.iter()
77				.map(|(k, v)| (CowStr::Borrowed(k.as_ref()), v.encode_esexpr()))
78				.collect::<BTreeMap<_, _>>(),
79		)
80	}
81
82	fn decode_esexpr(expr: ESExpr<'a>) -> Result<Self, DecodeError> {
83		match expr {
84			ESExpr::Constructor(ESExprConstructor { name, args, kwargs }) if name == "dict" => {
85				if !args.is_empty() {
86					return Err(DecodeError::new(
87						DecodeErrorType::OutOfRange("Dict must not have positional arguments".to_owned()),
88						DecodeErrorPath::Constructor(name.deref().to_owned()),
89					));
90				}
91
92				let mut dict = BTreeMap::default();
93
94				for (k, v) in kwargs {
95					dict.insert(Cow::from(k), A::decode_esexpr(v)?);
96				}
97
98				Ok(dict)
99			},
100			_ => Err(DecodeError::new(
101				DecodeErrorType::UnexpectedExpr {
102					expected_tags: <Self as ESExprCodec>::TAGS,
103					actual_tag: expr.tag().into_owned(),
104				},
105				DecodeErrorPath::Current,
106			)),
107		}
108	}
109}
110
111impl<'a, A: ESExprCodec<'a>> ESExprCodec<'a> for BTreeMap<CowStr<'a>, A> {
112	const TAGS: ESExprTagSet = ESExprTagSet::Tags(&[ESExprTag::Constructor(CowStr::Static("dict"))]);
113
114	fn encode_esexpr(&'a self) -> ESExpr<'a> {
115		ESExpr::constructor(
116			"dict",
117			[],
118			self.iter()
119				.map(|(k, v)| (CowStr::Borrowed(k.as_ref()), v.encode_esexpr()))
120				.collect::<BTreeMap<_, _>>(),
121		)
122	}
123
124	fn decode_esexpr(expr: ESExpr<'a>) -> Result<Self, DecodeError> {
125		match expr {
126			ESExpr::Constructor(ESExprConstructor { name, args, kwargs }) if name == "dict" => {
127				if !args.is_empty() {
128					return Err(DecodeError::new(
129						DecodeErrorType::OutOfRange("Dict must not have positional arguments".to_owned()),
130						DecodeErrorPath::Constructor(name.deref().to_owned()),
131					));
132				}
133
134				let mut dict = BTreeMap::default();
135
136				for (k, v) in kwargs {
137					dict.insert(k, A::decode_esexpr(v)?);
138				}
139
140				Ok(dict)
141			},
142			_ => Err(DecodeError::new(
143				DecodeErrorType::UnexpectedExpr {
144					expected_tags: <Self as ESExprCodec>::TAGS,
145					actual_tag: expr.tag().into_owned(),
146				},
147				DecodeErrorPath::Current,
148			)),
149		}
150	}
151}
152
153impl<'a, A: ESExprCodec<'a>> ESExprDictCodec<'a> for BTreeMap<Cow<'a, str>, A> {
154	type Element = A;
155
156	fn encode_dict_element(&'a self, kwargs: &mut BTreeMap<CowStr<'a>, ESExpr<'a>>) {
157		for (k, v) in self {
158			kwargs.insert(CowStr::Borrowed(k.as_ref()), v.encode_esexpr());
159		}
160	}
161
162	fn decode_dict_element(
163		kwargs: &mut BTreeMap<CowStr<'a>, ESExpr<'a>>,
164		constructor_name: &str,
165	) -> Result<Self, DecodeError> {
166		core::mem::take(kwargs)
167			.into_iter()
168			.map(|(k, v)| {
169				let value = A::decode_esexpr(v).map_err(|mut e| {
170					e.error_path_with(|old_path| {
171						DecodeErrorPath::Keyword(constructor_name.to_owned(), k.deref().to_owned(), Box::new(old_path))
172					});
173					e
174				})?;
175
176				Ok((Cow::from(k), value))
177			})
178			.collect()
179	}
180}
181
182impl<'a, A: ESExprCodec<'a>> ESExprDictCodec<'a> for BTreeMap<String, A> {
183	type Element = A;
184
185
186	fn encode_dict_element(&'a self, kwargs: &mut BTreeMap<CowStr<'a>, ESExpr<'a>>) {
187		for (k, v) in self {
188			kwargs.insert(CowStr::Borrowed(k.as_ref()), v.encode_esexpr());
189		}
190	}
191
192	fn decode_dict_element(
193		kwargs: &mut BTreeMap<CowStr<'a>, ESExpr<'a>>,
194		constructor_name: &str,
195	) -> Result<Self, DecodeError> {
196		core::mem::take(kwargs)
197			.into_iter()
198			.map(|(k, v)| {
199				let value = A::decode_esexpr(v).map_err(|mut e| {
200					e.error_path_with(|old_path| {
201						DecodeErrorPath::Keyword(constructor_name.to_owned(), k.deref().to_owned(), Box::new(old_path))
202					});
203					e
204				})?;
205
206				Ok((k.into_string(), value))
207			})
208			.collect()
209	}
210}