use super::Expander;
use crate::cel::{Type, Value, Values};
use crate::context::key;
use crate::expanded::Origin;
use crate::resolve::DocumentId;
use crate::tree::{Node, Slot};
use crate::validate::scope::Scope;
use std::rc::Rc;
#[derive(Clone)]
pub(super) struct Env<'r> {
pub(super) document: DocumentId,
pub(super) scope: Scope<'r>,
pub(super) values: Values,
pub(super) slots: Option<Rc<Slots<'r>>>,
pub(super) breakpoints: Option<Rc<[Values; 3]>>,
pub(super) shift: Option<u8>,
pub(super) origin: Option<Origin>,
}
pub(super) struct Slots<'r> {
pub(super) default: &'r [Node],
pub(super) named: &'r [Slot],
pub(super) env: Env<'r>,
pub(super) templates: Vec<Template>,
}
pub(super) struct Template {
pub(super) slot: String,
pub(super) items: Rc<Vec<Value>>,
pub(super) name: String,
}
impl<'r> Env<'r> {
pub(super) fn document(expander: &Expander<'r>, id: DocumentId) -> Self {
Self {
document: id,
scope: expander.scope(id),
values: expander.values(id),
slots: None,
breakpoints: None,
shift: None,
origin: None,
}
}
pub(super) fn input(&self, name: &str) -> Value {
let Some(Value::Map(md)) = self.values.get("md") else {
return Value::Null;
};
match md.get(&key("input")) {
Some(Value::Map(inputs)) => inputs.get(&key(name)).cloned().unwrap_or(Value::Null),
_ => Value::Null,
}
}
pub(super) fn iteration(&self, name: &str, items: &[Value], index: usize) -> Self {
let count = items.len();
let int = |n: usize| Value::Int(i64::try_from(n).unwrap_or(i64::MAX));
let looped = Value::Map(Rc::new(
[
(key("index"), int(index)),
(key("number"), int(index + 1)),
(key("count"), int(count)),
(key("first"), Value::Bool(index == 0)),
(key("last"), Value::Bool(index + 1 == count)),
]
.into_iter()
.collect(),
));
let mut inner = self.clone();
inner.scope = self.scope.template(name, Type::Dyn);
inner.values = with_loop(&self.values, &looped).with(name, items[index].clone());
inner.breakpoints = self.breakpoints.as_ref().map(|values| {
Rc::new(
values
.as_ref()
.clone()
.map(|values| with_loop(&values, &looped).with(name, items[index].clone())),
)
});
inner
}
}
fn with_loop(values: &Values, looped: &Value) -> Values {
let md = match values.get("md") {
Some(Value::Map(fields)) => {
let mut fields = fields.as_ref().clone();
fields.insert(key("loop"), looped.clone());
Value::Map(Rc::new(fields))
}
_ => Value::Null,
};
values.clone().with("md", md)
}