#![doc = include_str!("../README.md")]
#![deny(missing_docs)]
use std::{
any::{Any, TypeId},
fmt::{Display, Formatter},
hash::Hasher,
};
use indexmap::IndexMap;
pub use indexmap::TryReserveError;
#[derive(Debug, Default)]
pub struct SingletonSet(IndexMap<Type, Box<dyn Any>>);
impl SingletonSet {
#[inline]
#[must_use]
pub fn new() -> Self {
SingletonSet(IndexMap::new())
}
#[inline]
#[must_use]
pub fn with_capacity(capacity: usize) -> Self {
SingletonSet(IndexMap::with_capacity(capacity))
}
#[inline]
pub fn capacity(&self) -> usize {
self.0.capacity()
}
#[inline]
pub fn len(&self) -> usize {
self.0.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
#[inline]
pub fn clear(&mut self) {
self.0.clear()
}
#[inline]
pub fn reserve(&mut self, additional: usize) {
self.0.reserve(additional)
}
#[inline]
pub fn try_reserve(&mut self, additional: usize) -> Result<(), TryReserveError> {
self.0.try_reserve(additional)
}
#[inline]
pub fn shrink_to_fit(&mut self) {
self.0.shrink_to_fit()
}
#[inline]
pub fn shrink_to(&mut self, min_capacity: usize) {
self.0.shrink_to(min_capacity)
}
pub fn insert<T>(&mut self, value: T) -> Option<T>
where
T: 'static,
{
self.0
.insert(Type::of::<T>(), Box::new(value))
.and_then(|boxed| boxed.downcast().ok().map(|boxed| *boxed))
}
pub fn insert_default<T>(&mut self) -> Option<T>
where
T: 'static + Default,
{
self.insert(T::default())
}
pub fn insert_with<T>(&mut self, f: impl FnOnce() -> T) -> Option<T>
where
T: 'static,
{
self.insert(f())
}
pub fn contains<T>(&self) -> bool
where
T: 'static,
{
self.0.contains_key(&Type::of::<T>())
}
pub fn contains_type_of<T>(&self, value: &T) -> bool
where
T: 'static,
{
let _ = value;
self.0.contains_key(&Type::of::<T>())
}
pub fn contains_type(&self, t: &Type) -> bool {
self.0.contains_key(t)
}
pub fn try_with_ref<T, R>(&self, f: impl FnOnce(Option<&T>) -> R) -> R
where
T: 'static,
{
f(self.try_as_ref())
}
pub fn with_ref<T, R>(&self, f: impl FnOnce(&T) -> R) -> R
where
T: 'static,
{
f(self.as_ref())
}
pub fn with_ref_or<T, R>(&mut self, default: T, f: impl FnOnce(&T) -> R) -> R
where
T: 'static,
{
f(self.as_ref_or_insert::<T>(default))
}
pub fn with_ref_or_default<T, R>(&mut self, f: impl FnOnce(&T) -> R) -> R
where
T: 'static + Default,
{
f(self.as_ref_or_insert::<T>(T::default()))
}
pub fn with_ref_or_else<T, R>(
&mut self,
default: impl FnOnce() -> T,
f: impl FnOnce(&T) -> R,
) -> R
where
T: 'static,
{
f(self.as_ref_or_insert_with(default))
}
pub fn try_with_mut<T, R>(&mut self, f: impl FnOnce(Option<&mut T>) -> R) -> R
where
T: 'static,
{
f(self.try_as_mut::<T>())
}
pub fn with_mut<T, R>(&mut self, f: impl FnOnce(&mut T) -> R) -> R
where
T: 'static + Default,
{
f(self.as_mut())
}
pub fn with_mut_or<T, R>(&mut self, default: T, f: impl FnOnce(&mut T) -> R) -> R
where
T: 'static,
{
f(self.as_mut_or_insert::<T>(default))
}
pub fn with_mut_or_else<T, R>(
&mut self,
default: impl FnOnce() -> T,
f: impl FnOnce(&mut T) -> R,
) -> R
where
T: 'static,
{
f(self.as_mut_or_insert_with(default))
}
pub fn get<T>(&self) -> &T
where
T: 'static,
{
self.as_ref()
}
#[doc(alias = "try_get()")]
pub fn try_as_ref<T>(&self) -> Option<&T>
where
T: 'static,
{
self.0
.get(&Type::of::<T>())
.and_then(|boxed| boxed.downcast_ref::<T>())
}
pub fn try_get<T>(&self) -> Option<&T>
where
T: 'static,
{
self.try_as_ref()
}
pub fn get_mut<T>(&mut self) -> &mut T
where
T: 'static + Default,
{
self.as_mut()
}
#[doc(alias = "try_get_mut()")]
pub fn try_as_mut<T>(&mut self) -> Option<&mut T>
where
T: 'static,
{
self.0
.get_mut(&Type::of::<T>())
.and_then(|boxed| boxed.downcast_mut::<T>())
}
pub fn try_get_mut<T>(&mut self) -> Option<&mut T>
where
T: 'static,
{
self.try_as_mut()
}
#[doc(alias = "get_or_insert()")]
pub fn as_ref_or_insert<T>(&mut self, value: T) -> &T
where
T: 'static,
{
self.0
.entry(Type::of::<T>())
.or_insert(Box::new(value))
.downcast_ref::<T>()
.unwrap()
}
pub fn get_or_insert<T>(&mut self, value: T) -> &T
where
T: 'static,
{
self.as_ref_or_insert(value)
}
#[doc(alias = "get_or_insert_mut()")]
pub fn as_mut_or_insert<T>(&mut self, value: T) -> &mut T
where
T: 'static,
{
self.0
.entry(Type::of::<T>())
.or_insert(Box::new(value))
.downcast_mut::<T>()
.unwrap()
}
pub fn get_or_insert_mut<T>(&mut self, value: T) -> &mut T
where
T: 'static,
{
self.as_mut_or_insert(value)
}
#[doc(alias = "get_or_insert_mut()")]
pub fn as_ref_or_insert_with<T>(&mut self, default: impl FnOnce() -> T) -> &T
where
T: 'static,
{
self.0
.entry(Type::of::<T>())
.or_insert_with(|| Box::new(default()))
.downcast_ref::<T>()
.unwrap()
}
pub fn get_or_insert_with<T>(&mut self, default: impl FnOnce() -> T) -> &T
where
T: 'static,
{
self.as_ref_or_insert_with(default)
}
#[doc(alias = "get_or_insert_with_mut()")]
pub fn as_mut_or_insert_with<T>(&mut self, default: impl FnOnce() -> T) -> &mut T
where
T: 'static,
{
self.0
.entry(Type::of::<T>())
.or_insert_with(|| Box::new(default()))
.downcast_mut::<T>()
.unwrap()
}
pub fn get_or_insert_with_mut<T>(&mut self, default: impl FnOnce() -> T) -> &mut T
where
T: 'static,
{
self.as_mut_or_insert_with(default)
}
pub fn types(&self) -> Types<'_> {
Types(self.0.keys())
}
}
impl<T> AsRef<T> for SingletonSet
where
T: 'static,
{
#[doc(alias = "get_mut()")]
fn as_ref(&self) -> &T {
self.try_as_ref()
.expect(".try_as_ref() or .as_mut() should be used if the slot might be empty")
}
}
impl<T> AsMut<T> for SingletonSet
where
T: 'static + Default,
{
#[doc(alias = "get_mut()")]
fn as_mut(&mut self) -> &mut T {
self.as_mut_or_insert_with(|| T::default())
}
}
pub struct Types<'a>(indexmap::map::Keys<'a, Type, Box<dyn Any>>);
impl<'a> Iterator for Types<'a> {
type Item = &'a Type;
fn next(&mut self) -> Option<Self::Item> {
self.0.next()
}
}
#[derive(Clone, Copy, Debug, Eq)]
pub struct Type(TypeId, &'static str);
impl Type {
pub fn of<T>() -> Self
where
T: 'static,
{
Type(TypeId::of::<T>(), std::any::type_name::<T>())
}
pub fn as_id(&self) -> &TypeId {
&self.0
}
pub fn to_id(&self) -> TypeId {
self.0
}
pub fn as_str(&self) -> &str {
self.1
}
pub fn as_name(&self) -> &str {
let to_index = self.1.find('<').unwrap_or(self.1.len());
let from_index = self.1[..to_index].rfind(':').map_or(0, |i| i + 1);
&self.1[from_index..to_index]
}
pub fn to_name(&self) -> String {
let to_index = self.1.find('<').unwrap_or(self.1.len());
let from_index = self.1[..to_index].rfind(':').map_or(0, |i| i + 1);
self.1[from_index..to_index].to_string()
}
}
impl AsRef<str> for Type {
fn as_ref(&self) -> &str {
self.as_str()
}
}
impl AsRef<TypeId> for Type {
fn as_ref(&self) -> &TypeId {
self.as_id()
}
}
impl Display for Type {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.as_str())
}
}
impl std::hash::Hash for Type {
fn hash<H: Hasher>(&self, state: &mut H) {
self.0.hash(state)
}
}
impl PartialEq for Type {
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_singletonset_retains_last_element_of_type() {
let mut set = SingletonSet::new();
*set.as_mut() = 1u8;
*set.as_mut() = 2u8;
*set.as_mut() = 3u8;
*set.as_mut() = 1u16;
*set.as_mut() = 2u16;
*set.as_mut() = 3u32;
*set.as_mut() = 2u32;
*set.as_mut() = "foo";
*set.as_mut() = "bar";
*set.as_mut() = "baz".to_string();
assert_eq!(set.len(), 5);
assert_ne!(set.as_ref() as &u8, &1u8);
assert_ne!(set.get::<u8>(), &1u8);
assert_eq!(set.as_ref() as &u8, &3u8);
assert_ne!(set.get::<u16>(), &1u16);
assert_eq!(set.as_ref() as &u16, &2u16);
assert_ne!(set.get::<u32>(), &3u32);
assert_eq!(set.get::<u32>(), &2u32);
assert_ne!(set.get::<&str>(), &"foo");
assert_eq!(set.get::<&str>(), &"bar");
assert_eq!(set.get::<String>(), &"baz".to_string());
}
#[test]
fn test_singletonset_mutations() {
let mut set = SingletonSet::new();
*set.get_mut() = "foo".to_string();
(*set.get_mut::<String>()).push_str("bar");
*set.get_mut::<u8>() += 2;
*set.get_mut::<u8>() *= 2;
set.with_mut(|val: &mut u32| *val += 2);
set.with_mut::<u32, _>(|val| *val *= 3);
set.with_mut(|val: &mut String| *val += "baz");
assert_ne!(set.get::<String>(), &"foo".to_string());
assert_ne!(set.get::<String>(), &"foobar".to_string());
assert_eq!(set.get::<String>(), &"foobarbaz".to_string());
assert_ne!(set.get::<u8>(), &2);
assert_eq!(set.get::<u8>(), &4);
}
#[test]
fn singletonset_works_without_default() {
let mut set = SingletonSet::new();
#[derive(Debug, PartialEq)]
struct Foo(&'static str);
assert_eq!(set.try_get::<Foo>(), None);
set.get_or_insert(Foo("bar"));
assert_eq!(set.try_get::<Foo>(), Some(&Foo("bar")));
}
#[test]
fn singletonset_works_with_custom_defaults() {
let mut set = SingletonSet::new();
#[derive(Debug, PartialEq)]
struct Foo(&'static str);
impl Default for Foo {
fn default() -> Self {
Self("foo")
}
}
set.get_mut::<Foo>();
assert_eq!(set.try_get::<Foo>(), Some(&Foo("foo")));
*set.get_mut() = Foo("bar");
assert_eq!(set.try_get::<Foo>(), Some(&Foo("bar")));
}
#[test]
fn singletonset_can_be_iterated() {
let mut set = SingletonSet::new();
set.get_mut::<u8>();
set.get_mut::<u16>();
set.get_mut::<u32>();
let mut iter = set.types();
assert!(iter.next().is_some());
assert!(iter.next().is_some());
assert!(iter.next().is_some());
assert_eq!(iter.next(), None);
}
}