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/libs/intertrait/mod.rs | 247 ++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 224 insertions(+), 23 deletions(-) (limited to 'src/libs/intertrait/mod.rs') 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