#![warn(missing_docs)]
#![warn(rust_2018_idioms)]
#![cfg_attr(feature = "nightly", feature(coerce_unsized))]
#![cfg_attr(feature = "nightly", feature(ptr_metadata))]
use std::any::{Any, TypeId};
#[deprecated(since = "0.2", note = "use tid! macro instead")]
#[cfg(feature = "derive")]
pub use better_typeid_derive::impl_tid;
#[cfg(feature = "derive")]
pub use better_typeid_derive::Tid;
pub unsafe trait TidAble<'a>: Tid<'a> {
#[doc(hidden)]
type Static: ?Sized + Any;
}
pub trait TidExt<'a>: Tid<'a> {
fn is<T: Tid<'a>>(&self) -> bool {
self.self_id() == T::id()
}
fn downcast_ref<'b, T: Tid<'a>>(&'b self) -> Option<&'b T> {
if self.is::<T>() {
Some(unsafe { &*(self as *const _ as *const T) })
} else {
None
}
}
fn downcast_mut<'b, T: Tid<'a>>(&'b mut self) -> Option<&'b mut T> {
if self.is::<T>() {
Some(unsafe { &mut *(self as *mut _ as *mut T) })
} else {
None
}
}
fn downcast_rc<T: Tid<'a>>(self: Rc<Self>) -> Result<Rc<T>, Rc<Self>> {
if self.is::<T>() {
unsafe { Ok(Rc::from_raw(Rc::into_raw(self) as *const _)) }
} else {
Err(self)
}
}
fn downcast_arc<T: Tid<'a>>(self: Arc<Self>) -> Result<Arc<T>, Arc<Self>> {
if self.is::<T>() {
unsafe { Ok(Arc::from_raw(Arc::into_raw(self) as *const _)) }
} else {
Err(self)
}
}
fn downcast_box<T: Tid<'a>>(self: Box<Self>) -> Result<Box<T>, Box<Self>> {
if self.is::<T>() {
unsafe { Ok(Box::from_raw(Box::into_raw(self) as *mut _)) }
} else {
Err(self)
}
}
fn downcast_move<T: Tid<'a>>(self) -> Option<T>
where
Self: Sized,
{
if self.is::<T>() {
let this = core::mem::MaybeUninit::new(self);
return Some(unsafe { core::mem::transmute_copy(&this) });
}
None
}
}
impl<'a, X: ?Sized + Tid<'a>> TidExt<'a> for X {}
pub trait AnyExt: Any {
fn downcast_ref<T: Any>(this: &Self) -> Option<&T> {
if this.type_id() == TypeId::of::<T>() {
Some(unsafe { &*(this as *const _ as *const T) })
} else {
None
}
}
fn downcast_mut<T: Any>(this: &mut Self) -> Option<&mut T> {
if (*this).type_id() == TypeId::of::<T>() {
Some(unsafe { &mut *(this as *mut _ as *mut T) })
} else {
None
}
}
fn downcast_rc<T: Any>(this: Rc<Self>) -> Result<Rc<T>, Rc<Self>> {
if this.type_id() == TypeId::of::<T>() {
unsafe { Ok(Rc::from_raw(Rc::into_raw(this) as *const _)) }
} else {
Err(this)
}
}
fn downcast_arc<T: Any>(this: Arc<Self>) -> Result<Arc<T>, Arc<Self>> {
if this.type_id() == TypeId::of::<T>() {
unsafe { Ok(Arc::from_raw(Arc::into_raw(this) as *const _)) }
} else {
Err(this)
}
}
fn downcast_box<T: Any>(this: Box<Self>) -> Result<Box<T>, Box<Self>> {
if this.type_id() == TypeId::of::<T>() {
unsafe { Ok(Box::from_raw(Box::into_raw(this) as *mut _)) }
} else {
Err(this)
}
}
fn downcast_move<T: Any>(this: Self) -> Option<T>
where
Self: Sized,
{
let temp = &mut Some(this) as &mut dyn Any;
if let Some(temp) = AnyExt::downcast_mut::<Option<T>>(temp) {
return Some(temp.take().unwrap());
}
None
}
}
impl<T: ?Sized + Any> AnyExt for T {}
pub unsafe trait Tid<'a>: 'a {
fn self_id(&self) -> TypeId;
fn id() -> TypeId
where
Self: Sized;
}
unsafe impl<'a, T: ?Sized + TidAble<'a>> Tid<'a> for T {
#[inline]
fn self_id(&self) -> TypeId {
adjust_id::<T::Static>()
}
#[inline]
fn id() -> TypeId
where
Self: Sized,
{
adjust_id::<T::Static>()
}
}
#[inline(always)]
fn adjust_id<T: ?Sized + Any>() -> TypeId {
TypeId::of::<T>()
}
#[inline]
pub fn typeid_of<'a, T: ?Sized + TidAble<'a>>() -> TypeId {
adjust_id::<T::Static>()
}
impl<'a, T: Any> From<Box<T>> for Box<dyn Tid<'a> + 'a> {
#[inline]
fn from(f: Box<T>) -> Self {
unsafe { Box::from_raw(Box::into_raw(f) as *mut TypeIdAdjuster<T>) as _ }
}
}
impl<'a: 'b, 'b, T: Any> From<&'b T> for &'b (dyn Tid<'a> + 'a) {
#[inline]
fn from(f: &'b T) -> Self {
unsafe { &*(f as *const _ as *const TypeIdAdjuster<T> as *const _) }
}
}
impl<'a: 'b, 'b, T: Any> From<&'b mut T> for &'b mut (dyn Tid<'a> + 'a) {
#[inline]
fn from(f: &'b mut T) -> Self {
unsafe { &mut *(f as *mut _ as *mut TypeIdAdjuster<T> as *mut _) }
}
}
#[repr(transparent)]
struct TypeIdAdjuster<T: ?Sized>(T);
tid! {impl<'a,T:'static> TidAble<'a> for TypeIdAdjuster<T> where T:?Sized}
impl<'a> dyn Tid<'a> + 'a {
#[inline]
pub fn downcast_any_ref<T: Any>(&self) -> Option<&T> {
self.downcast_ref::<TypeIdAdjuster<T>>()
.map(|x| unsafe { &*(x as *const _ as *const T) })
}
#[inline]
pub fn downcast_any_mut<T: Any>(&mut self) -> Option<&mut T> {
self.downcast_mut::<TypeIdAdjuster<T>>()
.map(|x| unsafe { &mut *(x as *mut _ as *mut T) })
}
#[inline]
pub fn downcast_any_box<T: Any>(self: Box<Self>) -> Result<Box<T>, Box<Self>> {
self.downcast_box::<TypeIdAdjuster<T>>()
.map(|x| unsafe { Box::from_raw(Box::into_raw(x) as *mut T) as _ })
}
}
use std::cell::*;
use std::rc::*;
use std::sync::*;
tid!(impl<'a, T> TidAble<'a> for Box<T> where T:?Sized);
tid!(impl<'a, T> TidAble<'a> for Rc<T>);
tid!(impl<'a, T> TidAble<'a> for RefCell<T>);
tid!(impl<'a, T> TidAble<'a> for Cell<T>);
tid!(impl<'a, T> TidAble<'a> for Arc<T>);
tid!(impl<'a, T> TidAble<'a> for Mutex<T>);
tid!(impl<'a, T> TidAble<'a> for RwLock<T>);
const _: () = {
use core::marker::PhantomData;
type __Alias<'a, T> = Option<T>;
pub struct __TypeIdGenerator<'a, T: ?Sized>(PhantomData<&'a ()>, PhantomData<T>);
unsafe impl<'a, T: TidAble<'a>> TidAble<'a> for __Alias<'a, T> {
type Static = __TypeIdGenerator<'static, T::Static>;
}
};
tid! {impl<'a, T> TidAble<'a> for Vec<T>}
tid! { impl<'a,T,E> TidAble<'a> for Result<T,E> }
tid! { impl<'a> TidAble<'a> for dyn Tid<'a> + 'a }
#[macro_export]
macro_rules! tid {
($struct: ident) => {
unsafe impl<'a> $crate::TidAble<'a> for $struct {
type Static = $struct;
}
};
($struct: ident < $lt: lifetime >) => {
unsafe impl<'a> $crate::TidAble<'a> for $struct<'a> {
type Static = $struct<'static>;
}
};
(impl <$lt:lifetime $(,$param:ident)*> $tr:ident<$lt2:lifetime> for $($struct: tt)+ ) => {
$crate::tid!{ inner impl <$lt $(,$param)* static> $tr<$lt2> for $($struct)+ }
};
(inner impl <$lt:lifetime $(,$param:ident)* static $( $static_param:ident)* > Tid<$lt2:lifetime> for $($struct: tt)+ ) => {
$crate::tid!{ inner impl <$lt $(,$param)* static $( $static_param)*> TidAble<$lt2> for $($struct)+ }
};
(inner impl <$lt:lifetime $(,$param:ident)* static $( $static_param:ident)* > TidAble<$lt2:lifetime> for $($struct: tt)+ ) => {
const _:() = {
use core::marker::PhantomData;
type __Alias<$lt $(,$param)* $(,$static_param)*> = $crate::before_where!{ $($struct)+ };
pub struct __TypeIdGenerator<$lt $(,$param:?Sized)* $(,$static_param:?Sized)*>
(PhantomData<& $lt ()> $(,PhantomData<$param>)* $(,PhantomData<$static_param>)*);
$crate::impl_block!{
after where { $($struct)+ }
{unsafe impl<$lt $(,$param:$crate::TidAble<$lt>)* $(,$static_param: 'static)* > $crate::TidAble<$lt2> for __Alias<$lt $(,$param)* $(,$static_param)*>}
{
type Static = __TypeIdGenerator<'static $(,$param::Static)* $(,$static_param)*>;
}
}
};
};
(inner impl <$lt:lifetime $(,$param:ident)* static $( $static_param:ident)* > $tr:ident<$lt2:lifetime> for $($struct: tt)+ ) => {
compile_error!{" wrong trait, should be TidAble or Tid "}
};
(temp $(,$param:ident)* static $(,$static_param:ident)* impl <$lt:lifetime , $token:ident : 'static $($tail: tt)+ ) => {
$crate::tid!{ temp $(,$param)* static $(,$static_param)* , $token impl <$lt $($tail)+}
};
(temp $(,$param:ident)* static $(,$static_param:ident)* impl <$lt:lifetime , $token:ident $($tail: tt)+ ) => {
$crate::tid!{ temp $(,$param)* ,$token static $(,$static_param)* impl <$lt $($tail)+ }
};
(temp $(,$param:ident)* static $(,$static_param:ident)* impl <$lt:lifetime> $($tail: tt)+ ) => {
$crate::tid!{ inner impl <$lt $(,$param)* static $( $static_param)* > $($tail)+ }
};
( impl $($tail: tt)+) => {
$crate::tid!{ temp static impl $($tail)+ }
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! before_where {
(inner { $($processed:tt)* } where $($tokens:tt)* ) => { $($processed)* };
(inner { $($processed:tt)* } $token:tt $($tokens:tt)* ) => {
$crate::before_where!(inner { $($processed)* $token } $($tokens)*)
};
(inner { $($processed:tt)* } ) => { $($processed)* };
($($tokens:tt)*) => {$crate::before_where!(inner {} $($tokens)*)};
}
#[doc(hidden)]
#[macro_export]
macro_rules! impl_block {
(
after where {}
{$($imp:tt)*}
{ $($block:tt)* }
) => {
$($imp)*
{
$($block)*
}
};
(
after where { where $($bounds:tt)* }
{$($imp:tt)*}
{ $($block:tt)* }
) => {
$($imp)*
where $($bounds)*
{
$($block)*
}
};
(
after where {$token:tt $($tokens:tt)*}
{$($imp:tt)*}
{ $($block:tt)* }
) => {
$crate::impl_block!{
after where { $($tokens)*}
{$($imp)*}
{ $($block)* }
}
};
}
tid! { impl<'a,T:'static> TidAble<'a> for &'a T }
tid! { impl<'a,T:'static> TidAble<'a> for &'a mut T }
pub use tid as type_id;
#[cfg(feature = "nightly")]
pub mod nightly;