#![warn(missing_docs)]
use std::{
any::{Any, TypeId},
collections::{hash_map, HashMap},
marker::PhantomData,
};
mod impls;
pub trait Builder: Sized {
fn build(ctx: &mut Context) -> Self;
}
pub trait NamedBuilder: Sized {
fn build_with_name(ctx: &mut Context, name: &'static str) -> Self;
}
type AnyMap = HashMap<(TypeId, Option<&'static str>), Box<dyn Any + Send + Sync>>;
#[derive(Default)]
pub struct Context {
map: AnyMap,
}
impl Context {
pub fn new() -> Self {
Self::default()
}
pub fn entry<T: Send + Sync + 'static>(&mut self) -> Entry<'_, T> {
Entry::new(self.map.entry((TypeId::of::<T>(), None)))
}
pub fn entry_named<T: Send + Sync + 'static>(&mut self, name: &'static str) -> Entry<'_, T> {
Entry::new(self.map.entry((TypeId::of::<T>(), Some(name))))
}
pub fn get<T: Send + Sync + 'static>(&self) -> Option<&T> {
self.map
.get(&(TypeId::of::<T>(), None))
.and_then(|boxed| (**boxed).downcast_ref())
}
pub fn get_named<T: Send + Sync + 'static>(&self, name: &'static str) -> Option<&T> {
self.map
.get(&(TypeId::of::<T>(), Some(name)))
.and_then(|boxed| (**boxed).downcast_ref())
}
pub fn insert<T: Send + Sync + 'static>(&mut self, val: T) -> Option<T> {
self.map
.insert((TypeId::of::<T>(), None), Box::new(val))
.and_then(|boxed| boxed.downcast().ok().map(|boxed| *boxed))
}
pub fn insert_named<T: Send + Sync + 'static>(
&mut self,
name: &'static str,
val: T,
) -> Option<T> {
self.map
.insert((TypeId::of::<T>(), Some(name)), Box::new(val))
.and_then(|boxed| boxed.downcast().ok().map(|boxed| *boxed))
}
}
type InnerEntry<'c> =
hash_map::Entry<'c, (TypeId, Option<&'static str>), Box<dyn Any + Send + Sync>>;
#[derive(Debug)]
pub struct Entry<'c, T> {
inner: InnerEntry<'c>,
_phantom_data: PhantomData<T>,
}
impl<'c, T> Entry<'c, T> {
fn new(inner: InnerEntry<'c>) -> Self {
Self {
inner,
_phantom_data: PhantomData,
}
}
}
impl<'c, T: Send + Sync + 'static> Entry<'c, T> {
pub fn or_insert(self, default: T) -> &'c mut T {
self.inner
.or_insert(Box::new(default))
.downcast_mut()
.expect("downcast_mut on T")
}
pub fn or_insert_with<F: FnOnce() -> T>(self, default: F) -> &'c mut T {
self.inner
.or_insert_with(|| Box::new(default()))
.downcast_mut()
.expect("downcast_mut on T")
}
pub fn and_modify<F: FnOnce(&mut T)>(self, f: F) -> Self {
Entry::new(
self.inner
.and_modify(|v| f(v.downcast_mut().expect("downcast_mut on T"))),
)
}
}
impl<'c, T: Default + Send + Sync + 'static> Entry<'c, T> {
pub fn or_default(self) -> &'c mut T {
#[allow(clippy::unwrap_or_default)]
self.inner
.or_insert(Box::<T>::default())
.downcast_mut()
.expect("downcast_mut on T")
}
}