use alloc::borrow::ToOwned;
use alloc::boxed::Box;
use alloc::collections::VecDeque;
use alloc::vec::Vec;
use esexpr::expr::ESExprConstructor;
use crate::cowstr::CowStr;
use crate::{DecodeError, DecodeErrorPath, DecodeErrorType, ESExpr, ESExprCodec, ESExprEncodedEq, ESExprTag, ESExprTagSet, ESExprVarArgCodec};
impl<A: ESExprEncodedEq> ESExprEncodedEq for Vec<A> {
fn is_encoded_eq(&self, other: &Self) -> bool {
self.len() == other.len() &&
self.iter().zip(other.iter()).all(|(v1, v2)| A::is_encoded_eq(v1, v2))
}
}
impl<'a, A: ESExprCodec<'a>> ESExprCodec<'a> for Vec<A> {
const TAGS: ESExprTagSet = ESExprTagSet::Tags(&[ESExprTag::Constructor(CowStr::Static("list"))]);
fn encode_esexpr(&'a self) -> ESExpr<'a> {
ESExpr::<'a>::constructor(
"list",
self.iter().map(A::encode_esexpr).collect::<Vec<ESExpr<'a>>>(),
[],
)
}
fn decode_esexpr(expr: ESExpr<'a>) -> Result<Self, DecodeError> {
match expr {
ESExpr::Constructor(ESExprConstructor { name, args, kwargs }) if name == "list" => {
if !kwargs.is_empty() {
return Err(DecodeError::new(
DecodeErrorType::OutOfRange("List must not have keyword arguments".to_owned()),
DecodeErrorPath::Constructor(name.into_string()),
));
}
args.into_iter().map(A::decode_esexpr).collect::<Result<Vec<_>, _>>()
},
_ => Err(DecodeError::new(
DecodeErrorType::UnexpectedExpr {
expected_tags: <Self as ESExprCodec>::TAGS,
actual_tag: expr.tag().into_owned(),
},
DecodeErrorPath::Current,
)),
}
}
}
impl<'a, A: ESExprCodec<'a>> ESExprVarArgCodec<'a> for Vec<A> {
type Element = A;
fn encode_vararg_element(&'a self, args: &mut Vec<ESExpr<'a>>) {
for arg in self {
args.push(arg.encode_esexpr());
}
}
fn decode_vararg_element(
args: &mut VecDeque<ESExpr<'a>>,
constructor_name: &str,
start_index: &mut usize,
) -> Result<Self, DecodeError> {
let mut res = Vec::new();
while args.front().is_some_and(|e| A::TAGS.contains(&e.tag())) {
let Some(e) = args.pop_front() else { break };
res.push(A::decode_esexpr(e).map_err(|mut e| {
e.error_path_with(|old_path| {
DecodeErrorPath::Positional(constructor_name.to_owned(), *start_index + res.len(), Box::new(old_path))
});
e
})?);
}
*start_index += res.len();
Ok(res)
}
}