aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorHampusM <hampus@hampusmat.com>2022-07-27 14:43:51 +0200
committerHampusM <hampus@hampusmat.com>2022-07-27 14:43:51 +0200
commit224e59112e65ce6cbafe5a87dba031dd11e936a8 (patch)
treee07fe689f2e1fa50d205eb5e984f474007374da4 /src
parent26667cddb857a21522b49b5ac83228b2a72a353a (diff)
refactor: add back Intertrait tests & Rc support
Diffstat (limited to 'src')
-rw-r--r--src/di_container.rs12
-rw-r--r--src/libs/intertrait/cast/arc.rs32
-rw-r--r--src/libs/intertrait/cast/box.rs30
-rw-r--r--src/libs/intertrait/cast/mod.rs18
-rw-r--r--src/libs/intertrait/cast/rc.rs32
-rw-r--r--src/libs/intertrait/cast_box.rs32
-rw-r--r--src/libs/intertrait/cast_rc.rs34
-rw-r--r--src/libs/intertrait/mod.rs247
8 files changed, 342 insertions, 95 deletions
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::<Interface>();
@@ -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<dyn IUserManager> =
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
+//!
+//! <https://github.com/CodeChain-io/intertrait>
+//! <https://crates.io/crates/intertrait/0.2.2>
+//!
+//! Licensed under either of
+//!
+//! Apache License, Version 2.0 (LICENSE-APACHE or <http://www.apache.org/licenses/LICENSE-2.0>)
+//! MIT license (LICENSE-MIT or <http://opensource.org/licenses/MIT>)
+//!
+//! 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<T: ?Sized + 'static>(self: Arc<Self>) -> Result<Arc<T>, Arc<Self>>;
+}
+
+/// A blanket implementation of `CastArc` for traits extending `CastFrom`, `Sync`, and `Send`.
+impl<S: ?Sized + CastFromSync> CastArc for S
+{
+ fn cast<T: ?Sized + 'static>(self: Arc<Self>) -> Result<Arc<T>, Arc<Self>>
+ {
+ match caster::<T>((*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
+//!
+//! <https://github.com/CodeChain-io/intertrait>
+//! <https://crates.io/crates/intertrait/0.2.2>
+//!
+//! Licensed under either of
+//!
+//! Apache License, Version 2.0 (LICENSE-APACHE or <http://www.apache.org/licenses/LICENSE-2.0>)
+//! MIT license (LICENSE-MIT or <http://opensource.org/licenses/MIT>)
+//!
+//! 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<T: ?Sized + 'static>(self: Box<Self>) -> Result<Box<T>, Box<Self>>;
+}
+
+/// A blanket implementation of `CastBox` for traits extending `CastFrom`.
+impl<S: ?Sized + CastFrom> CastBox for S
+{
+ fn cast<T: ?Sized + 'static>(self: Box<Self>) -> Result<Box<T>, Box<Self>>
+ {
+ match caster::<T>((*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
+//!
+//! <https://github.com/CodeChain-io/intertrait>
+//! <https://crates.io/crates/intertrait/0.2.2>
+//!
+//! Licensed under either of
+//!
+//! Apache License, Version 2.0 (LICENSE-APACHE or <http://www.apache.org/licenses/LICENSE-2.0>)
+//! MIT license (LICENSE-MIT or <http://opensource.org/licenses/MIT>)
+//!
+//! 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
+//!
+//! <https://github.com/CodeChain-io/intertrait>
+//! <https://crates.io/crates/intertrait/0.2.2>
+//!
+//! Licensed under either of
+//!
+//! Apache License, Version 2.0 (LICENSE-APACHE or <http://www.apache.org/licenses/LICENSE-2.0>)
+//! MIT license (LICENSE-MIT or <http://opensource.org/licenses/MIT>)
+//!
+//! 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<T: ?Sized + 'static>(self: Rc<Self>) -> Result<Rc<T>, Rc<Self>>;
+}
+
+/// A blanket implementation of `CastRc` for traits extending `CastFrom`.
+impl<S: ?Sized + CastFrom> CastRc for S
+{
+ fn cast<T: ?Sized + 'static>(self: Rc<Self>) -> Result<Rc<T>, Rc<Self>>
+ {
+ match caster::<T>((*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
- *
- * <https://github.com/CodeChain-io/intertrait>
- * <https://crates.io/crates/intertrait/0.2.2>
- *
- * Licensed under either of
- *
- * Apache License, Version 2.0 (LICENSE-APACHE or <http://www.apache.org/licenses/LICENSE-2.0>)
- * MIT license (LICENSE-MIT or <http://opensource.org/licenses/MIT>)
-
- * 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<Trait: ?Sized + 'static>(self: Box<Self>) -> Result<Box<Trait>, Box<Self>>;
-}
-
-/// A blanket implementation of `CastBox` for traits extending `CastFrom`.
-impl<CastableFrom: ?Sized + CastFrom> CastBox for CastableFrom
-{
- fn cast<Trait: ?Sized + 'static>(self: Box<Self>) -> Result<Box<Trait>, Box<Self>>
- {
- match caster::<Trait>((*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
- *
- * <https://github.com/CodeChain-io/intertrait>
- * <https://crates.io/crates/intertrait/0.2.2>
- *
- * Licensed under either of
- *
- * Apache License, Version 2.0 (LICENSE-APACHE or <http://www.apache.org/licenses/LICENSE-2.0>)
- * MIT license (LICENSE-MIT or <http://opensource.org/licenses/MIT>)
-
- * 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<Trait: ?Sized + 'static>(self: Rc<Self>) -> Result<Rc<Trait>, Rc<Self>>;
-}
-
-/// A blanket implementation of `CastRc` for traits extending `CastFrom`.
-impl<CastableFrom: ?Sized + CastFrom> CastRc for CastableFrom
-{
- fn cast<Trait: ?Sized + 'static>(self: Rc<Self>) -> Result<Rc<Trait>, Rc<Self>>
- {
- match caster::<Trait>((*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
- *
- * <https://github.com/CodeChain-io/intertrait>
- * <https://crates.io/crates/intertrait/0.2.2>
- *
- * Licensed under either of
- *
- * Apache License, Version 2.0 (LICENSE-APACHE or <http://www.apache.org/licenses/LICENSE-2.0>)
- * MIT license (LICENSE-MIT or <http://opensource.org/licenses/MIT>)
-
- * 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
+//!
+//! <https://github.com/CodeChain-io/intertrait>
+//! <https://crates.io/crates/intertrait/0.2.2>
+//!
+//! Licensed under either of
+//!
+//! Apache License, Version 2.0 (LICENSE-APACHE or <http://www.apache.org/licenses/LICENSE-2.0>)
+//! MIT license (LICENSE-MIT or <http://opensource.org/licenses/MIT>)
+//!
+//! 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<dyn Any + Send + Sync>;
-type CasterFn = fn() -> (TypeId, BoxedCaster);
-
+/// A distributed slice gathering constructor functions for [`Caster<T>`]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<T>`].
+///
+/// [`Caster<T>`]: ./struct.Caster.html
#[distributed_slice]
-pub static CASTERS: [CasterFn] = [..];
+pub static CASTERS: [fn() -> (TypeId, BoxedCaster)] = [..];
+/// A `HashMap` mapping `TypeId` of a [`Caster<T>`] to an instance of it.
+///
+/// [`Caster<T>`]: ./struct.Caster.html
static CASTER_MAP: Lazy<AHashMap<(TypeId, TypeId), BoxedCaster>> = Lazy::new(|| {
CASTERS
.iter()
@@ -39,15 +58,31 @@ static CASTER_MAP: Lazy<AHashMap<(TypeId, TypeId), BoxedCaster>> = Lazy::new(||
.collect()
});
+fn cast_arc_panic<Trait: ?Sized + 'static>(_: Arc<dyn Any + Sync + Send>) -> Arc<Trait>
+{
+ 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<Trait: ?Sized + 'static>
{
/// 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<dyn Any>) -> Box<Trait>,
/// 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<dyn Any>) -> Rc<Trait>,
+
+ /// 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<dyn Any + Sync + Send + 'static>) -> Arc<Trait>,
}
impl<Trait: ?Sized + 'static> Caster<Trait>
@@ -57,12 +92,29 @@ impl<Trait: ?Sized + 'static> Caster<Trait>
cast_rc: fn(from: Rc<dyn Any>) -> Rc<Trait>,
) -> Caster<Trait>
{
- Caster::<Trait> { cast_box, cast_rc }
+ Caster::<Trait> {
+ cast_box,
+ cast_rc,
+ cast_arc: cast_arc_panic,
+ }
+ }
+
+ #[allow(clippy::similar_names)]
+ pub fn new_sync(
+ cast_box: fn(from: Box<dyn Any>) -> Box<Trait>,
+ cast_rc: fn(from: Rc<dyn Any>) -> Rc<Trait>,
+ cast_arc: fn(from: Arc<dyn Any + Sync + Send>) -> Arc<Trait>,
+ ) -> Caster<Trait>
+ {
+ Caster::<Trait> {
+ cast_box,
+ cast_rc,
+ cast_arc,
+ }
}
}
-/// Returns a `Caster<Implementation, Trait>` from a concrete type `Implementation`
-/// from inside `CASTER_MAP` to a `Trait` implemented by it.
+/// Returns a `Caster<S, Trait>` from a concrete type `S` to a trait `Trait` implemented by it.
fn caster<Trait: ?Sized + 'static>(type_id: TypeId) -> Option<&'static Caster<Trait>>
{
CASTER_MAP
@@ -90,6 +142,24 @@ pub trait CastFrom: Any + 'static
fn rc_any(self: Rc<Self>) -> Rc<dyn Any>;
}
+/// `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<Self>) -> Arc<dyn Any + Sync + Send + 'static>;
+}
+
impl<Trait: Sized + Any + 'static> CastFrom for Trait
{
fn box_any(self: Box<Self>) -> Box<dyn Any>
@@ -116,6 +186,14 @@ impl CastFrom for dyn Any + 'static
}
}
+impl<Trait: Sized + Sync + Send + 'static> CastFromSync for Trait
+{
+ 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>
@@ -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<Self>) -> Arc<dyn Any + Sync + Send + 'static>
+ {
+ 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::<TestStruct>();
+ let caster = Box::new(Caster::<dyn Debug> {
+ cast_box: |from| from.downcast::<TestStruct>().unwrap(),
+ cast_rc: |from| from.downcast::<TestStruct>().unwrap(),
+ cast_arc: |from| from.downcast::<TestStruct>().unwrap(),
+ });
+ (type_id, caster)
+ }
+
+ #[test]
+ fn cast_box()
+ {
+ let ts = Box::new(TestStruct);
+ let st: Box<dyn SourceTrait> = ts;
+ let debug = st.cast::<dyn Debug>();
+ assert!(debug.is_ok());
+ }
+
+ #[test]
+ fn cast_rc()
+ {
+ let ts = Rc::new(TestStruct);
+ let st: Rc<dyn SourceTrait> = ts;
+ let debug = st.cast::<dyn Debug>();
+ assert!(debug.is_ok());
+ }
+
+ #[test]
+ fn cast_arc()
+ {
+ let ts = Arc::new(TestStruct);
+ let st: Arc<dyn SourceTrait> = ts;
+ let debug = st.cast::<dyn Debug>();
+ assert!(debug.is_ok());
+ }
+
+ #[test]
+ fn cast_box_wrong()
+ {
+ let ts = Box::new(TestStruct);
+ let st: Box<dyn SourceTrait> = ts;
+ let display = st.cast::<dyn Display>();
+ assert!(display.is_err());
+ }
+
+ #[test]
+ fn cast_rc_wrong()
+ {
+ let ts = Rc::new(TestStruct);
+ let st: Rc<dyn SourceTrait> = ts;
+ let display = st.cast::<dyn Display>();
+ assert!(display.is_err());
+ }
+
+ #[test]
+ fn cast_arc_wrong()
+ {
+ let ts = Arc::new(TestStruct);
+ let st: Arc<dyn SourceTrait> = ts;
+ let display = st.cast::<dyn Display>();
+ assert!(display.is_err());
+ }
+
+ #[test]
+ fn cast_box_from_any()
+ {
+ let ts = Box::new(TestStruct);
+ let st: Box<dyn Any> = ts;
+ let debug = st.cast::<dyn Debug>();
+ assert!(debug.is_ok());
+ }
+
+ #[test]
+ fn cast_rc_from_any()
+ {
+ let ts = Rc::new(TestStruct);
+ let st: Rc<dyn Any> = ts;
+ let debug = st.cast::<dyn Debug>();
+ assert!(debug.is_ok());
+ }
+
+ #[test]
+ fn cast_arc_from_any()
+ {
+ let ts = Arc::new(TestStruct);
+ let st: Arc<dyn Any + Send + Sync> = ts;
+ let debug = st.cast::<dyn Debug>();
+ assert!(debug.is_ok());
+ }
+}