aboutsummaryrefslogtreecommitdiff
path: root/src/private/cast/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/private/cast/mod.rs')
-rw-r--r--src/private/cast/mod.rs273
1 files changed, 273 insertions, 0 deletions
diff --git a/src/private/cast/mod.rs b/src/private/cast/mod.rs
new file mode 100644
index 0000000..0b80057
--- /dev/null
+++ b/src/private/cast/mod.rs
@@ -0,0 +1,273 @@
+//! 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 arc;
+pub mod boxed;
+pub mod error;
+pub mod rc;
+
+pub type BoxedCaster = Box<dyn Any + Send + Sync>;
+
+/// 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 type or trait backed by a [`Caster`].
+#[distributed_slice]
+pub static CASTERS: [fn() -> (TypeId, BoxedCaster)] = [..];
+
+/// A `HashMap` mapping `TypeId` of a [`Caster`] to an instance of it.
+static CASTER_MAP: Lazy<AHashMap<(TypeId, TypeId), BoxedCaster>> = Lazy::new(|| {
+ CASTERS
+ .iter()
+ .map(|caster_fn| {
+ let (type_id, caster) = caster_fn();
+
+ ((type_id, (*caster).type_id()), caster)
+ })
+ .collect()
+});
+
+type CastBoxFn<Dest> = fn(from: Box<dyn Any>) -> Result<Box<Dest>, CasterError>;
+
+type CastRcFn<Dest> = fn(from: Rc<dyn Any>) -> Result<Rc<Dest>, CasterError>;
+
+type CastArcFn<Dest> =
+ fn(from: Arc<dyn Any + Sync + Send + 'static>) -> Result<Arc<Dest>, CasterError>;
+
+/// A `Caster` knows how to cast a type or trait to the type or trait `Dest`. Each
+/// `Caster` instance is specific to a concrete type. That is, it knows how to cast to
+/// single specific type or trait implemented by single specific type.
+pub struct Caster<Dest: ?Sized + 'static>
+{
+ /// Casts a `Box` holding a type or trait object for `Any` to another `Box` holding a
+ /// type or trait `Dest`.
+ pub cast_box: CastBoxFn<Dest>,
+
+ /// Casts an `Rc` holding a type or trait for `Any` to another `Rc` holding a type or
+ /// trait `Dest`.
+ pub cast_rc: CastRcFn<Dest>,
+
+ /// Casts an `Arc` holding a type or trait for `Any + Sync + Send + 'static` to
+ /// another `Arc` holding a type or trait for `Dest`.
+ pub opt_cast_arc: Option<CastArcFn<Dest>>,
+}
+
+impl<Dest: ?Sized + 'static> Caster<Dest>
+{
+ pub fn new(cast_box: CastBoxFn<Dest>, cast_rc: CastRcFn<Dest>) -> Caster<Dest>
+ {
+ Caster::<Dest> {
+ cast_box,
+ cast_rc,
+ opt_cast_arc: None,
+ }
+ }
+
+ #[allow(clippy::similar_names)]
+ pub fn new_sync(
+ cast_box: CastBoxFn<Dest>,
+ cast_rc: CastRcFn<Dest>,
+ cast_arc: CastArcFn<Dest>,
+ ) -> Caster<Dest>
+ {
+ Caster::<Dest> {
+ 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<Dest>` from a concrete type with the id `type_id` to a type or trait
+/// `Dest`.
+fn get_caster<Dest: ?Sized + 'static>(
+ type_id: TypeId,
+) -> Result<&'static Caster<Dest>, GetCasterError>
+{
+ let any_caster = CASTER_MAP
+ .get(&(type_id, TypeId::of::<Caster<Dest>>()))
+ .ok_or(GetCasterError::NotFound)?;
+
+ any_caster
+ .downcast_ref::<Caster<Dest>>()
+ .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<Self>) -> Box<dyn Any>;
+
+ /// Returns an `Rc` of `Any`, which is backed by the type implementing this trait.
+ 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<Source: Sized + Any + 'static> CastFrom for Source
+{
+ fn box_any(self: Box<Self>) -> Box<dyn Any>
+ {
+ self
+ }
+
+ fn rc_any(self: Rc<Self>) -> Rc<dyn Any>
+ {
+ self
+ }
+}
+
+impl CastFrom for dyn Any + 'static
+{
+ fn box_any(self: Box<Self>) -> Box<dyn Any>
+ {
+ self
+ }
+
+ fn rc_any(self: Rc<Self>) -> Rc<dyn Any>
+ {
+ self
+ }
+}
+
+impl<Source: Sized + Sync + Send + 'static> CastFromSync for Source
+{
+ 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>
+ {
+ self
+ }
+
+ fn rc_any(self: Rc<Self>) -> Rc<dyn Any>
+ {
+ 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::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::<subjects::Ninja>();
+
+ let caster = Box::new(Caster::<dyn Debug> {
+ cast_box: |from| {
+ let concrete = from
+ .downcast::<subjects::Ninja>()
+ .map_err(|_| CasterError::CastBoxFailed)?;
+
+ Ok(concrete as Box<dyn Debug>)
+ },
+ cast_rc: |from| {
+ let concrete = from
+ .downcast::<subjects::Ninja>()
+ .map_err(|_| CasterError::CastRcFailed)?;
+
+ Ok(concrete as Rc<dyn Debug>)
+ },
+ opt_cast_arc: Some(|from| {
+ let concrete = from
+ .downcast::<subjects::Ninja>()
+ .map_err(|_| CasterError::CastArcFailed)?;
+
+ Ok(concrete as Arc<dyn Debug>)
+ }),
+ });
+ (type_id, caster)
+ }
+}