use std::any::{Any, type_name};
use crate::context::Cx;
pub fn app_context<T>(cx: &Cx) -> &T
where
T: Any + Send + Sync,
{
match cx.app_context.get::<T>() {
Some(value) => value,
None => panic!(
"attempted to access app context of type `{:?}`, but this type was not registered for this context",
type_name::<T>()
),
}
}
pub fn request_context<T>(cx: &Cx) -> &T
where
T: Any + Send + Sync,
{
match cx.request_context.get::<T>() {
Some(value) => value,
None => panic!(
"attempted to access request context of type `{:?}`, but this type was not registered for this context",
type_name::<T>()
),
}
}
#[derive(Default, Debug)]
pub struct ContextMap {
entries: anymap3::Map<dyn Any + Send + Sync>,
}
impl ContextMap {
#[must_use]
pub fn new() -> Self {
Self::default()
}
pub fn insert<T>(&mut self, value: T) -> Option<T>
where
T: Any + Send + Sync,
{
self.entries.insert::<T>(value)
}
#[must_use]
pub fn contains<T>(&self) -> bool
where
T: Any + Send + Sync,
{
self.entries.contains::<T>()
}
#[must_use]
pub fn get<T>(&self) -> Option<&T>
where
T: Any + Send + Sync,
{
self.entries.get::<T>()
}
#[must_use]
pub fn get_mut<T>(&mut self) -> Option<&mut T>
where
T: Any + Send + Sync,
{
self.entries.get_mut::<T>()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::context::CxTestBuilder;
#[derive(Debug, PartialEq)]
struct Database(&'static str);
#[derive(Debug, PartialEq)]
struct Config(u32);
#[test]
fn register_and_get_returns_value() {
let mut context = ContextMap::new();
context.insert(Database("primary"));
assert_eq!(context.get::<Database>(), Some(&Database("primary")));
}
#[test]
fn get_returns_none_for_unregistered_type() {
let context = ContextMap::new();
assert_eq!(context.get::<Database>(), None);
}
#[test]
fn multiple_types_coexist() {
let mut context = ContextMap::new();
context.insert(Database("primary"));
context.insert(Config(42));
assert_eq!(context.get::<Database>(), Some(&Database("primary")));
assert_eq!(context.get::<Config>(), Some(&Config(42)));
}
#[test]
fn insert_replaces_and_returns_the_displaced_value() {
let mut context = ContextMap::new();
assert_eq!(context.insert(Database("primary")), None);
assert_eq!(
context.insert(Database("replica")),
Some(Database("primary"))
);
assert_eq!(context.get::<Database>(), Some(&Database("replica")));
}
#[test]
fn contains_reports_registered_types() {
let mut context = ContextMap::new();
assert!(!context.contains::<Database>());
context.insert(Database("primary"));
assert!(context.contains::<Database>());
assert!(!context.contains::<Config>());
}
#[test]
fn get_mut_allows_mutation_in_place() {
let mut context = ContextMap::new();
context.insert(Config(1));
context.get_mut::<Config>().unwrap().0 = 42;
assert_eq!(context.get::<Config>(), Some(&Config(42)));
assert_eq!(context.get_mut::<Database>(), None);
}
#[test]
fn app_context_returns_registered_value() {
let cx = CxTestBuilder::new()
.app_context(Database("primary"))
.build();
let db: &Database = app_context(&cx);
assert_eq!(db, &Database("primary"));
}
#[test]
#[should_panic(expected = "attempted to access app context")]
fn app_context_panics_for_unregistered_type() {
let cx = Cx::default();
let _: &Database = app_context(&cx);
}
#[test]
fn request_context_returns_registered_value() {
let cx = CxTestBuilder::new()
.request_context(Database("primary"))
.build();
let db: &Database = request_context(&cx);
assert_eq!(db, &Database("primary"));
}
#[test]
#[should_panic(expected = "attempted to access request context")]
fn request_context_panics_for_unregistered_type() {
let cx = Cx::default();
let _: &Database = request_context(&cx);
}
}