use std::any::{Any, type_name};
use crate::context::{ContextRead, Cx};
#[must_use]
pub fn try_context<T>(cx: &Cx) -> Option<&T>
where
T: Any + Send + Sync,
{
let binding = cx.scope.lookup::<T>();
if let Some(tracker) = &cx.tracker {
tracker.record(ContextRead::new::<T>(binding.map(|(id, _)| id)));
}
binding.map(|(_, value)| value)
}
#[must_use]
#[track_caller]
pub fn context<T>(cx: &Cx) -> &T
where
T: Any + Send + Sync,
{
match try_context(cx) {
Some(value) => value,
None => panic!(
"attempted to access context of type `{:?}`, but this type was not registered for this context",
type_name::<T>()
),
}
}
pub fn iter_context<T>(cx: &Cx) -> impl Iterator<Item = &T>
where
T: Any + Send + Sync,
{
if let Some(tracker) = &cx.tracker {
tracker.record(ContextRead::all::<T>(&cx.scope));
}
cx.scope.iter::<T>()
}
#[cfg(test)]
mod tests {
use std::sync::Arc;
use super::*;
use crate::context::{ContextMap, memoize_cache};
#[derive(Debug, PartialEq)]
struct Database(&'static str);
#[test]
fn context_returns_a_sealed_value() {
let mut shared = ContextMap::new();
shared.insert(Database("primary"));
let cx = Cx::new().with_shared(Arc::new(shared));
assert_eq!(context::<Database>(&cx), &Database("primary"));
}
#[test]
fn try_context_returns_none_for_unregistered_type() {
let cx = Cx::new();
assert_eq!(try_context::<Database>(&cx), None);
}
#[test]
#[should_panic(expected = "attempted to access context")]
fn context_panics_for_unregistered_type() {
let cx = Cx::new();
let _: &Database = context(&cx);
}
#[test]
fn iter_context_visits_request_values_before_sealed_values() {
let mut shared = ContextMap::new();
shared.insert(Database("primary"));
let cx = Cx::new()
.with_shared(Arc::new(shared))
.with(Database("replica"))
.with(Database("cache"));
assert_eq!(
iter_context::<Database>(&cx).collect::<Vec<_>>(),
[
&Database("cache"),
&Database("replica"),
&Database("primary")
]
);
}
#[test]
fn memoized_iterations_rerun_when_a_value_is_added() {
let count = |cx: Cx, (): ()| iter_context::<Database>(&cx).count();
let cx = Cx::new().with(Database("primary"));
let child = cx.with(Database("replica"));
assert_eq!(*memoize_cache(&cx).memoize(&cx, (), (), count), 1);
assert_eq!(*memoize_cache(&child).memoize(&child, (), (), count), 2);
assert_eq!(*memoize_cache(&cx).memoize(&cx, (), (), count), 1);
}
#[test]
fn memoized_iterations_through_own_values_depend_on_the_caller() {
let count = |cx: Cx, (): ()| iter_context::<Database>(&cx.with(Database("own"))).count();
let cx = Cx::new().with(Database("primary"));
let child = cx.with(Database("replica"));
assert_eq!(*memoize_cache(&cx).memoize(&cx, (), (), count), 2);
assert_eq!(*memoize_cache(&child).memoize(&child, (), (), count), 3);
}
}