esexpr 0.2.5

ESExpr serialization format and related utilities.
Documentation
use alloc::borrow::{Cow, ToOwned};
use alloc::boxed::Box;
use alloc::collections::BTreeMap;
use alloc::string::String;
use core::ops::Deref;

use crate::cowstr::CowStr;
use crate::{DecodeError, DecodeErrorPath, DecodeErrorType, ESExpr, ESExprCodec, ESExprConstructor, ESExprDictCodec, ESExprEncodedEq, ESExprTag, ESExprTagSet};

impl<K: Ord, A: ESExprEncodedEq> ESExprEncodedEq for BTreeMap<K, A> {
	fn is_encoded_eq(&self, other: &Self) -> bool {
		self.len() == other.len() &&
			self.iter()
				.zip(other.iter())
				.all(|((k1, v1), (k2, v2))|
					 k1 == k2 && v1.is_encoded_eq(v2)
				)
	}
}

impl<'a, A: ESExprCodec<'a>> ESExprCodec<'a> for BTreeMap<String, A> {
	const TAGS: ESExprTagSet = ESExprTagSet::Tags(&[ESExprTag::Constructor(CowStr::Static("dict"))]);

	fn encode_esexpr(&'a self) -> ESExpr<'a> {
		ESExpr::constructor(
			"dict",
			[],
			self.iter()
				.map(|(k, v)| (CowStr::Borrowed(k), v.encode_esexpr()))
				.collect::<BTreeMap<_, _>>(),
		)
	}

	fn decode_esexpr(expr: ESExpr<'a>) -> Result<Self, DecodeError> {
		match expr {
			ESExpr::Constructor(ESExprConstructor { name, args, kwargs }) if name == "dict" => {
				if !args.is_empty() {
					return Err(DecodeError::new(
						DecodeErrorType::OutOfRange("Dict must not have positional arguments".to_owned()),
						DecodeErrorPath::Constructor(name.deref().to_owned()),
					));
				}

				let mut dict = BTreeMap::default();

				for (k, v) in kwargs {
					dict.insert(k.into_string(), A::decode_esexpr(v)?);
				}

				Ok(dict)
			},
			_ => Err(DecodeError::new(
				DecodeErrorType::UnexpectedExpr {
					expected_tags: <Self as ESExprCodec>::TAGS,
					actual_tag: expr.tag().into_owned(),
				},
				DecodeErrorPath::Current,
			)),
		}
	}
}

impl<'a, A: ESExprCodec<'a>> ESExprCodec<'a> for BTreeMap<Cow<'a, str>, A> {
	const TAGS: ESExprTagSet = ESExprTagSet::Tags(&[ESExprTag::Constructor(CowStr::Static("dict"))]);

	fn encode_esexpr(&'a self) -> ESExpr<'a> {
		let mut kwargs = BTreeMap::new();

		for (k, v) in self {
			kwargs.insert(CowStr::Borrowed(k.as_ref()), v.encode_esexpr());
		}

		ESExpr::constructor(
			"dict",
			[],
			self.iter()
				.map(|(k, v)| (CowStr::Borrowed(k.as_ref()), v.encode_esexpr()))
				.collect::<BTreeMap<_, _>>(),
		)
	}

	fn decode_esexpr(expr: ESExpr<'a>) -> Result<Self, DecodeError> {
		match expr {
			ESExpr::Constructor(ESExprConstructor { name, args, kwargs }) if name == "dict" => {
				if !args.is_empty() {
					return Err(DecodeError::new(
						DecodeErrorType::OutOfRange("Dict must not have positional arguments".to_owned()),
						DecodeErrorPath::Constructor(name.deref().to_owned()),
					));
				}

				let mut dict = BTreeMap::default();

				for (k, v) in kwargs {
					dict.insert(Cow::from(k), A::decode_esexpr(v)?);
				}

				Ok(dict)
			},
			_ => Err(DecodeError::new(
				DecodeErrorType::UnexpectedExpr {
					expected_tags: <Self as ESExprCodec>::TAGS,
					actual_tag: expr.tag().into_owned(),
				},
				DecodeErrorPath::Current,
			)),
		}
	}
}

impl<'a, A: ESExprCodec<'a>> ESExprCodec<'a> for BTreeMap<CowStr<'a>, A> {
	const TAGS: ESExprTagSet = ESExprTagSet::Tags(&[ESExprTag::Constructor(CowStr::Static("dict"))]);

