//! Originally from Intertrait by CodeChain //! //! //! //! //! Licensed under either of //! //! Apache License, Version 2.0 (LICENSE-APACHE or ) //! MIT license (LICENSE-MIT or ) //! //! at your option. 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; /// A distributed slice gathering constructor functions for [`Caster`]s. /// /// A constructor function returns `TypeId` of a concrete type involved in the casting /// and a `Box` of a type or trait backed by a [`Caster`]. #[distributed_slice] pub static CASTERS: [fn() -> (TypeId, BoxedCaster)] = [..]; /// A `HashMap` mapping `TypeId` of a [`Caster`] to an instance of it. static CASTER_MAP: Lazy> = Lazy::new(|| { CASTERS .iter() .map(|caster_fn| { let (type_id, caster) = caster_fn(); ((type_id, (*caster).type_id()), caster) }) .collect() }); type CastBoxFn = fn(from: Box) -> Result, CasterError>; type CastRcFn = fn(from: Rc) -> Result, CasterError>; type CastArcFn = fn(from: Arc) -> Result, CasterError>; /// A `Caster` knows how to cast a type or trait to the type or trait `Dest`. Each /// `Caster` instance is specific to a concrete type. That is, it knows how to cast to /// single specific type or trait implemented by single specific type. pub struct Caster { /// Casts a `Box` holding a type or trait object for `Any` to another `Box` holding a /// type or trait `Dest`. pub cast_box: CastBoxFn, /// Casts an `Rc` holding a type or trait for `Any` to another `Rc` holding a type or /// trait `Dest`. pub cast_rc: CastRcFn, /// Casts an `Arc` holding a type or trait for `Any + Sync + Send + 'static` to /// another `Arc` holding a type or trait for `Dest`. pub opt_cast_arc: Option>, } impl Caster { pub fn new(cast_box: CastBoxFn, cast_rc: CastRcFn) -> Caster { Caster:: { cast_box, cast_rc, opt_cast_arc: None, } } #[allow(clippy::similar_names)] pub fn new_sync( cast_box: CastBoxFn, cast_rc: CastRcFn, cast_arc: CastArcFn, ) -> Caster { Caster:: { 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, } /// Returns a `Caster` from a concrete type with the id `type_id` to a type or trait /// `Dest`. fn get_caster( type_id: TypeId, ) -> Result<&'static Caster, GetCasterError> { let any_caster = CASTER_MAP .get(&(type_id, TypeId::of::>())) .ok_or(GetCasterError::NotFound)?; any_caster .downcast_ref::>() .ok_or(GetCasterError::DowncastFailed) } #[derive(Debug, thiserror::Error)] pub enum GetCasterError { #[error("Caster not found")] NotFound, #[error("Failed to downcast caster")] DowncastFailed, } /// `CastFrom` must be extended by a trait that wants to allow for casting into another /// trait. /// /// It is used for obtaining a trait object for [`Any`] from a trait object for its /// sub-trait, and blanket implemented for all `Sized + Any + 'static` types. /// /// # Examples /// ```ignore /// trait Source: CastFrom { /// ... /// } /// ``` pub trait CastFrom: Any + 'static { /// Returns a `Box` of `Any`, which is backed by the type implementing this trait. fn box_any(self: Box) -> Box; /// Returns an `Rc` of `Any`, which is backed by the type implementing this trait. fn rc_any(self: Rc) -> Rc; } /// `CastFromSync` must be extended by a trait that is `Any + Sync + Send + 'static` /// and wants to allow for casting into another trait behind references and smart pointers /// especially including `Arc`. /// /// It is used for obtaining a trait object for [`Any + Sync + Send + 'static`] from an /// object for its sub-trait, and blanket implemented for all `Sized + Sync + Send + /// 'static` types. /// /// # Examples /// ```ignore /// trait Source: CastFromSync { /// ... /// } /// ``` pub trait CastFromSync: CastFrom + Sync + Send + 'static { fn arc_any(self: Arc) -> Arc; } impl CastFrom for Source { fn box_any(self: Box) -> Box { self } fn rc_any(self: Rc) -> Rc { self } } impl CastFrom for dyn Any + 'static { fn box_any(self: Box) -> Box { self } fn rc_any(self: Rc) -> Rc { self } } impl CastFromSync for Source { fn arc_any(self: Arc) -> Arc { self } } impl CastFrom for dyn Any + Sync + Send + 'static { fn box_any(self: Box) -> Box { self } fn rc_any(self: Rc) -> Rc { self } } impl CastFromSync for dyn Any + Sync + Send + 'static { fn arc_any(self: Arc) -> Arc { 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::(); let caster = Box::new(Caster:: { cast_box: |from| { let concrete = from .downcast::() .map_err(|_| CasterError::CastBoxFailed)?; Ok(concrete as Box) }, cast_rc: |from| { let concrete = from .downcast::() .map_err(|_| CasterError::CastRcFailed)?; Ok(concrete as Rc) }, opt_cast_arc: Some(|from| { let concrete = from .downcast::() .map_err(|_| CasterError::CastArcFailed)?; Ok(concrete as Arc) }), }); (type_id, caster) } }