#![deny(
clippy::dbg_macro,
missing_copy_implementations,
rustdoc::missing_crate_level_docs,
missing_debug_implementations,
nonstandard_style,
unused_qualifications
)]
#![warn(missing_docs, clippy::pedantic, clippy::perf, clippy::cargo)]
#![allow(clippy::missing_panics_doc, clippy::module_name_repetitions)]
use std::{
any::{type_name, Any, TypeId},
collections::BTreeMap,
fmt::{self, Debug, Formatter},
};
pub mod entry;
use entry::Entry;
struct Value {
any: Box<dyn Any + Send + Sync>,
name: &'static str,
}
impl Value {
fn new<T: Any + Send + Sync + 'static>(value: T) -> Self {
Self {
any: Box::new(value),
name: type_name::<T>(),
}
}
fn downcast_mut<T: Any + Send + Sync + 'static>(&mut self) -> Option<&mut T> {
debug_assert_eq!(type_name::<T>(), self.name);
self.any.downcast_mut()
}
fn downcast<T: Any + Send + Sync + 'static>(self) -> Option<T> {
debug_assert_eq!(type_name::<T>(), self.name);
self.any.downcast().map(|t| *t).ok()
}
fn downcast_ref<T: Any + Send + Sync + 'static>(&self) -> Option<&T> {
debug_assert_eq!(type_name::<T>(), self.name);
self.any.downcast_ref()
}
}
type Key = TypeId;
macro_rules! unwrap {
($x:expr) => {
match $x {
#[cfg(debug_assertions)]
x => x.unwrap(),
#[cfg(not(debug_assertions))]
x => unsafe { x.unwrap_unchecked() },
}
};
}
use unwrap;
#[derive(Default)]
pub struct TypeSet(BTreeMap<Key, Value>);
fn field_with(f: impl Fn(&mut Formatter) -> fmt::Result) -> impl Debug {
struct DebugWith<F>(F);
impl<F> Debug for DebugWith<F>
where
F: Fn(&mut Formatter) -> fmt::Result,
{
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
self.0(f)
}
}
DebugWith(f)
}
impl Debug for TypeSet {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_tuple("TypeSet")
.field(&field_with(|f| {
let mut values = self.0.values().map(|v| v.name).collect::<Vec<_>>();
values.sort_unstable();
f.debug_set().entries(values).finish()
}))
.finish()
}
}
fn key<T: 'static>() -> Key {
TypeId::of::<T>()
}
impl TypeSet {
#[must_use]
pub const fn new() -> Self {
Self(BTreeMap::new())
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
#[must_use]
pub fn len(&self) -> usize {
self.0.len()
}
pub fn entry<T: Send + Sync + 'static>(&mut self) -> Entry<'_, T> {
Entry::new(self.0.entry(key::<T>()))
}
pub fn insert<T: Send + Sync + 'static>(&mut self, value: T) -> Option<T> {
self.entry().insert(value)
}
#[must_use]
pub fn with<T: Send + Sync + 'static>(mut self, value: T) -> Self {
self.insert(value);
self
}
#[must_use]
pub fn contains<T: Send + Sync + 'static>(&self) -> bool {
#[cfg(feature = "log")]
log::trace!(
"contains {}?: {}",
type_name::<T>(),
self.0.contains_key(&TypeId::of::<T>())
);
self.0.contains_key(&key::<T>())
}
#[must_use]
pub fn get<T: Send + Sync + 'static>(&self) -> Option<&T> {
#[cfg(feature = "log")]
log::trace!("getting {}", type_name::<T>(),);
self.0
.get(&key::<T>())
.map(|value| unwrap!(value.downcast_ref()))
}
pub fn get_mut<T: Send + Sync + 'static>(&mut self) -> Option<&mut T> {
self.0
.get_mut(&key::<T>())
.map(|value| unwrap!(value.downcast_mut()))
}
pub fn take<T: Send + Sync + 'static>(&mut self) -> Option<T> {
self.entry().take()
}
pub fn get_or_insert<T: Send + Sync + 'static>(&mut self, default: T) -> &mut T {
self.entry().or_insert(default)
}
pub fn get_or_insert_with<T: Send + Sync + 'static>(
&mut self,
default: impl FnOnce() -> T,
) -> &mut T {
self.entry().or_insert_with(default)
}
pub fn get_or_insert_default<T: Default + Send + Sync + 'static>(&mut self) -> &mut T {
self.entry().or_default()
}
pub fn merge(&mut self, other: TypeSet) {
self.0.extend(other.0);
}
}