use super::{Expr, ExprEvaluator};
use crate::err::*;
use crate::eval::*;
use crate::value::*;
#[derive(Debug, Clone)]
pub struct MapExpr {
pub items: Vec<(MapKey, Index)>,
}
impl MapExpr {
fn build_map(
&self,
evaluation: &dyn ExprEvaluator,
index: Index,
) -> Result<indexmap::IndexMap<String, Index>, ErrorKind> {
evaluation.ensure_resolved(
self.items
.iter()
.filter_map(|(k, _)| k.as_expression())
.collect(),
)?;
let mut keys = indexmap::IndexMap::new();
for (k, value) in &self.items {
let key = match k {
MapKey::Literal(lit) => lit.clone(),
MapKey::Expression(expr) => evaluation
.get_value(*expr)?
.as_string()
.ok_or_else(|| ErrorKind::ExpressionNotAnIndexer { index: *expr })?
.clone(),
};
if keys.insert(key.clone(), *value).is_some() {
return Err(ErrorKind::DuplicateMapKey {
index,
duplicate_key: key,
})?;
}
}
Ok(keys)
}
}
impl Expr for MapExpr {
fn resolve(
&self,
index: Index,
evaluation: &mut dyn ExprEvaluator,
) -> Result<ValueOrReference, ErrorKind> {
let map = self.build_map(evaluation, index)?;
evaluation.ensure_resolved(map.values().cloned().collect())?;
let mut object = indexmap::IndexMap::new();
for (key, v) in map.into_iter() {
let value = evaluation.get_value(v)?;
object.insert(key, value);
}
Ok(Value::Map(object).into())
}
fn traverse(
&self,
evaluation: &dyn ExprEvaluator,
expression: Index,
name: &Indexer,
) -> Result<Option<ValueOrReference>, ErrorKind> {
match name {
Indexer::String(name) => {
let keys = self.build_map(evaluation, expression)?;
Ok(keys.get(name).map(|index| (*index).into()))
}
Indexer::Number(_) => Err(ErrorKind::IndexNotAllowed { item: expression })?,
}
}
}
impl From<Vec<(Index, Index)>> for MapExpr {
fn from(value: Vec<(Index, Index)>) -> Self {
MapExpr {
items: value
.into_iter()
.map(|(k, v)| (MapKey::Expression(k), v))
.collect(),
}
}
}
impl From<Vec<(MapKey, Index)>> for MapExpr {
fn from(value: Vec<(MapKey, Index)>) -> Self {
MapExpr { items: value }
}
}
#[derive(Debug, Clone)]
pub enum MapKey {
Literal(String),
Expression(Index),
}
impl MapKey {
pub fn as_expression(&self) -> Option<Index> {
match self {
MapKey::Expression(idx) => Some(*idx),
_ => None,
}
}
}