use super::ast::Rule;
use std::{cell::RefCell, collections::HashMap, rc::Rc};
#[derive(Debug, Default)]
pub struct Env {
parent: Option<Rc<RefCell<Env>>>,
bindings: HashMap<Vec<u8>, Vec<u8>>,
}
impl Env {
pub fn with_parent(env: Rc<RefCell<Env>>) -> Self {
Env {
parent: Some(env),
..Default::default()
}
}
#[cfg(test)]
pub fn with_parent_owned(env: Env) -> Self {
Env {
parent: Some(Rc::new(RefCell::new(env))),
..Default::default()
}
}
pub fn add_binding<V1: Into<Vec<u8>>, V2: Into<Vec<u8>>>(&mut self, name: V1, value: V2) {
self.bindings.insert(name.into(), value.into());
}
pub fn lookup<'a, V: Into<&'a [u8]>>(&self, name: V) -> Option<Vec<u8>> {
let x = name.into();
self.bindings
.get(x)
.map(|x| x.clone())
.or_else(|| self.parent.as_ref().and_then(|p| p.borrow().lookup(x)))
}
pub fn lookup_for_build<'b, 'c, V: Into<&'c [u8]>>(
&self,
rule: &Rule,
name: V,
) -> Option<Vec<u8>> {
let x = name.into();
self.bindings.get(x).map(|x| x.clone()).or_else(|| {
let rule_val = rule.bindings.get(x);
if let Some(rule_val) = rule_val {
return Some(rule_val.eval_for_build(self, rule));
} else {
self.parent.as_ref().and_then(|p| p.borrow().lookup(x))
}
})
}
}
impl std::fmt::Display for Env {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Env {{\n")?;
for (k, v) in &self.bindings {
write!(
f,
" {} -> {},\n",
std::str::from_utf8(k).unwrap_or("non-utf8"),
std::str::from_utf8(v).unwrap_or("non-utf8"),
)?;
}
write!(f, "}}")
}
}
#[cfg(test)]
mod test {
use super::Env;
#[test]
fn test_basic() {
let mut env = Env::default();
env.add_binding("hello", "there");
assert_eq!(env.lookup(b"hello".as_ref()), Some(b"there".to_vec()));
assert_eq!(env.lookup(b"hello2".as_ref()), None);
}
#[test]
fn test_parent() {
let mut parent = Env::default();
parent.add_binding("in_parent", "exists");
let mut env = Env::with_parent_owned(parent);
env.add_binding("hello", "there");
assert_eq!(env.lookup(b"hello".as_ref()), Some(b"there".to_vec()));
assert_eq!(env.lookup(b"in_parent".as_ref()), Some(b"exists".to_vec()));
assert_eq!(env.lookup(b"not_in_parent".as_ref()), None);
}
}