femstache 0.1.2

Embrace the feminine mustache to unlock your templating dreams! Or something, I don't know. :3
Documentation
use crate::{input::TemplateInput, value::Value};

#[derive(Debug, Clone)]
struct Binding {
    depth: u32,
    name: String,
    value: Value,
}

#[derive(Debug, Clone)]
pub struct TemplateEnv<'i, I: TemplateInput> {
    inputs: &'i I,
    bindings: Vec<Binding>,
    depth: u32,
}

impl<'i, I: TemplateInput> TemplateEnv<'i, I> {
    pub fn new(inputs: &'i I) -> Self {
        TemplateEnv {
            inputs,
            bindings: Vec::new(),
            depth: 0,
        }
    }

    pub fn pop(&mut self) {
        self.depth -= 1;
        self.bindings.retain(|b| b.depth <= self.depth);
    }

    pub fn push(&mut self) {
        self.depth += 1;
    }

    pub fn set(&mut self, name: impl Into<String>, value: impl Into<Value>) {
        let name = name.into();
        let value = value.into();
        if let Some(binding) = self
            .bindings
            .iter_mut()
            .rev()
            .find(|b| b.name == name && b.depth == self.depth)
        {
            binding.value = value;
        } else {
            self.bindings.push(Binding {
                depth: self.depth,
                name,
                value,
            });
        }
    }

    pub fn get(&self, name: &str) -> Option<Value> {
        self.bindings
            .iter()
            .rev()
            .find(|b| b.name == name)
            .map(|b| b.value.clone())
            .or_else(|| self.inputs.get(name))
    }
}

#[cfg(test)]
mod tests {
    use std::collections::BTreeMap;

    use super::*;

    #[test]
    fn template_env_works() {
        let mut inputs = BTreeMap::new();
        inputs.insert("meow", Value::String(":3".to_owned()));
        let mut env = TemplateEnv::new(&inputs);
        assert_eq!(env.get("meow"), Some(Value::String(":3".to_owned())));
        assert_eq!(env.get("waf"), None);
        env.push();
        assert_eq!(env.get("meow"), Some(Value::String(":3".to_owned())));
        assert_eq!(env.get("waf"), None);
        env.set("meow", Value::String(":3c".to_owned()));
        assert_eq!(env.get("meow"), Some(Value::String(":3c".to_owned())));
        assert_eq!(env.get("waf"), None);
        env.set("waf", Value::String("fops!".to_owned()));
        assert_eq!(env.get("meow"), Some(Value::String(":3c".to_owned())));
        assert_eq!(env.get("waf"), Some(Value::String("fops!".to_owned())));
        env.push();
        env.set("waf", Value::String("fops!!!".to_owned()));
        assert_eq!(env.get("meow"), Some(Value::String(":3c".to_owned())));
        assert_eq!(env.get("waf"), Some(Value::String("fops!!!".to_owned())));
        env.pop();
        assert_eq!(env.get("meow"), Some(Value::String(":3c".to_owned())));
        assert_eq!(env.get("waf"), Some(Value::String("fops!".to_owned())));
        env.pop();
        assert_eq!(env.get("meow"), Some(Value::String(":3".to_owned())));
        assert_eq!(env.get("waf"), None);
    }
}