#![cfg_attr(not(any(test, doctest, feature = "std")), no_std)]
#![cfg_attr(feature = "used_linker", feature(used_with_arg))]
#![cfg_attr(feature = "std", doc = include_str!("../README.md"))]
#![cfg_attr(
not(feature = "std"),
doc = "Cast from `dyn Any` to other trait objects"
)]
#![deny(rust_2018_compatibility)]
#![deny(rust_2018_idioms)]
#![warn(missing_docs)]
#![forbid(unsafe_code)]
use core::any::{Any, TypeId};
#[cfg(feature = "alloc")]
extern crate alloc;
#[cfg(feature = "alloc")]
use alloc::boxed::Box;
#[cfg(feature = "std")]
use std::{rc::Rc, sync::Arc};
#[cfg(feature = "global_registry")]
mod global;
pub use global::*;
#[doc(hidden)]
pub use gensym::gensym;
#[doc(hidden)]
#[allow(clippy::type_complexity)]
#[derive(Clone, Debug, PartialEq)]
pub struct Caster<T: ?Sized> {
pub ref_: fn(&dyn Any) -> Option<&T>,
pub mut_: fn(&mut dyn Any) -> Option<&mut T>,
#[cfg(feature = "alloc")]
pub box_: fn(Box<dyn Any>) -> Result<Box<T>, Box<dyn Any>>,
#[cfg(feature = "std")]
pub rc: fn(Rc<dyn Any>) -> Result<Rc<T>, Rc<dyn Any>>,
#[cfg(feature = "std")]
pub arc: fn(Arc<dyn Any + Sync + Send>) -> Result<Arc<T>, Arc<dyn Any + Sync + Send>>,
}
#[doc(hidden)]
pub type Entry<'a> = (fn() -> [TypeId; 2], &'a (dyn Any + Send + Sync));
#[macro_export]
macro_rules! entry {
( $ty:ty => $tr:ty $(, $flag:ident)? ) => {
(
(|| [::core::any::TypeId::of::<$tr>(), ::core::any::TypeId::of::<$ty>()]) as _,
&$crate::caster!( $ty => $tr $(, $flag)? ) as _,
)
};
}
#[cfg(all(not(feature = "alloc"), not(feature = "std")))]
#[macro_export]
#[doc(hidden)]
macro_rules! caster {
( $ty:ty => $tr:ty $(, $flag:ident)? ) => {
$crate::Caster::<$tr> {
ref_: |any| any.downcast_ref::<$ty>().map(|t| t as _),
mut_: |any| any.downcast_mut::<$ty>().map(|t| t as _),
}
};
}
#[cfg(all(feature = "alloc", not(feature = "std")))]
#[macro_export]
#[doc(hidden)]
macro_rules! caster {
( $ty:ty => $tr:ty $(, $flag:ident)? ) => {
$crate::Caster::<$tr> {
ref_: |any| any.downcast_ref::<$ty>().map(|t| t as _),
mut_: |any| any.downcast_mut::<$ty>().map(|t| t as _),
box_: |any| any.downcast::<$ty>().map(|t| t as _),
}
};
}
#[cfg(feature = "std")]
#[macro_export]
#[doc(hidden)]
macro_rules! caster {
( $ty:ty => $tr:ty $(, $flag:ident)? ) => {
$crate::Caster::<$tr> {
ref_: |any| any.downcast_ref::<$ty>().map(|t| t as _),
mut_: |any| any.downcast_mut::<$ty>().map(|t| t as _),
box_: |any| any.downcast::<$ty>().map(|t| t as _),
rc: |any| any.downcast::<$ty>().map(|t| t as _),
arc: $crate::caster!( $ty $(, $flag)? ),
}
};
( $ty:ty ) => {
|any| any.downcast::<$ty>().map(|t| t as _)
};
( $ty:ty, no_arc ) => {
|any| Err(any)
};
}
#[cfg(not(feature = "std"))]
#[derive(Default, Debug, Clone)]
pub struct Registry<'a>(
) = 32 bytes.
heapless::FnvIndexMap<[TypeId; 2], &'a (dyn Any + Send + Sync), { 1 << 7 }>,
);
#[cfg(feature = "std")]
#[derive(Default, Debug, Clone)]
pub struct Registry<'a>(std::collections::HashMap<[TypeId; 2], &'a (dyn Any + Send + Sync)>);
impl<'a> Registry<'a> {
pub fn new<'b>(it: impl IntoIterator<Item = &'b Entry<'a>>) -> Self
where
'a: 'b,
{
Self(
it.into_iter()
.map(|(key, value)| ((*key)(), *value))
.collect(),
)
}
fn caster<T: ?Sized + 'static>(&self, any: TypeId) -> Option<&Caster<T>> {
self.0.get(&[TypeId::of::<T>(), any])?.downcast_ref()
}
pub fn castable_ref<T: ?Sized + 'static>(&self, any: &dyn Any) -> bool {
self.0.contains_key(&[TypeId::of::<T>(), any.type_id()])
}
pub fn castable<T: ?Sized + 'static, U: ?Sized + 'static>(&self) -> bool {
self.0.contains_key(&[TypeId::of::<T>(), TypeId::of::<U>()])
}
pub fn cast_ref<'b, T: ?Sized + 'static>(&self, any: &'b dyn Any) -> Option<&'b T> {
(self.caster(any.type_id())?.ref_)(any)
}
pub fn cast_mut<'b, T: ?Sized + 'static>(&self, any: &'b mut dyn Any) -> Option<&'b mut T> {
(self.caster((*any).type_id())?.mut_)(any)
}
#[cfg(feature = "alloc")]
pub fn cast_box<T: ?Sized + 'static>(&self, any: Box<dyn Any>) -> Result<Box<T>, Box<dyn Any>> {
match self.caster((*any).type_id()) {
Some(c) => (c.box_)(any),
None => Err(any),
}
}
#[cfg(feature = "std")]
pub fn cast_rc<T: ?Sized + 'static>(&self, any: Rc<dyn Any>) -> Result<Rc<T>, Rc<dyn Any>> {
match self.caster((*any).type_id()) {
Some(c) => (c.rc)(any),
None => Err(any),
}
}
#[cfg(feature = "std")]
pub fn cast_arc<T: ?Sized + 'static>(
&self,
any: Arc<dyn Any + Sync + Send>,
) -> Result<Arc<T>, Arc<dyn Any + Sync + Send>> {
match self.caster((*any).type_id()) {
Some(c) => (c.arc)(any),
None => Err(any),
}
}
}