	fn encode_esexpr(&'a self) -> ESExpr<'a> {
		ESExpr::constructor(
			"dict",
			[],
			self.iter()
				.map(|(k, v)| (CowStr::Borrowed(k.as_ref()), v.encode_esexpr()))
				.collect::<BTreeMap<_, _>>(),
		)
	}

	fn decode_esexpr(expr: ESExpr<'a>) -> Result<Self, DecodeError> {
		match expr {
			ESExpr::Constructor(ESExprConstructor { name, args, kwargs }) if name == "dict" => {
				if !args.is_empty() {
					return Err(DecodeError::new(
						DecodeErrorType::OutOfRange("Dict must not have positional arguments".to_owned()),
						DecodeErrorPath::Constructor(name.deref().to_owned()),
					));
				}

				let mut dict = BTreeMap::default();

				for (k, v) in kwargs {
					dict.insert(k, A::decode_esexpr(v)?);
				}

				Ok(dict)
			},
			_ => Err(DecodeError::new(
				DecodeErrorType::UnexpectedExpr {
					expected_tags: <Self as ESExprCodec>::TAGS,
					actual_tag: expr.tag().into_owned(),
				},
				DecodeErrorPath::Current,
			)),
		}
	}
}

impl<'a, A: ESExprCodec<'a>> ESExprDictCodec<'a> for BTreeMap<Cow<'a, str>, A> {
	type Element = A;

	fn encode_dict_element(&'a self, kwargs: &mut BTreeMap<CowStr<'a>, ESExpr<'a>>) {
		for (k, v) in self {
			kwargs.insert(CowStr::Borrowed(k.as_ref()), v.encode_esexpr());
		}
	}

	fn decode_dict_element(
		kwargs: &mut BTreeMap<CowStr<'a>, ESExpr<'a>>,
		constructor_name: &str,
	) -> Result<Self, DecodeError> {
		core::mem::take(kwargs)
			.into_iter()
			.map(|(k, v)| {
				let value = A::decode_esexpr(v).map_err(|mut e| {
					e.error_path_with(|old_path| {
						DecodeErrorPath::Keyword(constructor_name.to_owned(), k.deref().to_owned(), Box::new(old_path))
					});
					e
				})?;

				Ok((Cow::from(k), value))
			})
			.collect()
	}
}

impl<'a, A: ESExprCodec<'a>> ESExprDictCodec<'a> for BTreeMap<String, A> {
	type Element = A;


	fn encode_dict_element(&'a self, kwargs: &mut BTreeMap<CowStr<'a>, ESExpr<'a>>) {
		for (k, v) in self {
			kwargs.insert(CowStr::Borrowed(k.as_ref()), v.encode_esexpr());
		}
	}

	fn decode_dict_element(
		kwargs: &mut BTreeMap<CowStr<'a>, ESExpr<'a>>,
		constructor_name: &str,
	) -> Result<Self, DecodeError> {
		core::mem::take(kwargs)
			.into_iter()
			.map(|(k, v)| {
				let value = A::decode_esexpr(v).map_err(|mut e| {
					e.error_path_with(|old_path| {
						DecodeErrorPath::Keyword(constructor_name.to_owned(), k.deref().to_owned(), Box::new(old_path))
					});
					e
				})?;

				Ok((k.into_string(), value))
			})
			.collect()
	}
}