anathema-value-resolver 0.2.11

Anathema value resolver
Documentation
use anathema_templates::Expression;

use crate::context::ResolverCtx;
use crate::expression::{Kind, ValueExpr};

pub struct Resolver<'a, 'frame, 'bp> {
    ctx: &'a ResolverCtx<'frame, 'bp>,
}

impl<'a, 'frame, 'bp> Resolver<'a, 'frame, 'bp> {
    pub fn new(ctx: &'a ResolverCtx<'frame, 'bp>) -> Self {
        Self { ctx }
    }

    fn lookup(&self, ident: &str) -> ValueExpr<'bp> {
        match ident {
            "state" => {
                let Some(state_id) = self.ctx.scope.get_state() else { return ValueExpr::Null };
                let Some(state) = self.ctx.states.get(state_id) else { return ValueExpr::Null };
                let value = state.reference();
                value.into()
            }
            "attributes" => {
                let component = self.ctx.scope.get_attributes().unwrap();
                ValueExpr::Attributes(component)
            }
            ident => match self.ctx.scope.lookup(ident) {
                Some(value) => value,
                None => {
                    let Some(expr) = self.ctx.variables.global_lookup(ident) else { return ValueExpr::Null };
                    self.resolve(expr)
                }
            },
        }
    }

    pub(crate) fn resolve(&self, expr: &'bp Expression) -> ValueExpr<'bp> {
        match expr {
            Expression::Primitive(primitive) => ValueExpr::from(*primitive),
            Expression::Variable(var) => match self.ctx.variables.load(*var) {
                Some(expr) => self.resolve(expr),
                None => ValueExpr::Null,
            },
            Expression::Str(s) => ValueExpr::Str(Kind::Static(s)),
            Expression::List(vec) => ValueExpr::List(vec.iter().map(|e| self.resolve(e)).collect()),
            Expression::Map(map) => ValueExpr::Map(map.iter().map(|(k, e)| (k.as_str(), self.resolve(e))).collect()),
            Expression::TextSegments(vec) => ValueExpr::List(vec.iter().map(|e| self.resolve(e)).collect()),
            Expression::Not(expr) => ValueExpr::Not(self.resolve(expr).into()),
            Expression::Negative(expr) => ValueExpr::Negative(self.resolve(expr).into()),
            Expression::Equality(lhs, rhs, equality) => {
                let lhs = self.resolve(lhs);
                let rhs = self.resolve(rhs);
                ValueExpr::Equality(lhs.into(), rhs.into(), *equality)
            }
            Expression::LogicalOp(lhs, rhs, op) => {
                let lhs = self.resolve(lhs).into();
                let rhs = self.resolve(rhs).into();
                ValueExpr::LogicalOp(lhs, rhs, *op)
            }
            Expression::Op(lhs, rhs, op) => {
                let lhs = self.resolve(lhs).into();
                let rhs = self.resolve(rhs).into();
                ValueExpr::Op(lhs, rhs, *op)
            }
            Expression::Either(first, second) => {
                ValueExpr::Either(self.resolve(first).into(), self.resolve(second).into())
            }
            Expression::Ident(ident) => self.lookup(ident),
            Expression::Index(source, index) => {
                let source = self.resolve(source);
                let index = self.resolve(index);
                ValueExpr::Index(source.into(), index.into())
            }
            Expression::Range(from, to) => {
                let from = self.resolve(from);
                let to = self.resolve(to);
                ValueExpr::Range(from.into(), to.into())
            }
            Expression::Call { fun, args } => {
                match &**fun {
                    // function(args)
                    Expression::Ident(fun) => match self.ctx.lookup_function(fun) {
                        Some(fun_ptr) => {
                            let args = args.iter().map(|arg| self.resolve(arg)).collect::<Box<_>>();
                            ValueExpr::Call { fun_ptr, args }
                        }
                        None => ValueExpr::Null,
                    },
                    // some.value.function(args)
                    Expression::Index(lhs, rhs) => {
                        let first_arg = self.resolve(lhs);
                        let Expression::Str(fun) = &**rhs else { return ValueExpr::Null };
                        match self.ctx.lookup_function(fun) {
                            Some(fun_ptr) => {
                                let args = std::iter::once(first_arg)
                                    .chain(args.iter().map(|arg| self.resolve(arg)))
                                    .collect::<Box<_>>();
                                ValueExpr::Call { fun_ptr, args }
                            }
                            None => ValueExpr::Null,
                        }
                    }
                    _ => ValueExpr::Null,
                }
            }
        }
    }
}