use std::any::TypeId;
use std::cell::Cell;
use std::cell::RefCell;
use std::cell::UnsafeCell;
use std::collections::BTreeMap;
use std::collections::HashMap;
use std::rc::Rc;
use std::sync::Arc;
pub use gazebo_derive::ProvidesStaticType;
pub unsafe trait ProvidesStaticType {
type StaticType: 'static + ?Sized;
}
unsafe impl<'a, T: ProvidesStaticType + 'a + ?Sized> AnyLifetime<'a> for T {
fn static_type_id() -> TypeId
where
Self: Sized,
{
TypeId::of::<T::StaticType>()
}
fn static_type_of(&self) -> TypeId {
TypeId::of::<T::StaticType>()
}
}
pub unsafe trait AnyLifetime<'a>: 'a {
fn static_type_id() -> TypeId
where
Self: Sized;
fn static_type_of(&self) -> TypeId;
}
impl<'a> dyn AnyLifetime<'a> {
pub fn is<T: AnyLifetime<'a>>(&self) -> bool {
self.static_type_of() == T::static_type_id()
}
pub fn downcast_ref<T: AnyLifetime<'a>>(&self) -> Option<&T> {
if self.is::<T>() {
unsafe { Some(&*(self as *const Self as *const T)) }
} else {
None
}
}
pub fn downcast_mut<T: AnyLifetime<'a>>(&mut self) -> Option<&mut T> {
if self.is::<T>() {
unsafe { Some(&mut *(self as *mut Self as *mut T)) }
} else {
None
}
}
}
macro_rules! any_lifetime {
( $t:ty ) => {
unsafe impl $crate::any::ProvidesStaticType for $t {
type StaticType = $t;
}
};
}
any_lifetime!(());
any_lifetime!(bool);
any_lifetime!(u8);
any_lifetime!(u16);
any_lifetime!(u32);
any_lifetime!(u64);
any_lifetime!(u128);
any_lifetime!(usize);
any_lifetime!(i8);
any_lifetime!(i16);
any_lifetime!(i32);
any_lifetime!(i64);
any_lifetime!(i128);
any_lifetime!(isize);
any_lifetime!(f32);
any_lifetime!(f64);
any_lifetime!(String);
any_lifetime!(str);
unsafe impl<'a, T: ProvidesStaticType + ?Sized> ProvidesStaticType for &'a T {
type StaticType = &'static T::StaticType;
}
unsafe impl<'a, T: ProvidesStaticType + ?Sized> ProvidesStaticType for &'a mut T {
type StaticType = &'static mut T::StaticType;
}
unsafe impl<T: ProvidesStaticType + ?Sized> ProvidesStaticType for *const T {
type StaticType = *const T::StaticType;
}
unsafe impl<T: ProvidesStaticType + ?Sized> ProvidesStaticType for *mut T {
type StaticType = *mut T::StaticType;
}
unsafe impl<T> ProvidesStaticType for [T]
where
T: ProvidesStaticType,
T::StaticType: Sized,
{
type StaticType = [T::StaticType];
}
unsafe impl<T: ProvidesStaticType + ?Sized> ProvidesStaticType for Box<T> {
type StaticType = Box<T::StaticType>;
}
unsafe impl<T: ProvidesStaticType + ?Sized> ProvidesStaticType for Rc<T> {
type StaticType = Rc<T::StaticType>;
}
unsafe impl<T: ProvidesStaticType + ?Sized> ProvidesStaticType for Arc<T> {
type StaticType = Arc<T::StaticType>;
}
unsafe impl<T: ProvidesStaticType> ProvidesStaticType for Cell<T> {
type StaticType = Cell<T::StaticType>;
}
unsafe impl<T: ProvidesStaticType> ProvidesStaticType for UnsafeCell<T> {
type StaticType = UnsafeCell<T::StaticType>;
}
unsafe impl<T: ProvidesStaticType> ProvidesStaticType for RefCell<T> {
type StaticType = RefCell<T::StaticType>;
}
unsafe impl<T> ProvidesStaticType for Option<T>
where
T: ProvidesStaticType,
T::StaticType: Sized,
{
type StaticType = Option<T::StaticType>;
}
unsafe impl<T, E> ProvidesStaticType for Result<T, E>
where
T: ProvidesStaticType,
T::StaticType: Sized,
E: ProvidesStaticType,
E::StaticType: Sized,
{
type StaticType = Result<T::StaticType, E::StaticType>;
}
unsafe impl<T> ProvidesStaticType for Vec<T>
where
T: ProvidesStaticType,
T::StaticType: Sized,
{
type StaticType = Vec<T::StaticType>;
}
unsafe impl<K, V> ProvidesStaticType for HashMap<K, V>
where
K: ProvidesStaticType,
K::StaticType: Sized,
V: ProvidesStaticType,
V::StaticType: Sized,
{
type StaticType = HashMap<K::StaticType, V::StaticType>;
}
unsafe impl<K, V> ProvidesStaticType for BTreeMap<K, V>
where
K: ProvidesStaticType,
K::StaticType: Sized,
V: ProvidesStaticType,
V::StaticType: Sized,
{
type StaticType = BTreeMap<K::StaticType, V::StaticType>;
}
#[cfg(test)]
mod tests {
use std::fmt::Display;
use super::*;
#[allow(unused_imports)] use crate as gazebo;
#[test]
fn test_can_convert() {
#[derive(Debug, PartialEq, ProvidesStaticType)]
struct Value<'a>(&'a str);
#[derive(ProvidesStaticType)]
struct Value2<'a>(&'a str);
fn convert_value<'a>(x: &'a Value<'a>) -> Option<&'a Value<'a>> {
<dyn AnyLifetime>::downcast_ref(x)
}
fn convert_any<'p, 'a>(x: &'p dyn AnyLifetime<'a>) -> Option<&'p Value<'a>> {
x.downcast_ref()
}
let v = Value("test");
let v2 = Value2("test");
assert_eq!(convert_value(&v), Some(&v));
assert_eq!(convert_any(&v), Some(&v));
assert_eq!(convert_any(&v2), None);
}
#[test]
fn test_any_lifetime() {
fn test<'a, A: AnyLifetime<'a>>(expected: TypeId) {
assert_eq!(expected, A::static_type_id());
}
test::<&str>(TypeId::of::<&str>());
test::<&String>(TypeId::of::<&String>());
test::<Box<str>>(TypeId::of::<Box<str>>());
}
#[test]
fn test_provides_static_type_id() {
fn test<'a, A: AnyLifetime<'a>>(expected: TypeId) {
assert_eq!(expected, A::static_type_id());
}
#[derive(ProvidesStaticType)]
struct Aaa;
test::<Aaa>(TypeId::of::<Aaa>());
#[derive(ProvidesStaticType)]
struct Bbb<'a>(&'a str);
test::<Bbb>(TypeId::of::<Bbb<'static>>());
#[derive(ProvidesStaticType)]
struct Bbb2<'a, 'b>(&'a str, &'b str);
test::<Bbb2>(TypeId::of::<Bbb2<'static, 'static>>());
#[derive(ProvidesStaticType)]
struct Ccc<X>(X);
test::<Ccc<String>>(TypeId::of::<Ccc<String>>());
#[derive(ProvidesStaticType)]
struct LifetimeTypeConst<'a, T, const N: usize>([&'a T; N]);
test::<LifetimeTypeConst<i32, 3>>(TypeId::of::<LifetimeTypeConst<'static, i32, 3>>());
#[derive(ProvidesStaticType)]
struct TypeWithConstraint<T: Display>(T);
test::<TypeWithConstraint<String>>(TypeId::of::<TypeWithConstraint<String>>());
struct TypeWhichDoesNotImplementAnyLifetime;
#[derive(ProvidesStaticType)]
struct TypeWithStaticLifetime<T: 'static>(T);
test::<TypeWithStaticLifetime<TypeWhichDoesNotImplementAnyLifetime>>(TypeId::of::<
TypeWithStaticLifetime<TypeWhichDoesNotImplementAnyLifetime>,
>());
}
#[test]
fn test_provides_static_type_when_type_parameter_has_bound_with_lifetime() {
trait My<'a> {}
#[derive(ProvidesStaticType)]
struct FooBar<'x, P: My<'x>>(&'x P);
}
}