pub mod error;
mod registry;
mod scope;
mod snapshot;
pub(crate) mod store;
pub mod value;
mod wire;
mod attach;
mod config;
#[cfg(feature = "context-key")]
mod context_key;
mod future_ext;
#[macro_use]
mod macros;
pub use attach::AttachGuard;
pub use error::ContextError;
pub use future_ext::{ContextFutureExt, WithContext};
pub use scope::ScopeGuard;
pub use snapshot::ContextSnapshot;
pub use store::ContextStore;
#[cfg(feature = "context-key")]
pub use context_key::ContextKey;
pub use registry::{
initialize, keys_with_metadata, try_initialize, with_metadata, RegistrationOptions,
RegistryBuilder,
};
#[cfg(test)]
pub(crate) use registry::{
register, register_migration, register_with, try_register, try_register_migration,
try_register_with,
};
pub use wire::{make_wire_bytes, make_wire_bytes_v};
pub use config::{
max_context_size, max_scope_chain_len, set_max_context_size, set_max_scope_chain_len,
};
use std::collections::HashMap;
use std::sync::Arc;
use crate::store::{try_apply, CONTEXT};
use crate::value::ContextValue;
pub(crate) fn capture_with_registry(
store: &ContextStore,
registry: ®istry::Registry<'_>,
) -> ContextSnapshot {
let values: HashMap<&'static str, Arc<dyn ContextValue>> = store
.collect_values()
.into_iter()
.filter(|(k, _)| !registry.is_local_key(k))
.collect();
let scope_chain = store.scope_chain();
ContextSnapshot {
values: Arc::new(values),
scope_chain,
}
}
pub(crate) fn store_from_snapshot_with_registry(
snap: ContextSnapshot,
registry: ®istry::Registry<'_>,
) -> ContextStore {
let ContextSnapshot {
values,
scope_chain,
} = snap;
let values: HashMap<&'static str, Arc<dyn ContextValue>> = values
.iter()
.filter(|(k, v)| registry.is_valid_value(k, v.as_ref()) && !registry.is_local_key(k))
.map(|(k, v)| (*k, Arc::clone(v)))
.collect();
ContextStore::from_values_with_chain(values, scope_chain)
}
pub fn push_scope(name: &str) -> ScopeGuard {
let name = name.to_string();
registry::with_global_registry(|registry| {
try_apply(|store| ScopeGuard::new(store.push_scope(registry, Some(name))))
.unwrap_or_else(ScopeGuard::noop)
})
}
pub fn scope_chain() -> Vec<String> {
try_apply(|store| store.scope_chain()).unwrap_or_default()
}
pub fn set_context_variable<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_variable<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_variable<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_variable::<T>(key).unwrap_or_default();
let new = f(old);
set_context_variable(key, new);
}
pub fn capture() -> ContextSnapshot {
registry::with_global_registry(|registry| {
try_apply(|store| capture_with_registry(store, registry)).unwrap_or_default()
})
}
pub fn fork() -> ContextStore {
try_apply(|store| store.fork_child()).unwrap_or_else(ContextStore::new)
}
pub fn capture_serialized() -> Result<Vec<u8>, error::ContextError> {
capture().serialize()
}
pub fn attach_from_bytes(bytes: &[u8]) -> Result<AttachGuard, error::ContextError> {
let snap = ContextSnapshot::deserialize(bytes)?;
Ok(attach_snapshot(snap))
}
pub fn attach_snapshot(snap: ContextSnapshot) -> AttachGuard {
let store: ContextStore = snap.into();
attach_store(store)
}
pub fn attach_snapshot_with_scope(snap: ContextSnapshot, name: &str) -> (AttachGuard, ScopeGuard) {
let guard = attach_snapshot(snap);
let scope = push_scope(name);
(guard, scope)
}
pub fn attach_store(store: ContextStore) -> AttachGuard {
let prev = std::thread::LocalKey::with(&CONTEXT, |cell| cell.replace(Some(store)));
AttachGuard::new(prev)
}
pub fn merge_with(source: ContextStore) {
let values = source.collect_values();
try_apply(|store| {
for (key, val) in values {
store.set_value(key, val);
}
});
}
pub fn clear() {
try_apply(|store| {
*store = ContextStore::new();
});
}
impl From<ContextSnapshot> for ContextStore {
fn from(snap: ContextSnapshot) -> Self {
registry::with_global_registry(|registry| store_from_snapshot_with_registry(snap, registry))
}
}
#[cfg(test)]
mod tests;