Files
ngr/src/util/scoped_map.rs

135 lines
3.9 KiB
Rust

use std::borrow::Borrow;
use std::collections::HashMap;
use std::hash::Hash;
/// A version of [`std::collections::HashMap`] with a built-in notion of scope.
#[derive(Clone)]
pub struct ScopedMap<K: Eq + Hash + PartialEq, V> {
scopes: Vec<HashMap<K, V>>,
}
impl<K: Eq + Hash + PartialEq, V> Default for ScopedMap<K, V> {
fn default() -> Self {
ScopedMap::new()
}
}
impl<K: Eq + Hash + PartialEq, V> ScopedMap<K, V> {
/// Generate a new scoped map.
///
/// In addition to generate the map structure, this method also generates
/// an initial scope for use by the caller.
pub fn new() -> ScopedMap<K, V> {
ScopedMap {
scopes: vec![HashMap::new()],
}
}
/// Get a value from the scoped map.
pub fn get<Q>(&self, k: &Q) -> Option<&V>
where
K: Borrow<Q>,
Q: Hash + Eq + ?Sized,
{
for map in self.scopes.iter().rev() {
match map.get(k) {
None => continue,
Some(v) => return Some(v),
}
}
None
}
/// Returns true if the map contains the given key.
pub fn contains_key(&self, k: &K) -> bool {
self.scopes.iter().any(|x| x.contains_key(k))
}
/// Insert a value into the current binding scope.
///
/// If this variable is bound in the current scope, then its value will be
/// overridden. If it's bound in a previous scope, however, that value will
/// be shadowed, so that its value will preserved if/when the current scope
/// is popped.
pub fn insert(&mut self, k: K, v: V) {
self.scopes
.last_mut()
.expect("tried to insert into ScopedMap with no scopes")
.insert(k, v);
}
/// Create a new scope.
///
/// Modifications to this scope will shadow all previous scopes without
/// modifying them. Consider the following examples:
///
/// ```
/// use ngr::util::scoped_map::ScopedMap;
///
/// let mut example1 = ScopedMap::new();
/// example1.insert(1, true);
/// example1.insert(1, false);
/// assert_eq!(Some(&false), example1.get(&1));
/// let mut example2 = ScopedMap::new();
/// example2.insert(1, true);
/// example2.new_scope();
/// example2.insert(1, false);
/// assert_eq!(Some(&false), example2.get(&1));
/// example2.release_scope().expect("scope releases");
/// assert_eq!(Some(&true), example2.get(&1));
/// ```
pub fn new_scope(&mut self) {
self.scopes.push(HashMap::new());
}
/// Pop the current scope, returning to whatever was bound in the previous
/// scope. If there is no prior scope, `None` will be returned.
pub fn release_scope(&mut self) -> Option<HashMap<K, V>> {
self.scopes.pop()
}
/// Create a new scoped set by mapping over the values of this one.
pub fn map_values<F, W>(self, f: F) -> ScopedMap<K, W>
where
F: Fn(V) -> W,
{
let mut scopes = Vec::with_capacity(self.scopes.len());
for scope in self.scopes {
let mut map = HashMap::with_capacity(scope.len());
for (k, v) in scope {
map.insert(k, f(v));
}
scopes.push(map);
}
ScopedMap { scopes }
}
/// Returns true if this map is completely empty, at every level of
/// scope.
pub fn is_empty(&self) -> bool {
self.scopes.iter().all(|x| x.is_empty())
}
}
impl<K: Clone + Eq + Hash, V: Clone> ScopedMap<K, V> {
/// Returns the set of all variables bound at this time, with shadowed
/// variables hidden.
pub fn bindings(&self) -> HashMap<K, V> {
let mut result = HashMap::new();
for scope in self.scopes.iter().rev() {
for (key, value) in scope.iter() {
if !result.contains_key(key) {
result.insert(key.clone(), value.clone());
}
}
}
result
}
}