use std::any::{Any, TypeId};
use std::rc::Rc;
use std::sync::Arc;
use ahash::AHashMap;
use linkme::distributed_slice;
use once_cell::sync::Lazy;
pub mod arc;
pub mod boxed;
pub mod error;
pub mod rc;
pub type BoxedCaster = Box<dyn Any + Send + Sync>;
#[distributed_slice]
pub static CASTERS: [fn() -> (TypeId, BoxedCaster)] = [..];
static CASTER_MAP: Lazy<AHashMap<(TypeId, TypeId), BoxedCaster>> = Lazy::new(|| {
CASTERS
.iter()
.map(|caster_fn| {
let (type_id, caster) = caster_fn();
((type_id, (*caster).type_id()), caster)
})
.collect()
});
type CastBoxFn<Dest> = fn(from: Box<dyn Any>) -> Result<Box<Dest>, CasterError>;
type CastRcFn<Dest> = fn(from: Rc<dyn Any>) -> Result<Rc<Dest>, CasterError>;
type CastArcFn<Dest> =
fn(from: Arc<dyn Any + Sync + Send + 'static>) -> Result<Arc<Dest>, CasterError>;
pub struct Caster<Dest: ?Sized + 'static>
{
pub cast_box: CastBoxFn<Dest>,
pub cast_rc: CastRcFn<Dest>,
pub opt_cast_arc: Option<CastArcFn<Dest>>,
}
impl<Dest: ?Sized + 'static> Caster<Dest>
{
pub fn new(cast_box: CastBoxFn<Dest>, cast_rc: CastRcFn<Dest>) -> Caster<Dest>
{
Caster::<Dest> {
cast_box,
cast_rc,
opt_cast_arc: None,
}
}
#[allow(clippy::similar_names)]
pub fn new_sync(
cast_box: CastBoxFn<Dest>,
cast_rc: CastRcFn<Dest>,
cast_arc: CastArcFn<Dest>,
) -> Caster<Dest>
{
Caster::<Dest> {
cast_box,
cast_rc,
opt_cast_arc: Some(cast_arc),
}
}
}
#[derive(Debug, thiserror::Error)]
pub enum CasterError
{
#[error("Failed to cast Box")]
CastBoxFailed,
#[error("Failed to cast Rc")]
CastRcFailed,
#[error("Failed to cast Arc")]
CastArcFailed,
}
fn get_caster<Dest: ?Sized + 'static>(
type_id: TypeId,
) -> Result<&'static Caster<Dest>, GetCasterError>
{
let any_caster = CASTER_MAP
.get(&(type_id, TypeId::of::<Caster<Dest>>()))
.ok_or(GetCasterError::NotFound)?;
any_caster
.downcast_ref::<Caster<Dest>>()
.ok_or(GetCasterError::DowncastFailed)
}
#[derive(Debug, thiserror::Error)]
pub enum GetCasterError
{
#[error("Caster not found")]
NotFound,
#[error("Failed to downcast caster")]
DowncastFailed,
}
pub trait CastFrom: Any + 'static
{
fn box_any(self: Box<Self>) -> Box<dyn Any>;
fn rc_any(self: Rc<Self>) -> Rc<dyn Any>;
}
pub trait CastFromSync: CastFrom + Sync + Send + 'static
{
fn arc_any(self: Arc<Self>) -> Arc<dyn Any + Sync + Send + 'static>;
}
impl<Source: Sized + Any + 'static> CastFrom for Source
{
fn box_any(self: Box<Self>) -> Box<dyn Any>
{
self
}
fn rc_any(self: Rc<Self>) -> Rc<dyn Any>
{
self
}
}
impl CastFrom for dyn Any + 'static
{
fn box_any(self: Box<Self>) -> Box<dyn Any>
{
self
}
fn rc_any(self: Rc<Self>) -> Rc<dyn Any>
{
self
}
}
impl<Source: Sized + Sync + Send + 'static> CastFromSync for Source
{
fn arc_any(self: Arc<Self>) -> Arc<dyn Any + Sync + Send + 'static>
{
self
}
}
impl CastFrom for dyn Any + Sync + Send + 'static
{
fn box_any(self: Box<Self>) -> Box<dyn Any>
{
self
}
fn rc_any(self: Rc<Self>) -> Rc<dyn Any>
{
self
}
}
impl CastFromSync for dyn Any + Sync + Send + 'static
{
fn arc_any(self: Arc<Self>) -> Arc<dyn Any + Sync + Send + 'static>
{
self
}
}
#[cfg(test)]
mod tests
{
use std::any::TypeId;
use std::fmt::Debug;
use linkme::distributed_slice;
use super::*;
use crate::test_utils::subjects;
#[distributed_slice(super::CASTERS)]
static TEST_CASTER: fn() -> (TypeId, BoxedCaster) = create_test_caster;
fn create_test_caster() -> (TypeId, BoxedCaster)
{
let type_id = TypeId::of::<subjects::Ninja>();
let caster = Box::new(Caster::<dyn Debug> {
cast_box: |from| {
let concrete = from
.downcast::<subjects::Ninja>()
.map_err(|_| CasterError::CastBoxFailed)?;
Ok(concrete as Box<dyn Debug>)
},
cast_rc: |from| {
let concrete = from
.downcast::<subjects::Ninja>()
.map_err(|_| CasterError::CastRcFailed)?;
Ok(concrete as Rc<dyn Debug>)
},
opt_cast_arc: Some(|from| {
let concrete = from
.downcast::<subjects::Ninja>()
.map_err(|_| CasterError::CastArcFailed)?;
Ok(concrete as Arc<dyn Debug>)
}),
});
(type_id, caster)
}
}