#![cfg_attr(feature = "no_std", no_std)]
#[cfg(all(test, feature = "no_std"))]
extern crate std;
#[cfg(not(feature = "no_std"))]
extern crate std as core;
use core::any::Any as StdAny;
use core::any::TypeId;
pub trait Any: StdAny {
#[doc(hidden)]
fn get_type_id(&self) -> TypeId;
}
impl<T: StdAny> Any for T {
fn get_type_id(&self) -> TypeId { TypeId::of::<T>() }
}
#[repr(C)]
#[derive(Copy, Clone)]
#[doc(hidden)]
pub struct TraitObject {
pub data: *mut (),
pub vtable: *mut (),
}
#[macro_export]
macro_rules! mopafy {
($trait_:ident) => {
mopafy!($trait_, core = std, alloc = std);
};
($trait_:ident, core = $core:ident) => {
#[allow(dead_code)]
impl $trait_ {
#[inline]
pub fn is<T: $trait_>(&self) -> bool {
::$core::any::TypeId::of::<T>() == $crate::Any::get_type_id(self)
}
#[inline]
pub fn downcast_ref<T: $trait_>(&self) -> ::$core::option::Option<&T> {
if self.is::<T>() {
unsafe {
::$core::option::Option::Some(self.downcast_ref_unchecked())
}
} else {
::$core::option::Option::None
}
}
#[inline]
pub unsafe fn downcast_ref_unchecked<T: $trait_>
(&self) -> &T {
let trait_object: $crate::TraitObject = ::$core::mem::transmute(self);
::$core::mem::transmute(trait_object.data)
}
#[inline]
pub fn downcast_mut<T: $trait_>(&mut self) -> ::$core::option::Option<&mut T> {
if self.is::<T>() {
unsafe {
::$core::option::Option::Some(self.downcast_mut_unchecked())
}
} else {
::$core::option::Option::None
}
}
#[inline]
pub unsafe fn downcast_mut_unchecked<T: $trait_>
(&mut self) -> &mut T {
let trait_object: $crate::TraitObject = ::$core::mem::transmute(self);
::$core::mem::transmute(trait_object.data)
}
}
};
($trait_:ident, core = $core:ident, alloc = $alloc:ident) => {
mopafy!($trait_, core = $core);
#[allow(dead_code)]
impl $trait_ {
#[inline]
pub fn downcast<T: $trait_>(self: ::$alloc::boxed::Box<Self>)
-> ::$core::result::Result<::$alloc::boxed::Box<T>,
::$alloc::boxed::Box<Self>> {
if self.is::<T>() {
unsafe {
::$core::result::Result::Ok(self.downcast_unchecked())
}
} else {
::$core::result::Result::Err(self)
}
}
#[inline]
pub unsafe fn downcast_unchecked<T: $trait_>(self: ::$alloc::boxed::Box<Self>)
-> ::$alloc::boxed::Box<T> {
let trait_object: $crate::TraitObject = ::$core::mem::transmute(self);
::$core::mem::transmute(trait_object.data)
}
}
};
}
#[cfg(test)]
mod tests {
use std::prelude::v1::*;
trait Person: super::Any {
fn weight(&self) -> i16;
}
mopafy!(Person);
#[derive(Clone, Debug, PartialEq)]
struct Benny {
kilograms_of_food: u8,
}
impl Person for Benny {
fn weight(&self) -> i16 {
self.kilograms_of_food as i16 + 60
}
}
#[derive(Clone, Debug, PartialEq)]
struct Chris;
impl Person for Chris {
fn weight(&self) -> i16 { -5 }
}
#[test]
fn test_ref() {
let benny = Benny { kilograms_of_food: 13 };
let benny_ptr: *const Benny = &benny;
let person: &Person = &benny;
assert!(person.is::<Benny>());
assert_eq!(person.downcast_ref::<Benny>().map(|x| x as *const Benny), Some(benny_ptr));
assert_eq!(unsafe { person.downcast_ref_unchecked::<Benny>() as *const Benny }, benny_ptr);
assert!(!person.is::<Chris>());
assert_eq!(person.downcast_ref::<Chris>(), None);
}
#[test]
fn test_mut() {
let mut benny = Benny { kilograms_of_food: 13 };
let benny_ptr: *const Benny = &benny;
let person: &mut Person = &mut benny;
assert!(person.is::<Benny>());
assert_eq!(person.downcast_ref::<Benny>().map(|x| x as *const Benny), Some(benny_ptr));
assert_eq!(person.downcast_mut::<Benny>().map(|x| &*x as *const Benny), Some(benny_ptr));
assert_eq!(unsafe { person.downcast_ref_unchecked::<Benny>() as *const Benny }, benny_ptr);
assert_eq!(unsafe { &*person.downcast_mut_unchecked::<Benny>() as *const Benny }, benny_ptr);
assert!(!person.is::<Chris>());
assert_eq!(person.downcast_ref::<Chris>(), None);
assert_eq!(person.downcast_mut::<Chris>(), None);
}
#[test]
fn test_box() {
let mut benny = Benny { kilograms_of_food: 13 };
let mut person: Box<Person> = Box::new(benny.clone());
assert!(person.is::<Benny>());
assert_eq!(person.downcast_ref::<Benny>(), Some(&benny));
assert_eq!(person.downcast_mut::<Benny>(), Some(&mut benny));
assert_eq!(person.downcast::<Benny>().map(|x| *x).ok(), Some(benny.clone()));
person = Box::new(benny.clone());
assert_eq!(unsafe { person.downcast_ref_unchecked::<Benny>() }, &benny);
assert_eq!(unsafe { person.downcast_mut_unchecked::<Benny>() }, &mut benny);
assert_eq!(unsafe { *person.downcast_unchecked::<Benny>() }, benny);
person = Box::new(benny.clone());
assert!(!person.is::<Chris>());
assert_eq!(person.downcast_ref::<Chris>(), None);
assert_eq!(person.downcast_mut::<Chris>(), None);
assert!(person.downcast::<Chris>().err().is_some());
}
}