use core::any::{Any, TypeId};
use alloc::boxed::Box;
use crate::{std_deps::sync::LazyLock, utils::table::HMap};
pub fn any_to_trait<T: ?Sized + 'static>(r: &dyn Any) -> Option<&T> {
TRAIT_CASTERS_MAP
.get(&(r.type_id(), TypeId::of::<T>()))
.map(|caster| {
((**caster)
.downcast_ref::<for<'a> fn(&'a (dyn Any + 'static)) -> &'a T>()
.unwrap())(r)
})
}
pub fn impls_trait_static<T: 'static, I: ?Sized + 'static>() -> bool {
TRAIT_CASTERS_MAP.contains_key(&(TypeId::of::<T>(), TypeId::of::<I>()))
}
#[doc(hidden)]
pub struct TraitCasterInfo {
pub from: TypeId,
pub to: TypeId,
pub caster: &'static (dyn Any + Sync + Send),
}
#[cfg(not(target_family = "wasm"))]
pub mod statics {
use super::*;
#[::pliron::linkme::distributed_slice]
pub static TRAIT_CASTERS: [TraitCasterInfo] = [..];
pub fn get_trait_casters() -> impl Iterator<Item = &'static TraitCasterInfo> {
TRAIT_CASTERS.iter()
}
}
#[cfg(target_family = "wasm")]
pub mod statics {
use super::*;
::pliron::inventory::collect!(&'static TraitCasterInfo);
pub fn get_trait_casters() -> impl Iterator<Item = &'static &'static TraitCasterInfo> {
::pliron::inventory::iter::<&'static TraitCasterInfo>()
}
}
pub use statics::*;
#[doc(hidden)]
static TRAIT_CASTERS_MAP: LazyLock<HMap<(TypeId, TypeId), &'static (dyn Any + Sync + Send)>> =
LazyLock::new(|| {
get_trait_casters()
.map(|lazy| ((lazy.from, lazy.to), lazy.caster))
.collect()
});
#[macro_export]
macro_rules! type_to_trait {
($ty_name:ty, $to_trait_name:path) => {
const _: () = {
#[cfg_attr(
not(target_family = "wasm"),
::pliron::linkme::distributed_slice
($crate::utils::trait_cast::TRAIT_CASTERS), linkme(crate = ::pliron::linkme)
)]
static CAST_TO_TRAIT: $crate::utils::trait_cast::TraitCasterInfo =
$crate::utils::trait_cast::TraitCasterInfo {
from: core::any::TypeId::of::<$ty_name>(),
to: core::any::TypeId::of::<dyn $to_trait_name>(),
caster: &(cast_to_trait
as for<'a> fn(
&'a (dyn core::any::Any + 'static),
) -> &'a (dyn $to_trait_name + 'static))
as &'static (dyn core::any::Any + Sync + Send),
};
#[cfg(target_family = "wasm")]
::pliron::inventory::submit! {
&CAST_TO_TRAIT
}
fn cast_to_trait<'a>(
r: &'a (dyn core::any::Any + 'static),
) -> &'a (dyn $to_trait_name + 'static) {
r.downcast_ref::<$ty_name>().unwrap() as &dyn $to_trait_name
}
};
};
}
pub fn boxed_any_to_trait<T: ?Sized + 'static>(r: Box<dyn Any>) -> Option<Box<T>> {
BOXED_TRAIT_CASTERS_MAP
.get(&((*r).type_id(), TypeId::of::<T>()))
.map(|caster| {
((**caster)
.downcast_ref::<fn(Box<dyn Any>) -> Box<T>>()
.unwrap())(r)
})
}
#[doc(hidden)]
pub struct BoxedTraitCasterInfo {
pub from: TypeId,
pub to: TypeId,
pub caster: &'static (dyn Any + Sync + Send),
}
#[cfg(not(target_family = "wasm"))]
pub mod boxed_statics {
use super::*;
#[::pliron::linkme::distributed_slice]
pub static BOXED_TRAIT_CASTERS: [BoxedTraitCasterInfo] = [..];
pub fn get_boxed_trait_casters() -> impl Iterator<Item = &'static BoxedTraitCasterInfo> {
BOXED_TRAIT_CASTERS.iter()
}
}
#[cfg(target_family = "wasm")]
pub mod boxed_statics {
use super::*;
::pliron::inventory::collect!(&'static BoxedTraitCasterInfo);
pub fn get_boxed_trait_casters() -> impl Iterator<Item = &'static &'static BoxedTraitCasterInfo>
{
::pliron::inventory::iter::<&'static BoxedTraitCasterInfo>()
}
}
pub use boxed_statics::*;
#[doc(hidden)]
static BOXED_TRAIT_CASTERS_MAP: LazyLock<HMap<(TypeId, TypeId), &'static (dyn Any + Sync + Send)>> =
LazyLock::new(|| {
get_boxed_trait_casters()
.map(|lazy| ((lazy.from, lazy.to), lazy.caster))
.collect()
});
#[macro_export]
macro_rules! boxed_type_to_trait {
($ty_name:ty, $to_trait_name:path) => {
const _: () = {
#[cfg_attr(
not(target_family = "wasm"),
::pliron::linkme::distributed_slice
($crate::utils::trait_cast::BOXED_TRAIT_CASTERS), linkme(crate = ::pliron::linkme)
)]
static BOXED_CAST_TO_TRAIT: $crate::utils::trait_cast::BoxedTraitCasterInfo =
$crate::utils::trait_cast::BoxedTraitCasterInfo {
from: core::any::TypeId::of::<$ty_name>(),
to: core::any::TypeId::of::<dyn $to_trait_name>(),
caster: &(boxed_cast_to_trait
as fn(
$crate::alloc::boxed::Box<dyn core::any::Any + 'static>,
)
-> $crate::alloc::boxed::Box<dyn $to_trait_name + 'static>)
as &'static (dyn core::any::Any + Sync + Send),
};
#[cfg(target_family = "wasm")]
::pliron::inventory::submit! {
&BOXED_CAST_TO_TRAIT
}
fn boxed_cast_to_trait(
r: $crate::alloc::boxed::Box<dyn core::any::Any + 'static>,
) -> $crate::alloc::boxed::Box<dyn $to_trait_name + 'static> {
r.downcast::<$ty_name>().unwrap() as $crate::alloc::boxed::Box<dyn $to_trait_name>
}
};
};
}