use std::alloc::Layout;
use std::collections::HashMap;
use std::collections::HashSet;
use std::marker::PhantomData;
pub use gazebo_derive::Coerce;
use crate::cast::transmute_unchecked;
pub unsafe trait Coerce<To: ?Sized> {}
pub unsafe trait CoerceKey<To: ?Sized>: Coerce<To> {}
unsafe impl<'a, From: ?Sized, To: ?Sized> Coerce<&'a To> for &'a From where From: Coerce<To> {}
unsafe impl<'a, From: ?Sized, To: ?Sized> CoerceKey<&'a To> for &'a From where From: CoerceKey<To> {}
unsafe impl<From, To> Coerce<[To]> for [From] where From: Coerce<To> {}
unsafe impl<From, To> CoerceKey<[To]> for [From] where From: CoerceKey<To> {}
unsafe impl<From, To> Coerce<Vec<To>> for Vec<From> where From: Coerce<To> {}
unsafe impl<From, To> CoerceKey<Vec<To>> for Vec<From> where From: CoerceKey<To> {}
unsafe impl<From: ?Sized, To: ?Sized> CoerceKey<Box<To>> for Box<From> where From: CoerceKey<To> {}
unsafe impl<From: ?Sized, To: ?Sized> Coerce<Box<To>> for Box<From> where From: Coerce<To> {}
unsafe impl<From, To> Coerce<HashSet<To>> for HashSet<From> where From: CoerceKey<To> {}
unsafe impl<FromK, FromV, ToK, ToV> Coerce<HashMap<ToK, ToV>> for HashMap<FromK, FromV>
where
FromK: CoerceKey<ToK>,
FromV: Coerce<ToV>,
{
}
unsafe impl<From1: Coerce<To1>, To1> Coerce<(To1,)> for (From1,) {}
unsafe impl<From1: CoerceKey<To1>, To1> CoerceKey<(To1,)> for (From1,) {}
unsafe impl<From1: Coerce<To1>, From2: Coerce<To2>, To1, To2> Coerce<(To1, To2)>
for (From1, From2)
{
}
unsafe impl<From1: CoerceKey<To1>, From2: CoerceKey<To2>, To1, To2> CoerceKey<(To1, To2)>
for (From1, From2)
{
}
unsafe impl<From: Coerce<To>, To, const N: usize> Coerce<[To; N]> for [From; N] {}
unsafe impl<From: CoerceKey<To>, To, const N: usize> CoerceKey<[To; N]> for [From; N] {}
unsafe impl<From, To> Coerce<PhantomData<To>> for PhantomData<From> {}
unsafe impl Coerce<String> for String {}
unsafe impl CoerceKey<String> for String {}
unsafe impl Coerce<str> for str {}
unsafe impl CoerceKey<str> for str {}
unsafe impl Coerce<()> for () {}
unsafe impl CoerceKey<()> for () {}
#[inline]
pub fn coerce<From, To>(x: From) -> To
where
From: Coerce<To>,
{
assert_eq!(Layout::new::<From>(), Layout::new::<To>());
unsafe { transmute_unchecked(x) }
}
#[cfg(test)]
mod tests {
use std::marker;
use super::*;
use crate as gazebo;
#[test]
fn test_ptr_coerce() {
fn f<'v>(x: (&'static str,)) -> (&'v str,) {
coerce(x)
}
let x = "test".to_owned();
assert_eq!(f(("test",)), (x.as_str(),))
}
#[test]
fn test_coerce_lifetime() {
#[derive(Coerce)]
#[repr(transparent)]
struct NewtypeWithLifetime<'v>(&'v [usize]);
let newtype = NewtypeWithLifetime(&[1, 2]);
assert_eq!(&[1, 2], coerce(newtype))
}
#[test]
fn test_coerce_type_and_lifetime_params() {
#[derive(Coerce)]
#[repr(C)]
struct Aaa<'a>(&'a u32);
#[derive(Coerce)]
#[repr(C)]
struct Bbb<'a>(&'a u32);
unsafe impl<'a> Coerce<Bbb<'a>> for Aaa<'a> {}
#[derive(Coerce)]
#[repr(C)]
struct StructWithLifetimeAndTypeParams<'a, X> {
x: X,
_marker: marker::PhantomData<&'a u32>,
}
let ten = 10;
let old = StructWithLifetimeAndTypeParams::<Aaa> {
x: Aaa(&ten),
_marker: marker::PhantomData,
};
let new: StructWithLifetimeAndTypeParams<Bbb> = coerce(old);
assert_eq!(10, *new.x.0);
}
}