use std::cell::Cell;
use std::collections::HashMap;
use std::sync::Arc;
use crate::scope::ScopeGuard;
use crate::snapshot::ContextSnapshot;
use crate::store::ContextStore;
use crate::value::ContextValue;
thread_local! {
pub(crate) static CONTEXT: Cell<Option<ContextStore>> =
Cell::new(Some(ContextStore::new()));
}
pub fn push_scope(name: &str) -> ScopeGuard {
let name = name.to_string();
try_apply(|store| ScopeGuard::new(store.push_scope(Some(name))))
.unwrap_or_else(ScopeGuard::noop)
}
pub fn pop_scope(expected_depth: usize) {
if expected_depth == usize::MAX {
return;
}
let _garbage = try_apply(|store| store.pop_scope(expected_depth));
}
pub(crate) fn set_scope_barrier() {
try_apply(|store| store.set_scope_barrier());
}
pub fn current_depth() -> Option<usize> {
try_apply(|store| store.depth)
}
pub fn try_push_scope(name: &str) -> Option<ScopeGuard> {
let name = name.to_string();
try_apply(|store| ScopeGuard::new(store.push_scope(Some(name))))
}
pub fn scope_chain() -> Vec<String> {
try_apply(|store| store.scope_chain()).unwrap_or_default()
}
pub fn set_context<T>(key: &'static str, value: T)
where
T: Clone + Send + Sync + serde::Serialize + serde::de::DeserializeOwned + 'static,
{
try_apply(|store| {
store.set_value(key, Arc::new(value));
});
}
pub fn get_context<T>(key: &str) -> Option<T>
where
T: Clone + Send + Sync + 'static,
{
try_apply(|store| {
store
.get_value(key)
.and_then(|arc| arc.as_any().downcast_ref::<T>().cloned())
})
.flatten()
}
pub fn update_context<T>(key: &'static str, f: impl FnOnce(T) -> T)
where
T: Clone + Default + Send + Sync + serde::Serialize + serde::de::DeserializeOwned + 'static,
{
let old = get_context::<T>(key).unwrap_or_default();
let new = f(old);
set_context(key, new);
}
pub fn set_raw_value(key: &'static str, value: Arc<dyn ContextValue>) {
try_apply(|store| {
store.set_value(key, value);
});
}
pub fn get_raw_value(key: &str) -> Option<Arc<dyn ContextValue>> {
try_apply(|store| store.get_value(key)).flatten()
}
pub fn with_context_value<R>(key: &str, f: impl FnOnce(&dyn std::any::Any) -> R) -> Option<R> {
get_raw_value(key).map(|arc_val| f(arc_val.as_any()))
}
pub fn snapshot() -> ContextSnapshot {
try_apply(|store| {
let values = store.collect_values();
let scope_chain = store.scope_chain();
ContextSnapshot {
values: Arc::new(values),
scope_chain,
}
})
.unwrap_or_default()
}
pub fn restore(snapshot: ContextSnapshot) {
try_apply(|store| {
let chain = snapshot.scope_chain.clone();
let values: HashMap<&'static str, Arc<dyn ContextValue>> = snapshot
.values
.iter()
.map(|(k, v)| (*k, Arc::clone(v)))
.collect();
*store = ContextStore::from_values_with_chain(values, chain);
});
}
pub fn clear() {
try_apply(|store| {
*store = ContextStore::new();
});
}
pub fn attach(snap: ContextSnapshot) -> ScopeGuard {
let guard = enter_scope();
if !snap.scope_chain.is_empty() {
set_remote_chain(snap.scope_chain);
}
for (key, val) in snap.values.iter() {
set_value(key, Arc::clone(val));
}
guard
}
pub fn serialize_context() -> Result<Vec<u8>, crate::error::ContextError> {
crate::wire::serialize_from(collect_values(), collect_scope_chain())
}
pub fn deserialize_context(bytes: &[u8]) -> Result<ScopeGuard, crate::error::ContextError> {
crate::wire::deserialize_into(bytes, false)
}
pub fn enter_scope() -> ScopeGuard {
try_apply(|store| ScopeGuard::new(store.push_scope(None))).unwrap_or_else(ScopeGuard::noop)
}
pub fn enter_named_scope(name: impl Into<String>) -> ScopeGuard {
let name = name.into();
try_apply(|store| ScopeGuard::new(store.push_scope(Some(name))))
.unwrap_or_else(ScopeGuard::noop)
}
pub(crate) fn leave_scope(expected_depth: usize) {
if expected_depth == usize::MAX {
return; }
let _garbage = try_apply(|store| store.pop_scope(expected_depth));
}
pub(crate) fn fork() -> Option<crate::store::ContextStore> {
try_apply(|store| store.fork_child())
}
pub(crate) fn set_value(key: &'static str, value: Arc<dyn ContextValue>) {
let _old = try_apply(|store| store.set_value(key, value));
}
pub(crate) fn collect_values() -> HashMap<&'static str, Arc<dyn ContextValue>> {
try_apply(|store| store.collect_values()).unwrap_or_default()
}
pub(crate) fn collect_scope_chain() -> Vec<String> {
scope_chain()
}
pub(crate) fn set_remote_chain(chain: Vec<String>) {
try_apply(|store| store.set_remote_chain(chain));
}
pub(crate) fn try_apply<R>(f: impl FnOnce(&mut ContextStore) -> R) -> Option<R> {
CONTEXT
.try_with(|cell| {
let mut store = cell.take()?; let result = f(&mut store);
cell.set(Some(store));
Some(result)
})
.unwrap_or(None) }