cco 0.2.0

cascading configuration
Documentation
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> {
        // We need all keys, so that we can ensure uniqueness
        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,
        }
    }
}