use gcmodule::{Cc, Trace, Tracer};
use std::cell::RefCell;
use std::collections::HashMap;
#[derive(Clone)]
pub struct KvEnv<V: Trace>(Cc<RefCell<Inner<V>>>);
#[derive(Trace)]
struct Inner<V: Trace> {
parent: Option<KvEnv<V>>,
names: HashMap<String, V>,
}
impl<V: Trace> Trace for KvEnv<V> {
fn trace(&self, tracer: &mut Tracer) {
self.0.trace(tracer)
}
fn is_type_tracked() -> bool {
true
}
fn as_any(&self) -> Option<&dyn std::any::Any> {
Some(self)
}
}
impl<V: Trace> Default for KvEnv<V> {
fn default() -> Self {
let inner = Inner {
parent: None,
names: HashMap::new(),
};
KvEnv(Cc::new(RefCell::new(inner)))
}
}
impl<V: Trace + Clone> KvEnv<V> {
pub fn get(&self, name: &str) -> Option<V> {
self.lookup(name).map(|v| v.1)
}
pub fn set_local(&self, name: &str, value: V) {
let mut inner = self.0.borrow_mut();
if let Some(existing_value) = inner.names.get_mut(name) {
*existing_value = value;
} else {
inner.names.insert(name.to_string(), value);
}
}
pub fn set(&self, name: &str, value: V) {
match self.lookup(name) {
Some((env, _)) => env.set_local(name, value),
None => self.set_local(name, value),
}
}
pub fn nested(&self) -> Self {
let inner = Inner {
parent: Some(self.clone()),
names: HashMap::new(),
};
KvEnv(Cc::new(RefCell::new(inner)))
}
fn lookup(&self, name: &str) -> Option<(Self, V)> {
let inner = self.0.borrow();
match inner.names.get(name) {
Some(v) => Some((self.clone(), v.clone())),
None => inner.parent.clone().and_then(|p| p.lookup(name)),
}
}
}