#![cfg_attr(test, deny(warnings))]
#![deny(missing_docs)]
extern crate unsafe_any as uany;
use uany::UnsafeAnyExt;
use std::any::{TypeId, Any};
use std::{fmt, mem};
pub struct Dynamic {
id: TypeId,
data: Dyn
}
impl Dynamic {
#[inline]
pub fn new<T: Any>(val: T) -> Box<Dynamic> {
let un_sized = Box::new(Described {
id: TypeId::of::<T>(),
data: val
}) as Box<Described<Dyn>>;
unsafe { mem::transmute(un_sized) }
}
#[inline]
pub fn from_ref<T: Any>(val: &Described<T>) -> &Dynamic {
let un_sized = val as &Described<Dyn>;
unsafe { mem::transmute(un_sized) }
}
#[inline]
pub fn from_mut<T: Any>(val: &mut Described<T>) -> &mut Dynamic {
let un_sized = val as &mut Described<Dyn>;
unsafe { mem::transmute(un_sized) }
}
#[inline]
pub fn id(&self) -> TypeId { self.id }
#[inline(always)]
pub fn is<T: Any>(&self) -> bool {
self.id == TypeId::of::<T>()
}
#[inline]
pub fn downcast<T: Any>(self: Box<Self>) -> Result<Box<Described<T>>, Box<Self>> {
if self.is::<T>() {
Ok(unsafe { Box::from_raw(Box::into_raw(self) as *mut Described<T>) })
} else {
Err(self)
}
}
#[inline]
pub fn downcast_ref<T: Any>(&self) -> Option<&T> {
if self.is::<T>() {
Some(unsafe { self.data.downcast_ref_unchecked() })
} else {
None
}
}
#[inline]
pub fn downcast_mut<T: Any>(&mut self) -> Option<&mut T> {
if self.is::<T>() {
Some(unsafe { self.data.downcast_mut_unchecked() })
} else {
None
}
}
}
impl fmt::Debug for Dynamic {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Dynamic")
.field("id", &self.id)
.field("data", &"{{ dynamically typed value }}")
.finish()
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Described<T: ?Sized> {
id: TypeId,
pub data: T
}
impl<T: Any> Described<T> {
#[inline]
pub fn new(val: T) -> Described<T> {
Described {
id: TypeId::of::<T>(),
data: val
}
}
#[inline]
pub fn id(&self) -> TypeId { self.id }
}
trait Dyn {}
impl<T> Dyn for T {}
unsafe impl UnsafeAnyExt for Dyn {}
#[cfg(test)]
mod test {
use std::any::TypeId;
use {Dynamic, Described};
struct X(usize);
struct Y(usize);
struct Z(usize);
#[test]
fn test_downcasting() {
let mut x = Dynamic::new(X(1));
assert!(x.is::<X>());
assert!(!x.is::<Y>());
assert!(!x.is::<Z>());
*x.downcast_mut::<X>().unwrap() = X(100);
assert_eq!(x.downcast_ref::<X>().unwrap().0, 100);
let described_x = x.downcast::<X>().unwrap();
assert_eq!(described_x.id(), TypeId::of::<X>());
assert_eq!(described_x.data.0, 100);
}
#[test]
fn test_dynamic_refs() {
let described_z = Described::new(Z(1000));
let z_ref = Dynamic::from_ref(&described_z);
assert_eq!(z_ref.downcast_ref::<Z>().unwrap().0, 1000);
}
}