use std::collections::HashMap;
use crate::value::ContextValue;
pub(crate) struct Scope {
pub(crate) name: Option<String>,
pub(crate) values: HashMap<&'static str, Box<dyn ContextValue>>,
pub(crate) saved_remote_chain: Option<(Vec<String>, usize)>,
}
impl Scope {
pub(crate) fn new() -> Self {
Self {
name: None,
values: HashMap::new(),
saved_remote_chain: None,
}
}
pub(crate) fn named(name: String) -> Self {
Self {
name: Some(name),
values: HashMap::new(),
saved_remote_chain: None,
}
}
}
pub(crate) struct ContextStack {
pub(crate) scopes: Vec<Scope>,
next_scope_id: u64,
read_cache: HashMap<&'static str, usize>,
pub(crate) remote_chain: Vec<String>,
remote_chain_base: usize,
}
impl ContextStack {
pub(crate) fn new() -> Self {
Self {
scopes: vec![Scope::new()], next_scope_id: 1,
read_cache: HashMap::new(),
remote_chain: Vec::new(),
remote_chain_base: 0,
}
}
pub(crate) fn push_scope(&mut self) -> (u64, usize) {
let id = self.next_scope_id;
self.next_scope_id += 1;
self.scopes.push(Scope::new());
let depth = self.scopes.len();
(id, depth)
}
pub(crate) fn push_named_scope(&mut self, name: String) -> (u64, usize) {
let id = self.next_scope_id;
self.next_scope_id += 1;
self.scopes.push(Scope::named(name));
let depth = self.scopes.len();
(id, depth)
}
pub(crate) fn pop_scope(&mut self, expected_depth: usize) {
assert_eq!(
self.scopes.len(),
expected_depth,
"ScopeGuard dropped out of order: expected depth {}, got {}. \
Scopes must be exited in LIFO order.",
expected_depth,
self.scopes.len()
);
if self.scopes.len() > 1 {
if let Some(popped) = self.scopes.pop() {
if let Some((saved_chain, saved_base)) = popped.saved_remote_chain {
self.remote_chain = saved_chain;
self.remote_chain_base = saved_base;
}
}
}
self.rebuild_cache();
}
pub(crate) fn lookup(&self, key: &str) -> Option<&dyn ContextValue> {
let &scope_idx = self.read_cache.get(key)?;
self.scopes[scope_idx].values.get(key).map(|v| v.as_ref())
}
pub(crate) fn set(&mut self, key: &'static str, value: Box<dyn ContextValue>) -> Option<Box<dyn ContextValue>> {
let scope_idx = self.scopes.len() - 1;
self.read_cache.insert(key, scope_idx);
if let Some(scope) = self.scopes.last_mut() {
scope.values.insert(key, value)
} else {
None
}
}
fn rebuild_cache(&mut self) {
self.read_cache.clear();
for (idx, scope) in self.scopes.iter().enumerate() {
for key in scope.values.keys() {
self.read_cache.insert(key, idx);
}
}
}
pub(crate) fn merged_values(&self) -> HashMap<&'static str, &dyn ContextValue> {
self.read_cache
.iter()
.filter_map(|(&key, &scope_idx)| {
self.scopes[scope_idx]
.values
.get(key)
.map(|v| (key, v.as_ref()))
})
.collect()
}
#[allow(dead_code)]
pub(crate) fn from_values(values: HashMap<&'static str, Box<dyn ContextValue>>) -> Self {
Self::from_values_with_chain(values, Vec::new())
}
pub(crate) fn from_values_with_chain(
values: HashMap<&'static str, Box<dyn ContextValue>>,
remote_chain: Vec<String>,
) -> Self {
let read_cache: HashMap<&'static str, usize> = values.keys().map(|&k| (k, 0)).collect();
Self {
scopes: vec![Scope { name: None, values, saved_remote_chain: None }],
next_scope_id: 1,
read_cache,
remote_chain,
remote_chain_base: 1, }
}
pub(crate) fn set_remote_chain(&mut self, chain: Vec<String>) {
if let Some(top) = self.scopes.last_mut() {
if top.saved_remote_chain.is_none() {
top.saved_remote_chain = Some((
std::mem::take(&mut self.remote_chain),
self.remote_chain_base,
));
}
}
self.remote_chain = chain;
self.remote_chain_base = self.scopes.len();
}
pub(crate) fn scope_chain(&self) -> Vec<String> {
let start = self.remote_chain_base.max(1); let local_names = self.scopes.iter()
.skip(start)
.filter_map(|s| s.name.as_ref().cloned())
.collect::<Vec<_>>();
let max_len = crate::config::max_scope_chain_len();
let mut chain = self.remote_chain.clone();
chain.extend(local_names);
if max_len > 0 && chain.len() > max_len {
let start = chain.len() - max_len;
chain.drain(..start);
}
chain
}
}
pub struct ScopeGuard {
#[allow(dead_code)]
scope_id: u64,
expected_depth: usize,
_not_send: std::marker::PhantomData<*const ()>,
}
impl ScopeGuard {
pub(crate) fn new(scope_id: u64, expected_depth: usize) -> Self {
Self {
scope_id,
expected_depth,
_not_send: std::marker::PhantomData,
}
}
}
impl Drop for ScopeGuard {
fn drop(&mut self) {
crate::storage::leave_scope(self.expected_depth);
}
}