use somni_expr::{Context, DynFunction, TypedValue};
use crate::value::{IntoValue, TemplateTypes, default_str};
type FnApplier = Box<dyn for<'c> FnOnce(&mut Context<'c, TemplateTypes>, &'c str)>;
pub struct Env {
names: Vec<String>,
values: Vec<(usize, TypedValue<TemplateTypes>)>,
funcs: Vec<(usize, FnApplier)>,
}
impl Default for Env {
fn default() -> Self {
Self::new()
}
}
impl Env {
pub fn new() -> Self {
let mut env = Env {
names: Vec::new(),
values: Vec::new(),
funcs: Vec::new(),
};
env.conversion("str", default_str);
env
}
pub fn value(&mut self, name: &str, value: impl IntoValue) -> &mut Self {
let idx = self.intern(name);
self.values.push((idx, value.into_value()));
self
}
pub fn function<F, A>(&mut self, name: &str, func: F) -> &mut Self
where
F: DynFunction<A, TemplateTypes> + 'static,
{
let idx = self.intern(name);
let applier: FnApplier = Box::new(move |ctx, name| {
ctx.add_function(name, func);
});
self.funcs.push((idx, applier));
self
}
pub fn conversion<F, A>(&mut self, name: &str, func: F) -> &mut Self
where
F: DynFunction<A, TemplateTypes> + 'static,
{
self.function(name, func)
}
fn intern(&mut self, name: &str) -> usize {
let idx = self.names.len();
self.names.push(name.to_string());
idx
}
pub(crate) fn apply<'c>(self, ctx: &mut Context<'c, TemplateTypes>, names: &'c [String]) {
for (idx, value) in self.values {
ctx.add_variable(names[idx].as_str(), value);
}
for (idx, applier) in self.funcs {
applier(ctx, names[idx].as_str());
}
}
pub(crate) fn into_parts(self) -> (Vec<String>, Self) {
(self.names.clone(), self)
}
}