From 224e59112e65ce6cbafe5a87dba031dd11e936a8 Mon Sep 17 00:00:00 2001 From: HampusM Date: Wed, 27 Jul 2022 14:43:51 +0200 Subject: refactor: add back Intertrait tests & Rc support --- src/di_container.rs | 12 +- src/libs/intertrait/cast/arc.rs | 32 ++++++ src/libs/intertrait/cast/box.rs | 30 +++++ src/libs/intertrait/cast/mod.rs | 18 +++ src/libs/intertrait/cast/rc.rs | 32 ++++++ src/libs/intertrait/cast_box.rs | 32 ------ src/libs/intertrait/cast_rc.rs | 34 ------ src/libs/intertrait/mod.rs | 247 ++++++++++++++++++++++++++++++++++++---- 8 files changed, 342 insertions(+), 95 deletions(-) create mode 100644 src/libs/intertrait/cast/arc.rs create mode 100644 src/libs/intertrait/cast/box.rs create mode 100644 src/libs/intertrait/cast/mod.rs create mode 100644 src/libs/intertrait/cast/rc.rs delete mode 100644 src/libs/intertrait/cast_box.rs delete mode 100644 src/libs/intertrait/cast_rc.rs (limited to 'src') diff --git a/src/di_container.rs b/src/di_container.rs index 40de948..1f7700f 100644 --- a/src/di_container.rs +++ b/src/di_container.rs @@ -9,7 +9,7 @@ use error_stack::{Report, ResultExt}; use crate::castable_factory::CastableFactory; use crate::errors::di_container::DIContainerError; use crate::interfaces::injectable::Injectable; -use crate::libs::intertrait::cast_box::CastBox; +use crate::libs::intertrait::cast::CastBox; use crate::provider::{IProvider, InjectableTypeProvider, Providable}; use crate::ptr::InterfacePtr; @@ -225,7 +225,7 @@ impl DIContainer match binding_providable { Providable::Factory(binding_factory) => { - use crate::libs::intertrait::cast_rc::CastRc; + use crate::libs::intertrait::cast::CastRc; let factory_box_result = binding_factory.cast::(); @@ -493,14 +493,14 @@ mod tests let mut mock_provider = MockProvider::new(); mock_provider.expect_provide().returning(|_| { - Ok(Providable::Factory(FactoryPtr::new(CastableFactory::new( - &|users| { + Ok(Providable::Factory(crate::ptr::FactoryPtr::new( + CastableFactory::new(&|users| { let user_manager: InterfacePtr = InterfacePtr::new(UserManager::new(users)); user_manager - }, - )))) + }), + ))) }); di_container diff --git a/src/libs/intertrait/cast/arc.rs b/src/libs/intertrait/cast/arc.rs new file mode 100644 index 0000000..4099ae7 --- /dev/null +++ b/src/libs/intertrait/cast/arc.rs @@ -0,0 +1,32 @@ +//! 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::sync::Arc; + +use crate::libs::intertrait::{caster, CastFromSync}; + +pub trait CastArc +{ + /// Casts an `Arc` for this trait into that for type `T`. + fn cast(self: Arc) -> Result, Arc>; +} + +/// A blanket implementation of `CastArc` for traits extending `CastFrom`, `Sync`, and `Send`. +impl CastArc for S +{ + fn cast(self: Arc) -> Result, Arc> + { + match caster::((*self).type_id()) { + Some(caster) => Ok((caster.cast_arc)(self.arc_any())), + None => Err(self), + } + } +} diff --git a/src/libs/intertrait/cast/box.rs b/src/libs/intertrait/cast/box.rs new file mode 100644 index 0000000..c88fb84 --- /dev/null +++ b/src/libs/intertrait/cast/box.rs @@ -0,0 +1,30 @@ +//! 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 crate::libs::intertrait::{caster, CastFrom}; + +pub trait CastBox +{ + /// Casts a box to this trait into that of type `T`. If fails, returns the receiver. + fn cast(self: Box) -> Result, Box>; +} + +/// A blanket implementation of `CastBox` for traits extending `CastFrom`. +impl CastBox for S +{ + fn cast(self: Box) -> Result, Box> + { + match caster::((*self).type_id()) { + Some(caster) => Ok((caster.cast_box)(self.box_any())), + None => Err(self), + } + } +} diff --git a/src/libs/intertrait/cast/mod.rs b/src/libs/intertrait/cast/mod.rs new file mode 100644 index 0000000..83f7210 --- /dev/null +++ b/src/libs/intertrait/cast/mod.rs @@ -0,0 +1,18 @@ +//! 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. +mod arc; +mod r#box; +mod rc; + +pub use arc::*; +pub use r#box::*; +pub use rc::*; diff --git a/src/libs/intertrait/cast/rc.rs b/src/libs/intertrait/cast/rc.rs new file mode 100644 index 0000000..b53ced0 --- /dev/null +++ b/src/libs/intertrait/cast/rc.rs @@ -0,0 +1,32 @@ +//! 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::rc::Rc; + +use crate::libs::intertrait::{caster, CastFrom}; + +pub trait CastRc +{ + /// Casts an `Rc` for this trait into that for type `T`. + fn cast(self: Rc) -> Result, Rc>; +} + +/// A blanket implementation of `CastRc` for traits extending `CastFrom`. +impl CastRc for S +{ + fn cast(self: Rc) -> Result, Rc> + { + match caster::((*self).type_id()) { + Some(caster) => Ok((caster.cast_rc)(self.rc_any())), + None => Err(self), + } + } +} diff --git a/src/libs/intertrait/cast_box.rs b/src/libs/intertrait/cast_box.rs deleted file mode 100644 index b2ec77e..0000000 --- a/src/libs/intertrait/cast_box.rs +++ /dev/null @@ -1,32 +0,0 @@ -/** - * 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 crate::libs::intertrait::{caster, CastFrom}; - -pub trait CastBox -{ - /// Casts a box to this trait into that of `Trait`. If fails, returns the receiver. - fn cast(self: Box) -> Result, Box>; -} - -/// A blanket implementation of `CastBox` for traits extending `CastFrom`. -impl CastBox for CastableFrom -{ - fn cast(self: Box) -> Result, Box> - { - match caster::((*self).type_id()) { - Some(caster) => Ok((caster.cast_box)(self.box_any())), - None => Err(self), - } - } -} diff --git a/src/libs/intertrait/cast_rc.rs b/src/libs/intertrait/cast_rc.rs deleted file mode 100644 index 5901b5e..0000000 --- a/src/libs/intertrait/cast_rc.rs +++ /dev/null @@ -1,34 +0,0 @@ -/** - * 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::rc::Rc; - -use crate::libs::intertrait::{caster, CastFrom}; - -pub trait CastRc -{ - /// Casts an `Rc` for this trait into that for `Trait`. - fn cast(self: Rc) -> Result, Rc>; -} - -/// A blanket implementation of `CastRc` for traits extending `CastFrom`. -impl CastRc for CastableFrom -{ - fn cast(self: Rc) -> Result, Rc> - { - match caster::((*self).type_id()) { - Some(caster) => Ok((caster.cast_rc)(self.rc_any())), - None => Err(self), - } - } -} diff --git a/src/libs/intertrait/mod.rs b/src/libs/intertrait/mod.rs index 2cdc67d..a8d912b 100644 --- a/src/libs/intertrait/mod.rs +++ b/src/libs/intertrait/mod.rs @@ -1,33 +1,52 @@ -/** - * 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. -*/ +#![allow(clippy::module_name_repetitions)] + +//! 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_box; -pub mod cast_rc; +pub mod cast; pub type BoxedCaster = Box; -type CasterFn = fn() -> (TypeId, BoxedCaster); - +/// 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: [CasterFn] = [..]; +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() @@ -39,15 +58,31 @@ static CASTER_MAP: Lazy> = Lazy::new(|| .collect() }); +fn cast_arc_panic(_: Arc) -> Arc +{ + panic!("Prepend [sync] to the list of target traits for Sync + Send types") +} + +/// 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. +/// +/// An implementation of a trait for a concrete type doesn't need to manually provide +/// a `Caster`. Instead attach `#[cast_to]` to the `impl` block. +#[doc(hidden)] pub struct Caster { /// Casts a `Box` holding a trait object for `Any` to another `Box` holding a trait object - /// for `Trait`. + /// for trait `Trait`. pub cast_box: fn(from: Box) -> Box, /// Casts an `Rc` holding a trait object for `Any` to another `Rc` holding a trait object - /// for `Trait`. + /// for trait `Trait`. pub cast_rc: fn(from: Rc) -> Rc, + + /// Casts an `Arc` holding a trait object for `Any + Sync + Send + 'static` + /// to another `Arc` holding a trait object for trait `Trait`. + pub cast_arc: fn(from: Arc) -> Arc, } impl Caster @@ -57,12 +92,29 @@ impl Caster cast_rc: fn(from: Rc) -> Rc, ) -> Caster { - Caster:: { cast_box, cast_rc } + Caster:: { + cast_box, + cast_rc, + cast_arc: cast_arc_panic, + } + } + + #[allow(clippy::similar_names)] + pub fn new_sync( + cast_box: fn(from: Box) -> Box, + cast_rc: fn(from: Rc) -> Rc, + cast_arc: fn(from: Arc) -> Arc, + ) -> Caster + { + Caster:: { + cast_box, + cast_rc, + cast_arc, + } } } -/// Returns a `Caster` from a concrete type `Implementation` -/// from inside `CASTER_MAP` to a `Trait` implemented by it. +/// Returns a `Caster` from a concrete type `S` to a trait `Trait` implemented by it. fn caster(type_id: TypeId) -> Option<&'static Caster> { CASTER_MAP @@ -90,6 +142,24 @@ pub trait CastFrom: Any + 'static 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 @@ -116,6 +186,14 @@ impl CastFrom for dyn Any + 'static } } +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 @@ -128,3 +206,126 @@ impl CastFrom for dyn Any + Sync + Send + 'static self } } + +impl CastFromSync for dyn Any + Sync + Send + 'static +{ + fn arc_any(self: Arc) -> Arc + { + self + } +} + +#[cfg(test)] +mod tests +{ + use std::any::{Any, TypeId}; + use std::fmt::{Debug, Display}; + + use linkme::distributed_slice; + + #[allow(clippy::wildcard_imports)] + use super::cast::*; + use super::*; + + #[distributed_slice(super::CASTERS)] + static TEST_CASTER: fn() -> (TypeId, BoxedCaster) = create_test_caster; + + #[derive(Debug)] + struct TestStruct; + + trait SourceTrait: CastFromSync {} + + impl SourceTrait for TestStruct {} + + fn create_test_caster() -> (TypeId, BoxedCaster) + { + let type_id = TypeId::of::(); + let caster = Box::new(Caster:: { + cast_box: |from| from.downcast::().unwrap(), + cast_rc: |from| from.downcast::().unwrap(), + cast_arc: |from| from.downcast::().unwrap(), + }); + (type_id, caster) + } + + #[test] + fn cast_box() + { + let ts = Box::new(TestStruct); + let st: Box = ts; + let debug = st.cast::(); + assert!(debug.is_ok()); + } + + #[test] + fn cast_rc() + { + let ts = Rc::new(TestStruct); + let st: Rc = ts; + let debug = st.cast::(); + assert!(debug.is_ok()); + } + + #[test] + fn cast_arc() + { + let ts = Arc::new(TestStruct); + let st: Arc = ts; + let debug = st.cast::(); + assert!(debug.is_ok()); + } + + #[test] + fn cast_box_wrong() + { + let ts = Box::new(TestStruct); + let st: Box = ts; + let display = st.cast::(); + assert!(display.is_err()); + } + + #[test] + fn cast_rc_wrong() + { + let ts = Rc::new(TestStruct); + let st: Rc = ts; + let display = st.cast::(); + assert!(display.is_err()); + } + + #[test] + fn cast_arc_wrong() + { + let ts = Arc::new(TestStruct); + let st: Arc = ts; + let display = st.cast::(); + assert!(display.is_err()); + } + + #[test] + fn cast_box_from_any() + { + let ts = Box::new(TestStruct); + let st: Box = ts; + let debug = st.cast::(); + assert!(debug.is_ok()); + } + + #[test] + fn cast_rc_from_any() + { + let ts = Rc::new(TestStruct); + let st: Rc = ts; + let debug = st.cast::(); + assert!(debug.is_ok()); + } + + #[test] + fn cast_arc_from_any() + { + let ts = Arc::new(TestStruct); + let st: Arc = ts; + let debug = st.cast::(); + assert!(debug.is_ok()); + } +} -- cgit v1.2.3-18-g5258