use std::borrow::{Cow, ToOwned};
use std::boxed::Box;
use std::collections::{BTreeMap, HashMap};
use std::hash::{BuildHasher, Hash};
use std::ops::Deref;
use std::string::String;
use esexpr::{ESExprConstructor, ESExprEncodedEq};
use crate::cowstr::CowStr;
use crate::{
DecodeError,
DecodeErrorPath,
DecodeErrorType,
ESExpr,
ESExprCodec,
ESExprDictCodec,
ESExprTag,
ESExprTagSet,
};
impl<K: Eq + Hash, A: ESExprEncodedEq, S: BuildHasher> ESExprEncodedEq for HashMap<K, A, S> {
fn is_encoded_eq(&self, other: &Self) -> bool {
self.len() == other.len() &&
self.iter().all(|(k, v1)|
other.get(k).is_some_and(|v2| v1.is_encoded_eq(v2))
)
}
}
impl<'a, A: ESExprCodec<'a>, S: BuildHasher + Default + 'static> ESExprCodec<'a> for HashMap<String, A, S> {
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_str()), 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 = HashMap::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>, S: BuildHasher + Default + 'static> ESExprCodec<'a> for HashMap<Cow<'a, str>, A, S> {
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 = HashMap::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>, S: BuildHasher + Default + 'static> ESExprCodec<'a> for HashMap<CowStr<'a>, A, S> {
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)| (k.as_borrowed(), 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 = HashMap::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>, S: BuildHasher + Default + 'static> ESExprDictCodec<'a> for HashMap<String, A, S> {
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), v.encode_esexpr());
}
}
fn decode_dict_element(
kwargs: &mut BTreeMap<CowStr<'a>, ESExpr<'a>>,
constructor_name: &str,
) -> Result<Self, DecodeError> {
std::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()
}
}
impl<'a, A: ESExprCodec<'a>, S: BuildHasher + Default + 'static> ESExprDictCodec<'a> for HashMap<Cow<'a, str>, A, S> {
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> {
std::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>, S: BuildHasher + Default + 'static> ESExprDictCodec<'a> for HashMap<CowStr<'a>, A, S> {
type Element = A;
fn encode_dict_element(&'a self, kwargs: &mut BTreeMap<CowStr<'a>, ESExpr<'a>>) {
for (k, v) in self {
kwargs.insert(k.as_borrowed(), v.encode_esexpr());
}
}
fn decode_dict_element(
kwargs: &mut BTreeMap<CowStr<'a>, ESExpr<'a>>,
constructor_name: &str,
) -> Result<Self, DecodeError> {
std::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, value))
})
.collect()
}
}