diff options
author | HampusM <hampus@hampusmat.com> | 2022-07-27 14:43:51 +0200 |
---|---|---|
committer | HampusM <hampus@hampusmat.com> | 2022-07-27 14:43:51 +0200 |
commit | 224e59112e65ce6cbafe5a87dba031dd11e936a8 (patch) | |
tree | e07fe689f2e1fa50d205eb5e984f474007374da4 /src/libs/intertrait | |
parent | 26667cddb857a21522b49b5ac83228b2a72a353a (diff) |
refactor: add back Intertrait tests & Rc support
Diffstat (limited to 'src/libs/intertrait')
-rw-r--r-- | src/libs/intertrait/cast/arc.rs | 32 | ||||
-rw-r--r-- | src/libs/intertrait/cast/box.rs | 30 | ||||
-rw-r--r-- | src/libs/intertrait/cast/mod.rs | 18 | ||||
-rw-r--r-- | src/libs/intertrait/cast/rc.rs | 32 | ||||
-rw-r--r-- | src/libs/intertrait/cast_box.rs | 32 | ||||
-rw-r--r-- | src/libs/intertrait/cast_rc.rs | 34 | ||||
-rw-r--r-- | src/libs/intertrait/mod.rs | 247 |
7 files changed, 336 insertions, 89 deletions
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()); + } +} |