#![deny(unsafe_code)]
use std::any::Any;
pub trait Downcast: Any {
fn into_any(self: Box<Self>) -> Box<dyn Any>;
fn into_any_rc(self: ::std::rc::Rc<Self>) -> ::std::rc::Rc<dyn Any>;
fn as_any(&self) -> &dyn Any;
fn as_any_mut(&mut self) -> &mut dyn Any;
}
impl<T: Any> Downcast for T {
fn into_any(self: Box<Self>) -> Box<dyn Any> { self }
fn into_any_rc(self: ::std::rc::Rc<Self>) -> ::std::rc::Rc<dyn Any> { self }
fn as_any(&self) -> &dyn Any { self }
fn as_any_mut(&mut self) -> &mut dyn Any { self }
}
pub trait DowncastSync: Downcast + Send + Sync {
fn into_any_arc(self: ::std::sync::Arc<Self>) -> ::std::sync::Arc<dyn Any + Send + Sync>;
}
impl<T: Any + Send + Sync> DowncastSync for T {
fn into_any_arc(self: ::std::sync::Arc<Self>) -> ::std::sync::Arc<dyn Any + Send + Sync> { self }
}
#[macro_export(local_inner_macros)]
macro_rules! impl_downcast {
(@impl_full
$trait_:ident [$($param_types:tt)*]
for [$($forall_types:ident),*]
where [$($preds:tt)*]
) => {
impl_downcast! {
@inject_where
[impl<$($forall_types),*> dyn $trait_<$($param_types)*>]
types [$($forall_types),*]
where [$($preds)*]
[{
impl_downcast! { @impl_body $trait_ [$($param_types)*] }
}]
}
};
(@impl_full_sync
$trait_:ident [$($param_types:tt)*]
for [$($forall_types:ident),*]
where [$($preds:tt)*]
) => {
impl_downcast! {
@inject_where
[impl<$($forall_types),*> dyn $trait_<$($param_types)*>]
types [$($forall_types),*]
where [$($preds)*]
[{
impl_downcast! { @impl_body $trait_ [$($param_types)*] }
impl_downcast! { @impl_body_sync $trait_ [$($param_types)*] }
}]
}
};
(@impl_body $trait_:ident [$($types:tt)*]) => {
#[inline]
pub fn is<__T: $trait_<$($types)*>>(&self) -> bool {
$crate::Downcast::as_any(self).is::<__T>()
}
#[inline]
pub fn downcast<__T: $trait_<$($types)*>>(
self: ::std::boxed::Box<Self>
) -> ::std::result::Result<::std::boxed::Box<__T>, ::std::boxed::Box<Self>> {
if self.is::<__T>() {
Ok($crate::Downcast::into_any(self).downcast::<__T>().unwrap())
} else {
Err(self)
}
}
#[inline]
pub fn downcast_rc<__T: $trait_<$($types)*>>(
self: ::std::rc::Rc<Self>
) -> ::std::result::Result<::std::rc::Rc<__T>, ::std::rc::Rc<Self>> {
if self.is::<__T>() {
Ok($crate::Downcast::into_any_rc(self).downcast::<__T>().unwrap())
} else {
Err(self)
}
}
#[inline]
pub fn downcast_ref<__T: $trait_<$($types)*>>(&self) -> ::std::option::Option<&__T> {
$crate::Downcast::as_any(self).downcast_ref::<__T>()
}
#[inline]
pub fn downcast_mut<__T: $trait_<$($types)*>>(&mut self) -> ::std::option::Option<&mut __T> {
$crate::Downcast::as_any_mut(self).downcast_mut::<__T>()
}
};
(@impl_body_sync $trait_:ident [$($types:tt)*]) => {
#[inline]
pub fn downcast_arc<__T: $trait_<$($types)*>>(
self: ::std::sync::Arc<Self>,
) -> ::std::result::Result<::std::sync::Arc<__T>, ::std::sync::Arc<Self>>
where __T: ::std::any::Any + ::std::marker::Send + ::std::marker::Sync
{
if self.is::<__T>() {
Ok($crate::DowncastSync::into_any_arc(self).downcast::<__T>().unwrap())
} else {
Err(self)
}
}
};
(@inject_where [$($before:tt)*] types [] where [] [$($after:tt)*]) => {
impl_downcast! { @as_item $($before)* $($after)* }
};
(@inject_where [$($before:tt)*] types [$($types:ident),*] where [] [$($after:tt)*]) => {
impl_downcast! {
@as_item
$($before)*
where $( $types: ::std::any::Any + 'static ),*
$($after)*
}
};
(@inject_where [$($before:tt)*] types [$($types:ident),*] where [$($preds:tt)+] [$($after:tt)*]) => {
impl_downcast! {
@as_item
$($before)*
where
$( $types: ::std::any::Any + 'static, )*
$($preds)*
$($after)*
}
};
(@as_item $i:item) => { $i };
($trait_:ident ) => { impl_downcast! { @impl_full $trait_ [] for [] where [] } };
($trait_:ident <>) => { impl_downcast! { @impl_full $trait_ [] for [] where [] } };
(sync $trait_:ident ) => { impl_downcast! { @impl_full_sync $trait_ [] for [] where [] } };
(sync $trait_:ident <>) => { impl_downcast! { @impl_full_sync $trait_ [] for [] where [] } };
($trait_:ident < $($types:ident),* >) => {
impl_downcast! { @impl_full $trait_ [$($types),*] for [$($types),*] where [] }
};
(sync $trait_:ident < $($types:ident),* >) => {
impl_downcast! { @impl_full_sync $trait_ [$($types),*] for [$($types),*] where [] }
};
($trait_:ident < $($types:ident),* > where $($preds:tt)+) => {
impl_downcast! { @impl_full $trait_ [$($types),*] for [$($types),*] where [$($preds)*] }
};
(sync $trait_:ident < $($types:ident),* > where $($preds:tt)+) => {
impl_downcast! { @impl_full_sync $trait_ [$($types),*] for [$($types),*] where [$($preds)*] }
};
($trait_:ident assoc $($atypes:ident),*) => {
impl_downcast! { @impl_full $trait_ [$($atypes = $atypes),*] for [$($atypes),*] where [] }
};
(sync $trait_:ident assoc $($atypes:ident),*) => {
impl_downcast! { @impl_full_sync $trait_ [$($atypes = $atypes),*] for [$($atypes),*] where [] }
};
($trait_:ident assoc $($atypes:ident),* where $($preds:tt)+) => {
impl_downcast! { @impl_full $trait_ [$($atypes = $atypes),*] for [$($atypes),*] where [$($preds)*] }
};
(sync $trait_:ident assoc $($atypes:ident),* where $($preds:tt)+) => {
impl_downcast! { @impl_full_sync $trait_ [$($atypes = $atypes),*] for [$($atypes),*] where [$($preds)*] }
};
($trait_:ident < $($types:ident),* > assoc $($atypes:ident),*) => {
impl_downcast! {
@impl_full
$trait_ [$($types),*, $($atypes = $atypes),*]
for [$($types),*, $($atypes),*]
where []
}
};
(sync $trait_:ident < $($types:ident),* > assoc $($atypes:ident),*) => {
impl_downcast! {
@impl_full_sync
$trait_ [$($types),*, $($atypes = $atypes),*]
for [$($types),*, $($atypes),*]
where []
}
};
($trait_:ident < $($types:ident),* > assoc $($atypes:ident),* where $($preds:tt)+) => {
impl_downcast! {
@impl_full
$trait_ [$($types),*, $($atypes = $atypes),*]
for [$($types),*, $($atypes),*]
where [$($preds)*]
}
};
(sync $trait_:ident < $($types:ident),* > assoc $($atypes:ident),* where $($preds:tt)+) => {
impl_downcast! {
@impl_full_sync
$trait_ [$($types),*, $($atypes = $atypes),*]
for [$($types),*, $($atypes),*]
where [$($preds)*]
}
};
(concrete $trait_:ident < $($types:ident),* >) => {
impl_downcast! { @impl_full $trait_ [$($types),*] for [] where [] }
};
(sync concrete $trait_:ident < $($types:ident),* >) => {
impl_downcast! { @impl_full_sync $trait_ [$($types),*] for [] where [] }
};
(concrete $trait_:ident assoc $($atypes:ident = $aty:ty),*) => {
impl_downcast! { @impl_full $trait_ [$($atypes = $aty),*] for [] where [] }
};
(sync concrete $trait_:ident assoc $($atypes:ident = $aty:ty),*) => {
impl_downcast! { @impl_full_sync $trait_ [$($atypes = $aty),*] for [] where [] }
};
(concrete $trait_:ident < $($types:ident),* > assoc $($atypes:ident = $aty:ty),*) => {
impl_downcast! { @impl_full $trait_ [$($types),*, $($atypes = $aty),*] for [] where [] }
};
(sync concrete $trait_:ident < $($types:ident),* > assoc $($atypes:ident = $aty:ty),*) => {
impl_downcast! { @impl_full_sync $trait_ [$($types),*, $($atypes = $aty),*] for [] where [] }
};
}
#[cfg(test)]
mod test {
macro_rules! test_mod {
(
$test_mod_name:ident,
trait $base_trait:path { $($base_impl:tt)* },
non_sync: { $($non_sync_def:tt)+ },
sync: { $($sync_def:tt)+ }
) => {
test_mod! {
$test_mod_name,
trait $base_trait { $($base_impl:tt)* },
type dyn $base_trait,
non_sync: { $($non_sync_def)* },
sync: { $($sync_def)* }
}
};
(
$test_mod_name:ident,
trait $base_trait:path { $($base_impl:tt)* },
type $base_type:ty,
non_sync: { $($non_sync_def:tt)+ },
sync: { $($sync_def:tt)+ }
) => {
mod $test_mod_name {
test_mod!(
@test
$test_mod_name,
test_name: test_non_sync,
trait $base_trait { $($base_impl)* },
type $base_type,
{ $($non_sync_def)+ },
[]);
test_mod!(
@test
$test_mod_name,
test_name: test_sync,
trait $base_trait { $($base_impl)* },
type $base_type,
{ $($sync_def)+ },
[{
let arc: ::std::sync::Arc<$base_type> = ::std::sync::Arc::new(Foo(42));
let res = arc.downcast_arc::<Bar>();
assert!(res.is_err());
let arc = res.unwrap_err();
assert_eq!(
42, arc.downcast_arc::<Foo>().map_err(|_| "Shouldn't happen.").unwrap().0);
}]);
}
};
(
@test
$test_mod_name:ident,
test_name: $test_name:ident,
trait $base_trait:path { $($base_impl:tt)* },
type $base_type:ty,
{ $($def:tt)+ },
[ $($more_tests:block)* ]
) => {
#[test]
fn $test_name() {
#[allow(unused_imports)]
use super::super::{Downcast, DowncastSync};
#[allow(dead_code)] struct Any;
#[allow(dead_code)] struct Arc;
#[allow(dead_code)] struct Box;
#[allow(dead_code)] struct Option;
#[allow(dead_code)] struct Result;
#[allow(dead_code)] struct Rc;
#[allow(dead_code)] struct Send;
#[allow(dead_code)] struct Sync;
$($def)*
#[derive(Debug)]
struct Foo(u32);
impl $base_trait for Foo { $($base_impl)* }
#[derive(Debug)]
struct Bar(f64);
impl $base_trait for Bar { $($base_impl)* }
fn get_val(base: &::std::boxed::Box<$base_type>) -> u32 {
match base.downcast_ref::<Foo>() {
Some(val) => val.0,
None => 0
}
}
fn set_val(base: &mut ::std::boxed::Box<$base_type>, val: u32) {
if let Some(foo) = base.downcast_mut::<Foo>() {
foo.0 = val;
}
}
let mut base: ::std::boxed::Box<$base_type> = ::std::boxed::Box::new(Foo(42));
assert_eq!(get_val(&base), 42);
if let Some(foo) = base.downcast_ref::<Foo>() {
assert_eq!(foo.0, 42);
} else if let Some(bar) = base.downcast_ref::<Bar>() {
assert_eq!(bar.0, 42.0);
}
set_val(&mut base, 6*9);
assert_eq!(get_val(&base), 6*9);
assert!(base.is::<Foo>());
let res = base.downcast::<Bar>();
assert!(res.is_err());
let base = res.unwrap_err();
assert_eq!(
6*9, base.downcast::<Foo>().map_err(|_| "Shouldn't happen.").unwrap().0);
let rc: ::std::rc::Rc<$base_type> = ::std::rc::Rc::new(Foo(42));
let res = rc.downcast_rc::<Bar>();
assert!(res.is_err());
let rc = res.unwrap_err();
assert_eq!(
42, rc.downcast_rc::<Foo>().map_err(|_| "Shouldn't happen.").unwrap().0);
$($more_tests)*
}
};
(
$test_mod_name:ident,
trait $base_trait:path { $($base_impl:tt)* },
non_sync: { $($non_sync_def:tt)+ },
sync: { $($sync_def:tt)+ }
) => {
test_mod! {
$test_mod_name,
trait $base_trait { $($base_impl:tt)* },
type $base_trait,
non_sync: { $($non_sync_def)* },
sync: { $($sync_def)* }
}
};
}
test_mod!(non_generic, trait Base {},
non_sync: {
trait Base: Downcast {}
impl_downcast!(Base);
},
sync: {
trait Base: DowncastSync {}
impl_downcast!(sync Base);
});
test_mod!(generic, trait Base<u32> {},
non_sync: {
trait Base<T>: Downcast {}
impl_downcast!(Base<T>);
},
sync: {
trait Base<T>: DowncastSync {}
impl_downcast!(sync Base<T>);
});
test_mod!(constrained_generic, trait Base<u32> {},
non_sync: {
trait Base<T: Copy>: Downcast {}
impl_downcast!(Base<T> where T: Copy);
},
sync: {
trait Base<T: Copy>: DowncastSync {}
impl_downcast!(sync Base<T> where T: Copy);
});
test_mod!(associated,
trait Base { type H = f32; },
type dyn Base<H=f32>,
non_sync: {
trait Base: Downcast { type H; }
impl_downcast!(Base assoc H);
},
sync: {
trait Base: DowncastSync { type H; }
impl_downcast!(sync Base assoc H);
});
test_mod!(constrained_associated,
trait Base { type H = f32; },
type dyn Base<H=f32>,
non_sync: {
trait Base: Downcast { type H: Copy; }
impl_downcast!(Base assoc H where H: Copy);
},
sync: {
trait Base: DowncastSync { type H: Copy; }
impl_downcast!(sync Base assoc H where H: Copy);
});
test_mod!(param_and_associated,
trait Base<u32> { type H = f32; },
type dyn Base<u32, H=f32>,
non_sync: {
trait Base<T>: Downcast { type H; }
impl_downcast!(Base<T> assoc H);
},
sync: {
trait Base<T>: DowncastSync { type H; }
impl_downcast!(sync Base<T> assoc H);
});
test_mod!(constrained_param_and_associated,
trait Base<u32> { type H = f32; },
type dyn Base<u32, H=f32>,
non_sync: {
trait Base<T: Clone>: Downcast { type H: Copy; }
impl_downcast!(Base<T> assoc H where T: Clone, H: Copy);
},
sync: {
trait Base<T: Clone>: DowncastSync { type H: Copy; }
impl_downcast!(sync Base<T> assoc H where T: Clone, H: Copy);
});
test_mod!(concrete_parametrized, trait Base<u32> {},
non_sync: {
trait Base<T>: Downcast {}
impl_downcast!(concrete Base<u32>);
},
sync: {
trait Base<T>: DowncastSync {}
impl_downcast!(sync concrete Base<u32>);
});
test_mod!(concrete_associated,
trait Base { type H = u32; },
type dyn Base<H=u32>,
non_sync: {
trait Base: Downcast { type H; }
impl_downcast!(concrete Base assoc H=u32);
},
sync: {
trait Base: DowncastSync { type H; }
impl_downcast!(sync concrete Base assoc H=u32);
});
test_mod!(concrete_parametrized_associated,
trait Base<u32> { type H = f32; },
type dyn Base<u32, H=f32>,
non_sync: {
trait Base<T>: Downcast { type H; }
impl_downcast!(concrete Base<u32> assoc H=f32);
},
sync: {
trait Base<T>: DowncastSync { type H; }
impl_downcast!(sync concrete Base<u32> assoc H=f32);
});
}