use core::{
any::{Any, TypeId, type_name},
fmt::{self, Debug, Formatter},
ptr,
ptr::DynMetadata,
};
#[cfg(feature = "alloc")]
use alloc::{boxed::Box, rc::Rc, sync::Arc};
pub trait TraitcastableTo<Target: 'static + ?Sized>: TraitcastableAny {
const METADATA: DynMetadata<Target>;
}
pub struct TraitcastTarget {
target_type_id: TypeId,
target_type_name: &'static str,
metadata: *const (),
}
impl TraitcastTarget {
#[must_use]
pub const fn from<Src: TraitcastableTo<Target>, Target: 'static + ?Sized>() -> Self {
#[allow(clippy::borrow_as_ptr)] Self {
target_type_id: TypeId::of::<Target>(),
target_type_name: type_name::<Target>(),
metadata: ptr::from_ref::<DynMetadata<Target>>(&Src::METADATA).cast::<()>(),
}
}
#[must_use]
pub const fn target_type_id(&self) -> TypeId {
self.target_type_id
}
}
pub unsafe trait TraitcastableAny: Any {
fn traitcast_targets(&self) -> &[TraitcastTarget];
fn find_traitcast_target(&self, target: TypeId) -> Option<&TraitcastTarget> {
self
.traitcast_targets()
.iter()
.find(|possible| possible.target_type_id == target)
}
fn type_id(&self) -> TypeId {
Any::type_id(self)
}
}
pub trait TraitcastableAnyInfra<Target: ?Sized>: 'static {
fn is(&self) -> bool;
fn can_be(&self) -> bool;
fn downcast_ref(&self) -> Option<&Target>;
#[cfg(feature = "downcast_unchecked")]
#[doc(cfg(feature = "downcast_unchecked"))]
unsafe fn downcast_ref_unchecked(&self) -> &Target;
fn downcast_mut(&mut self) -> Option<&mut Target>;
#[cfg(feature = "downcast_unchecked")]
#[doc(cfg(feature = "downcast_unchecked"))]
unsafe fn downcast_mut_unchecked(&mut self) -> &mut Target;
}
#[cfg(feature = "alloc")]
#[doc(cfg(feature = "alloc"))]
pub trait TraitcastableAnyInfraExt<Target: ?Sized + 'static>: Sized {
type Output;
fn downcast(self) -> Result<Self::Output, Self>;
#[cfg(feature = "downcast_unchecked")]
#[doc(cfg(feature = "downcast_unchecked"))]
unsafe fn downcast_unchecked(self) -> Self::Output;
}
#[cfg(feature = "min_specialization")]
unsafe impl<T: 'static> TraitcastableAny for T {
default fn traitcast_targets(&self) -> &[TraitcastTarget] {
&[]
}
default fn find_traitcast_target(&self, target: TypeId) -> Option<&TraitcastTarget> {
self
.traitcast_targets()
.iter()
.find(|possible| possible.target_type_id == target)
}
}
impl Debug for dyn TraitcastableAny {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "TraitcastableAny to {{")?;
for (i, target) in self.traitcast_targets().iter().enumerate() {
if i != 0 {
write!(f, ", ")?;
}
write!(f, "{}", target.target_type_name)?;
}
write!(f, "}}")
}
}
macro_rules! implement_with_markers {
($($(+)? $traits:ident)*) => {
impl<Target: ?Sized + 'static + $($traits +)*> TraitcastableAnyInfra<Target> for dyn TraitcastableAny $(+ $traits)* {
default fn is(&self) -> bool {
false
}
default fn can_be(&self) -> bool {
let found_target = self.find_traitcast_target(TypeId::of::<Target>());
found_target.is_some()
}
default fn downcast_ref(&self) -> Option<&Target> {
let metadata = Self::find_traitcast_target(self, TypeId::of::<Target>()).map(|target| unsafe {*(target.metadata.cast::<<Target as ::core::ptr::Pointee>::Metadata>())});
let raw_ptr = core::ptr::from_ref::<Self>(self).to_raw_parts().0;
metadata.map(|metadata| {
let ret_ptr: *const Target = ptr::from_raw_parts(raw_ptr, metadata);
unsafe {&*ret_ptr}
})
}
#[cfg(feature = "downcast_unchecked")]
default unsafe fn downcast_ref_unchecked(&self) -> &Target {
unsafe { self.downcast_ref().unwrap_unchecked() }
}
default fn downcast_mut(&mut self) -> Option<&mut Target> {
let metadata = Self::find_traitcast_target(self, TypeId::of::<Target>()).map(|target| unsafe {*(target.metadata.cast::<<Target as ::core::ptr::Pointee>::Metadata>())});
let raw_ptr = core::ptr::from_mut::<Self>(self).to_raw_parts().0;
metadata.map(|metadata| {
let ret_ptr: *mut Target = ptr::from_raw_parts_mut(raw_ptr, metadata);
unsafe {&mut *ret_ptr}
})
}
#[cfg(feature = "downcast_unchecked")]
default unsafe fn downcast_mut_unchecked(&mut self) -> &mut Target {
unsafe { self.downcast_mut().unwrap_unchecked() }
}
}
impl<Target: Sized + 'static + $($traits +)*> TraitcastableAnyInfra<Target> for dyn TraitcastableAny $(+ $traits)* {
fn is(&self) -> bool {
<dyn Any>::is::<Target>(self)
}
fn can_be(&self) -> bool {
<dyn TraitcastableAny as TraitcastableAnyInfra<Target>>::is(self)
}
fn downcast_ref(&self) -> Option<&Target> {
<dyn Any>::downcast_ref::<Target>(self)
}
#[cfg(feature = "downcast_unchecked")]
unsafe fn downcast_ref_unchecked(&self) -> &Target {
unsafe { <dyn Any>::downcast_unchecked_ref::<Target>(self) }
}
fn downcast_mut(&mut self) -> Option<&mut Target> {
<dyn Any>::downcast_mut::<Target>(self)
}
#[cfg(feature = "downcast_unchecked")]
unsafe fn downcast_mut_unchecked(&mut self) -> &mut Target {
unsafe { <dyn Any>::downcast_unchecked_mut::<Target>(self) }
}
}
};
}
#[cfg(feature = "alloc")]
impl<Src: TraitcastableAnyInfra<Target> + ?Sized, Target: ?Sized + 'static>
TraitcastableAnyInfraExt<Target> for Box<Src>
{
type Output = Box<Target>;
default fn downcast(self) -> Result<Self::Output, Self> {
let raw = Self::into_raw(self);
if let Some(to_ref) = unsafe { &mut *raw }.downcast_mut() {
Ok(unsafe { Box::from_raw(to_ref) })
} else {
Err(unsafe { Self::from_raw(raw) })
}
}
#[cfg(feature = "downcast_unchecked")]
default unsafe fn downcast_unchecked(self) -> Self::Output {
unsafe { <Self as TraitcastableAnyInfraExt<Target>>::downcast(self).unwrap_unchecked() }
}
}
#[cfg(feature = "alloc")]
impl<Src: TraitcastableAnyInfra<Target>, Target: Sized + 'static> TraitcastableAnyInfraExt<Target>
for Box<Src>
{
fn downcast(self) -> Result<Self::Output, Self> {
#[cfg(feature = "downcast_unchecked")]
if TraitcastableAnyInfra::<Target>::is(self.as_ref()) {
unsafe { Ok(<Box<dyn Any>>::downcast_unchecked(self)) }
} else {
Err(self)
}
#[cfg(not(feature = "downcast_unchecked"))]
if TraitcastableAnyInfra::<Target>::is(self.as_ref()) {
Ok(<Box<dyn Any>>::downcast::<Target>(self).unwrap())
} else {
Err(self)
}
}
#[cfg(feature = "downcast_unchecked")]
unsafe fn downcast_unchecked(self) -> Self::Output {
unsafe { <Box<dyn Any>>::downcast_unchecked::<Target>(self) }
}
}
#[cfg(feature = "alloc")]
impl<Src: TraitcastableAnyInfra<Target> + ?Sized, Target: ?Sized + 'static>
TraitcastableAnyInfraExt<Target> for Rc<Src>
{
type Output = Rc<Target>;
default fn downcast(self) -> Result<Self::Output, Self> {
let raw = Self::into_raw(self);
if let Some(to_ref) = unsafe { &*raw }.downcast_ref() {
Ok(unsafe { Rc::from_raw(to_ref) })
} else {
Err(unsafe { Self::from_raw(raw) })
}
}
#[cfg(feature = "downcast_unchecked")]
default unsafe fn downcast_unchecked(self) -> Self::Output {
unsafe { <Self as TraitcastableAnyInfraExt<Target>>::downcast(self).unwrap_unchecked() }
}
}
#[cfg(feature = "alloc")]
impl<Src: TraitcastableAnyInfra<Target>, Target: Sized + 'static> TraitcastableAnyInfraExt<Target>
for Rc<Src>
{
fn downcast(self) -> Result<Self::Output, Self> {
#[cfg(feature = "downcast_unchecked")]
if TraitcastableAnyInfra::<Target>::is(self.as_ref()) {
unsafe { Ok(<Rc<dyn Any>>::downcast_unchecked(self)) }
} else {
Err(self)
}
#[cfg(not(feature = "downcast_unchecked"))]
if TraitcastableAnyInfra::<Target>::is(self.as_ref()) {
Ok(<Rc<dyn Any>>::downcast(self).unwrap())
} else {
Err(self)
}
}
#[cfg(feature = "downcast_unchecked")]
unsafe fn downcast_unchecked(self) -> Self::Output {
unsafe { <Rc<dyn Any>>::downcast_unchecked::<Target>(self) }
}
}
#[cfg(feature = "alloc")]
impl<
Src: TraitcastableAnyInfra<Target> + ?Sized + Send + Sync,
Target: ?Sized + 'static + Send + Sync,
> TraitcastableAnyInfraExt<Target> for Arc<Src>
{
type Output = Arc<Target>;
default fn downcast(self) -> Result<Self::Output, Self> {
let raw = Self::into_raw(self);
if let Some(to_ref) = unsafe { &*raw }.downcast_ref() {
Ok(unsafe { Arc::from_raw(to_ref) })
} else {
Err(unsafe { Self::from_raw(raw) })
}
}
#[cfg(feature = "downcast_unchecked")]
default unsafe fn downcast_unchecked(self) -> Self::Output {
unsafe { <Self as TraitcastableAnyInfraExt<Target>>::downcast(self).unwrap_unchecked() }
}
}
#[cfg(feature = "alloc")]
impl<Src: TraitcastableAnyInfra<Target> + Send + Sync, Target: Sized + 'static + Send + Sync>
TraitcastableAnyInfraExt<Target> for Arc<Src>
{
fn downcast(self) -> Result<Self::Output, Self> {
#[cfg(feature = "downcast_unchecked")]
if TraitcastableAnyInfra::<Target>::is(self.as_ref()) {
unsafe { Ok(<Arc<dyn Any + Send + Sync>>::downcast_unchecked(self)) }
} else {
Err(self)
}
#[cfg(not(feature = "downcast_unchecked"))]
if TraitcastableAnyInfra::<Target>::is(self.as_ref()) {
Ok(<Arc<dyn Any + Send + Sync>>::downcast(self).unwrap())
} else {
Err(self)
}
}
#[cfg(feature = "downcast_unchecked")]
unsafe fn downcast_unchecked(self) -> Self::Output {
unsafe { <Arc<dyn Any + Send + Sync>>::downcast_unchecked::<Target>(self) }
}
}
implement_with_markers!();
implement_with_markers!(Send);
implement_with_markers!(Send + Sync);