//! A library providing direct casting among trait objects implemented by a type. //! //! In Rust, an object of a sub-trait of [`Any`] can be downcast to a concrete type //! at runtime if the type is known. But no direct casting between two trait objects //! (i.e. without involving the concrete type of the backing value) is possible //! (even no coercion from a trait object to that of its super-trait yet). //! //! With this crate, any trait object with [`CastFrom`] as its super-trait can be cast //! directly to another trait object implemented by the underlying type if the target //! traits are registered beforehand with the macros provided by this crate. //! //! //! 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 cast; 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 trait object backed by a [`Caster`]. /// /// [`Caster`]: ./struct.Caster.html #[distributed_slice] pub static CASTERS: [fn() -> (TypeId, BoxedCaster)] = [..]; /// A `HashMap` mapping `TypeId` of a [`Caster`] to an instance of it. /// /// [`Caster`]: ./struct.Caster.html 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 reference to or `Box` of a trait object for `Any` /// to a trait object of trait `Trait`. Each `Caster` instance is specific to a concrete /// type. That is, it knows how to cast to single specific trait implemented by single /// specific type. pub struct Caster { /// Casts a `Box` holding a trait object for `Any` to another `Box` holding a trait /// object for trait `Trait`. pub cast_box: CastBoxFn, /// Casts an `Rc` holding a trait object for `Any` to another `Rc` holding a trait /// object for trait `Trait`. pub cast_rc: CastRcFn, /// Casts an `Arc` holding a trait object for `Any + Sync + Send + 'static` /// to another `Arc` holding a trait object for trait `Trait`. 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 `S` to a trait `Trait` implemented /// by it. 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 Trait { 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 Trait { 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) } }