use crate::any::AnyClone;
use std::{collections::HashMap, fmt};
type AnyMap = HashMap<&'static str, Box<dyn AnyClone + Send + Sync>>;
#[derive(Clone, Default)]
pub struct Context {
map: Option<Box<AnyMap>>,
}
impl Context {
#[inline]
#[must_use]
pub const fn new() -> Self {
Self {
map: None,
}
}
pub fn insert<T: Clone + Send + Sync + 'static>(
&mut self,
key: &'static str,
val: T,
) -> Option<T> {
self.map
.get_or_insert_with(Box::default)
.insert(key, Box::new(val))
.and_then(|boxed| boxed.into_any().downcast().ok().map(|boxed| *boxed))
}
#[must_use]
pub fn get<T: 'static>(&self, key: &'static str) -> Option<&T> {
self.map
.as_ref()
.and_then(|map| map.get(key))
.and_then(|boxed| (**boxed).as_any().downcast_ref())
}
pub fn get_mut<T: 'static>(&mut self, key: &'static str) -> Option<&mut T> {
self.map
.as_mut()
.and_then(|map| map.get_mut(key))
.and_then(|boxed| (**boxed).as_any_mut().downcast_mut())
}
#[allow(clippy::missing_panics_doc)]
pub fn get_or_insert_with<T: Clone + Send + Sync + 'static, F: FnOnce() -> T>(
&mut self,
key: &'static str,
f: F,
) -> &mut T {
let out = self
.map
.get_or_insert_with(Box::default)
.entry(key)
.or_insert_with(|| Box::new(f()));
(**out).as_any_mut().downcast_mut().unwrap()
}
pub fn get_or_insert<T: Clone + Send + Sync + 'static>(
&mut self,
key: &'static str,
value: T,
) -> &mut T {
self.get_or_insert_with(key, || value)
}
pub fn get_or_insert_default<T: Default + Clone + Send + Sync + 'static>(
&mut self,
key: &'static str,
) -> &mut T {
self.get_or_insert_with(key, T::default)
}
pub fn remove<T: 'static>(&mut self, key: &'static str) -> Option<T> {
self.map
.as_mut()
.and_then(|map| map.remove(key))
.and_then(|boxed| boxed.into_any().downcast().ok().map(|boxed| *boxed))
}
pub fn clear(&mut self) {
if let Some(ref mut map) = self.map {
map.clear();
}
}
#[must_use]
pub fn contains_key(&self, key: &'static str) -> bool {
self.map.as_ref().is_some_and(|map| map.contains_key(key))
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.map.as_ref().map_or(true, |map| map.is_empty())
}
#[must_use]
pub fn len(&self) -> usize {
self.map.as_ref().map_or(0, |map| map.len())
}
pub fn extend(&mut self, other: Self) {
if let Some(other) = other.map {
if let Some(map) = &mut self.map {
map.extend(*other);
} else {
self.map = Some(other);
}
}
}
}
impl fmt::Debug for Context {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Context").finish()
}
}