atman-runtime 1.1.1

atman flow execution runtime: evaluator, tool dispatch, provider dispatch, executor, memory stores
Documentation
use crate::value::Value;

#[derive(Debug, Clone, Default)]
pub struct Env {
    bindings: Vec<(String, Value)>,
}

impl Env {
    pub fn new() -> Self {
        Self::default()
    }

    pub fn bind(&mut self, name: impl Into<String>, value: Value) {
        self.bindings.push((name.into(), value));
    }

    pub fn lookup(&self, name: &str) -> Option<&Value> {
        self.bindings
            .iter()
            .rev()
            .find(|(k, _)| k == name)
            .map(|(_, v)| v)
    }

    pub fn iter(&self) -> impl Iterator<Item = (&str, &Value)> {
        self.bindings.iter().map(|(k, v)| (k.as_str(), v))
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn empty_env_lookup_returns_none() {
        assert!(Env::new().lookup("x").is_none());
    }

    #[test]
    fn bind_then_lookup_returns_value() {
        let mut env = Env::new();
        env.bind("x", Value::Int(1));
        assert!(matches!(env.lookup("x"), Some(Value::Int(1))));
    }

    #[test]
    fn later_binding_shadows_earlier() {
        let mut env = Env::new();
        env.bind("x", Value::Int(1));
        env.bind("x", Value::Int(2));
        assert!(matches!(env.lookup("x"), Some(Value::Int(2))));
    }

    #[test]
    fn unrelated_lookup_after_shadow_still_works() {
        let mut env = Env::new();
        env.bind("x", Value::Int(1));
        env.bind("y", Value::Str("hi".into()));
        env.bind("x", Value::Int(2));
        assert!(matches!(env.lookup("y"), Some(Value::Str(s)) if s == "hi"));
    }

    #[test]
    fn iter_yields_declaration_order() {
        let mut env = Env::new();
        env.bind("a", Value::Int(1));
        env.bind("b", Value::Int(2));
        let names: Vec<_> = env.iter().map(|(k, _)| k).collect();
        assert_eq!(names, vec!["a", "b"]);
    }
}