cco 0.2.0

cascading configuration
Documentation
mod binary_op;
mod concat_string;
mod conditional;
mod func_call;
mod list;
mod literal;
mod map;
mod mapper;
mod traversal;
mod trim_string;
mod type_guard;
mod unary_op;
mod variable;

use crate::err::*;
use crate::eval::*;
use crate::value::*;

use enum_dispatch::enum_dispatch;

pub use binary_op::{BinaryOpExpr, BinaryOperator, OpError};
pub use concat_string::ConcatStringExpr;
pub use conditional::ConditionalExpr;
pub use func_call::FuncCallExpr;
pub use list::ListExpr;
pub use literal::LiteralExpr;
pub use map::{MapExpr, MapKey};
pub use mapper::{MapperExpr, Output};
pub use traversal::TraversalExpr;
pub use trim_string::TrimStringExpr;
pub use type_guard::TypeGuardExpr;
pub use unary_op::{UnaryOpExpr, UnaryOperator};
pub use variable::VariableExpr;

#[enum_dispatch(Expr)]
#[derive(Debug, Clone)]
pub enum Expression {
    /// A literal value
    Literal(LiteralExpr),
    /// A list of expressions
    List(ListExpr),
    /// A map of expressions. Keys must be strings and unique
    Map(MapExpr),
    /// A reference to a variable
    Variable(VariableExpr),
    /// Operation with two parameters
    BinaryOp(BinaryOpExpr),
    /// Operation with one parameter
    UnaryOp(UnaryOpExpr),
    /// Multiple strings joined
    ConcatString(ConcatStringExpr),
    /// Choose an expression depending on a condition
    Conditional(ConditionalExpr),
    /// Invoke a function
    FuncCall(FuncCallExpr),
    /// Access a property or index into an expression
    Traversal(TraversalExpr),
    Mapper(MapperExpr),
    TypeGuard(TypeGuardExpr),
    /// String with whitespace removed
    TrimString(TrimStringExpr),
}

impl Expression {
    pub fn literal(literal: impl Into<LiteralExpr>) -> Self {
        Expression::Literal(literal.into())
    }

    pub fn list(list: impl Into<ListExpr>) -> Self {
        Expression::List(list.into())
    }

    pub fn map(map: impl Into<MapExpr>) -> Self {
        Expression::Map(map.into())
    }

    pub fn variable(variable: impl Into<VariableExpr>) -> Self {
        Expression::Variable(variable.into())
    }

    pub fn binary_op(binary_op: impl Into<BinaryOpExpr>) -> Self {
        Expression::BinaryOp(binary_op.into())
    }

    pub fn unary_op(unary_op: impl Into<UnaryOpExpr>) -> Self {
        Expression::UnaryOp(unary_op.into())
    }

    pub fn concat_string(concat_string: impl Into<ConcatStringExpr>) -> Self {
        Expression::ConcatString(concat_string.into())
    }

    pub fn conditional(conditional: impl Into<ConditionalExpr>) -> Self {
        Expression::Conditional(conditional.into())
    }

    pub fn func_call(func_call: impl Into<FuncCallExpr>) -> Self {
        Expression::FuncCall(func_call.into())
    }

    pub fn traversal(traversal: impl Into<TraversalExpr>) -> Self {
        Expression::Traversal(traversal.into())
    }

    pub fn mapper(mapper: impl Into<MapperExpr>) -> Self {
        Expression::Mapper(mapper.into())
    }

    pub fn type_guard(type_guard: impl Into<TypeGuardExpr>) -> Self {
        Expression::TypeGuard(type_guard.into())
    }

    pub fn trim_string(trim_string: impl Into<TrimStringExpr>) -> Self {
        Expression::TrimString(trim_string.into())
    }

    pub fn as_string_literal(&self) -> Option<&str> {
        match self {
            Expression::Literal(LiteralExpr {
                value: Value::String(s),
            }) => Some(s.as_str()),
            _ => None,
        }
    }
}

impl<T> From<T> for Expression
where
    T: Into<Value>,
{
    fn from(value: T) -> Self {
        Expression::Literal(LiteralExpr {
            value: value.into(),
        })
    }
}

pub trait ExprEvaluator {
    fn ensure_resolved(&self, indices: Vec<Index>) -> Result<(), ErrorKind>;

    fn get_value(&self, target: Index) -> Result<Value, ErrorKind>;

    fn traverse(
        &self,
        expression: Index,
        name: &Indexer,
    ) -> Result<Option<ValueOrReference>, ErrorKind>;

    fn resolve_variable(&self, index: Index, name: String) -> Result<ValueOrReference, ErrorKind>;

    fn create(&mut self, expression: Expression) -> Index;

    fn clone_with_context(&mut self, index: Index, context: Index) -> Index;
}

#[enum_dispatch]
pub trait Expr {
    /// Resolves the expression to a value or reference
    ///
    /// This method is called by the evaluator to compute the value of the expression.
    /// Whenever the expression requires more context, it should return a [ErrorKind::Error] containing [ErrorKind::DependenciesNotSatisfied].
    /// It is important that the error returns all dependencies at once, so that circular dependencies can be avoided.
    fn resolve(
        &self,
        index: Index,
        evaluation: &mut dyn ExprEvaluator,
    ) -> Result<ValueOrReference, ErrorKind>;

    /// Traverses the expression to find a value or reference based on the provided indexer.
    ///
    /// This method is used to access properties or indices in expressions like maps or lists.
    /// The expression may implement this method to provide custom traversal logic.
    /// If no special logic is needed, it should return a request for its own ID, so that the traversal is done on either the resulting value or reference.
    fn traverse(
        &self,
        _evaluation: &dyn ExprEvaluator,
        _expression: Index,
        _name: &Indexer,
    ) -> Result<Option<ValueOrReference>, ErrorKind> {
        Ok(None)
    }
}