use std::any::TypeId;
use std::convert::TryFrom;
use crate::vm::{GcInfo, Pr47PtrNonNull, Pr47Ptr};
use crate::error::Pr47Error;
use crate::util::type_traits::is_copy;
pub trait CastFromPr47<T> {
type CastResult = T;
fn any_cast(ptr: &Pr47Ptr) -> Result<Self::CastResult, Pr47Error>;
}
impl<T: 'static> CastFromPr47<T> for Pr47Ptr {
default fn any_cast(ptr: &Pr47Ptr) -> Result<T, Pr47Error> {
<Pr47Ptr as CastFromPr47NonNull<T>>::any_cast_non_null(&Pr47PtrNonNull::try_from(ptr)?)
}
}
impl<T: 'static> CastFromPr47<Option<T>> for Pr47Ptr {
fn any_cast(ptr: &Pr47Ptr) -> Result<Option<T>, Pr47Error> {
Pr47PtrNonNull::try_from(ptr).map_or(
Ok(None),
|ptr_non_null| {
Ok(Some(<Pr47Ptr as CastFromPr47NonNull<T>>::any_cast_non_null(&ptr_non_null)?))
})
}
}
pub trait CastFromPr47NonNull<T> {
type CastResult = T;
fn any_cast_non_null(ptr: &Pr47PtrNonNull) -> Result<Self::CastResult, Pr47Error>;
}
impl<T: 'static> CastFromPr47NonNull<T> for Pr47Ptr {
default fn any_cast_non_null(ptr: &Pr47PtrNonNull) -> Result<Self::CastResult, Pr47Error> {
if is_copy::<T>() {
<Self as CastHelperCopy<Self::CastResult>>::cast_copy(ptr)
} else {
unsafe {
if ptr.gc_info_ptr.as_ref().load(std::sync::atomic::Ordering::SeqCst)
!= GcInfo::MovedToHost.into() {
Err(Pr47Error::NotMove)
} else {
Ok(
*Box::from_raw(
ptr.data_ptr.as_ref()
.cast(TypeId::of::<T>(), std::any::type_name::<T>())
.unwrap()
.cast::<T>()
.as_ptr()))
}
}
}
}
}
trait CastHelperCopy<T> {
type CastResult = T;
fn cast_copy(ptr: &Pr47PtrNonNull) -> Result<Self::CastResult, Pr47Error>;
}
impl<T: 'static> CastHelperCopy<T> for Pr47Ptr {
default fn cast_copy(ptr: &Pr47PtrNonNull) -> Result<Self::CastResult, Pr47Error> {
unreachable!()
}
}
impl<T: 'static + Copy> CastHelperCopy<T> for Pr47Ptr {
fn cast_copy(ptr: &Pr47PtrNonNull) -> Result<Self::CastResult, Pr47Error> {
unsafe {
Ok(*ptr.data_ptr
.as_ref()
.cast(TypeId::of::<T>(), std::any::type_name::<T>())?
.cast::<T>()
.as_ref())
}
}
}
impl<T: 'static> CastFromPr47NonNull<&'_ T> for Pr47Ptr {
fn any_cast_non_null(reference: &Pr47PtrNonNull) -> Result<Self::CastResult, Pr47Error> {
unsafe {
if reference.gc_info_ptr.as_ref().load(std::sync::atomic::Ordering::SeqCst)
!= GcInfo::SharedWithHost.into() {
Err(Pr47Error::NotBorrow)
} else {
Ok(reference.data_ptr
.as_ref()
.cast(TypeId::of::<T>(),
std::any::type_name::<T>())?
.cast::<T>()
.as_ptr()
.as_ref()
.unwrap())
}
}
}
}
impl<T: 'static> CastFromPr47NonNull<&'_ mut T> for Pr47Ptr {
fn any_cast_non_null(reference: &Pr47PtrNonNull) -> Result<Self::CastResult, Pr47Error> {
unsafe {
if reference.gc_info_ptr.as_ref().load(std::sync::atomic::Ordering::SeqCst)
!= GcInfo::MutSharedWithHost.into() {
Err(Pr47Error::NotBorrow)
} else {
Ok(reference.data_ptr
.as_ref()
.cast(TypeId::of::<T>(),
std::any::type_name::<T>())?
.cast::<T>()
.as_ptr()
.as_mut()
.unwrap())
}
}
}
}