mod bound_helper_resolver;
mod context;
use std::collections::{HashMap, HashSet};
use helm_schema_ast::TemplateExpr;
pub(crate) use context::FragmentEvalContext;
use crate::abstract_value::AbstractValue;
use crate::eval_effect::EvalResult;
use crate::eval_env::EvalEnv;
use crate::expr_eval::eval_expr;
use bound_helper_resolver::{BoundHelperValueResolverParams, eval_expr_result_with_bound_helpers};
pub(crate) fn context_value_from_outer_expr(
expr: &TemplateExpr,
outer_locals: Option<&HashMap<String, AbstractValue>>,
outer: Option<&HashMap<String, AbstractValue>>,
current_dot: Option<&AbstractValue>,
) -> Option<AbstractValue> {
if matches!(expr, TemplateExpr::Variable(var) if var.is_empty())
&& let Some(bindings) = outer
{
return Some(AbstractValue::Dict(
bindings
.iter()
.map(|(key, binding)| (key.clone(), binding.to_context_value()))
.collect(),
));
}
let dot = current_dot.cloned().or_else(|| {
outer.map(|bindings| {
AbstractValue::Dict(
bindings
.iter()
.map(|(name, binding)| (name.clone(), binding.clone()))
.collect(),
)
})
});
let env = EvalEnv {
dot,
root_fields: outer.cloned().unwrap_or_default(),
locals: outer_locals.cloned().unwrap_or_default(),
allow_field_root_lookup: true,
..EvalEnv::default()
};
eval_expr(expr, &env)
.value
.map(|value| value.to_context_value())
}
pub(crate) fn fragment_context_value(
expr: &TemplateExpr,
root_bindings: &HashMap<String, AbstractValue>,
template_bindings: &HashMap<String, AbstractValue>,
fragment_context: FragmentEvalContext<'_>,
current_dot_fragment: Option<&AbstractValue>,
) -> Option<AbstractValue> {
let locals = locals_with_roots(template_bindings, root_bindings);
context_value_from_outer_expr(
expr,
Some(&locals),
Some(root_bindings),
current_dot_fragment,
)
.or_else(|| {
fragment_context.fragment_value_from_expr(
expr,
template_bindings,
current_dot_fragment,
&mut HashSet::new(),
)
})
}
pub(crate) fn locals_with_roots(
template_bindings: &HashMap<String, AbstractValue>,
root_bindings: &HashMap<String, AbstractValue>,
) -> HashMap<String, AbstractValue> {
let mut locals = template_bindings.clone();
for (key, value) in root_bindings {
locals
.entry(key.clone())
.or_insert_with(|| value.to_context_value());
}
locals
}
#[expect(
clippy::too_many_arguments,
reason = "expression evaluation requires the complete lexical and fragment context"
)]
pub(crate) fn document_result_from_expr(
expr: &TemplateExpr,
env: &EvalEnv,
fragment_locals: &HashMap<String, AbstractValue>,
outer: Option<&HashMap<String, AbstractValue>>,
outer_root_facts: crate::analysis_db::OuterRootFacts<'_>,
current_dot: Option<&AbstractValue>,
context: FragmentEvalContext<'_>,
seen: &mut HashSet<String>,
) -> EvalResult {
eval_expr_result_with_bound_helpers(
expr,
env,
BoundHelperValueResolverParams {
fragment_locals,
outer,
outer_root_facts,
current_dot,
context,
seen,
},
)
}
pub(crate) fn helper_result_from_expr_with_fragment_locals(
expr: &TemplateExpr,
fragment_locals: &HashMap<String, AbstractValue>,
outer: Option<&HashMap<String, AbstractValue>>,
current_dot: Option<&AbstractValue>,
context: FragmentEvalContext<'_>,
seen: &mut HashSet<String>,
) -> EvalResult {
let mut env = EvalEnv::from_helper_context(outer, current_dot);
env.locals = fragment_locals.clone();
let mut result = eval_expr_result_with_bound_helpers(
expr,
&env,
BoundHelperValueResolverParams {
fragment_locals,
outer,
outer_root_facts: crate::analysis_db::OuterRootFacts::default(),
current_dot,
context,
seen,
},
);
result.value = result.value.map(|value| value.to_context_value());
result
}