use crate::ty::CommonTypeInfo;
use crate::{Error, Reflected, Type};
use core::marker::PhantomData;
use core::ptr::NonNull;
use core::{fmt, mem};
use craft_eraser::{ErasedBox, ErasedNonNull};
#[derive(Debug)]
enum ValueKind {
Owned(ErasedBox),
Borrowed(ErasedNonNull),
Moved,
}
pub unsafe trait NotOutlives<'no> {}
#[derive(Debug)]
pub struct Value<'a> {
value: ValueKind,
ty: Type,
_phantom: PhantomData<&'a ()>,
}
#[allow(clippy::should_implement_trait)]
impl<'a> Value<'a> {
pub unsafe fn from_ptr_owned<T: ?Sized + Reflected + 'a>(val: NonNull<T>) -> Value<'a> {
Value {
value: ValueKind::Owned(ErasedBox::from_raw(val)),
ty: Type::from::<T>(),
_phantom: PhantomData,
}
}
pub fn from_ref<T: ?Sized + Reflected>(val: &T) -> Value<'_> {
Value {
value: ValueKind::Borrowed(ErasedNonNull::from(val)),
ty: Type::from::<T>(),
_phantom: PhantomData,
}
}
pub fn raw_meta(&self) -> NonNull<()> {
match &self.value {
ValueKind::Owned(b) => b.raw_meta_ptr(),
ValueKind::Borrowed(p) => p.raw_meta_ptr(),
ValueKind::Moved => unreachable!(),
}
}
pub fn raw_ptr(&self) -> NonNull<()> {
match &self.value {
ValueKind::Owned(b) => b.raw_ptr(),
ValueKind::Borrowed(p) => p.raw_ptr(),
ValueKind::Moved => unreachable!(),
}
}
pub fn ty(&self) -> Type {
self.ty
}
pub unsafe fn try_cast_unsafe<T: Reflected>(mut self) -> Result<T, (Self, Error)> {
let value = mem::replace(&mut self.value, ValueKind::Moved);
if let ValueKind::Owned(b) = value {
if Type::from::<T>() == self.ty {
Ok(*b.reify_box::<T>())
} else {
self.value = ValueKind::Owned(b);
let ty = self.ty;
Err((self, Error::wrong_type(Type::from::<T>(), ty)))
}
} else {
self.value = value;
Err((self, Error::BorrowedValue))
}
}
pub unsafe fn cast_unsafe<T: Reflected>(self) -> T {
self.try_cast_unsafe()
.unwrap_or_else(|_| panic!("Couldn't cast Value into type {}", T::name()))
}
pub fn try_cast<T: Reflected + NotOutlives<'a>>(self) -> Result<T, (Self, Error)> {
unsafe { self.try_cast_unsafe() }
}
pub fn cast<T: Reflected + NotOutlives<'a>>(self) -> T {
self.try_cast()
.unwrap_or_else(|_| panic!("Couldn't cast Value into type {}", T::name()))
}
pub unsafe fn try_borrow_unsafe<T: ?Sized + Reflected>(&self) -> Result<&T, Error> {
if Type::from::<T>() == self.ty() {
let ptr =
NonNull::<T>::from_raw_parts(self.raw_ptr(), *self.raw_meta().cast().as_ref());
Ok(ptr.as_ref())
} else {
Err(Error::wrong_type(Type::from::<T>(), self.ty()))
}
}
pub unsafe fn borrow_unsafe<T: ?Sized + Reflected>(&self) -> &T {
self.try_borrow_unsafe()
.unwrap_or_else(|_| panic!("Couldn't borrow Value as type {}", T::name()))
}
pub fn try_borrow<'b, T: ?Sized + Reflected + NotOutlives<'a>>(
&'b self,
) -> Result<&'b T, Error> {
unsafe { self.try_borrow_unsafe() }
}
pub fn borrow<'b, T: ?Sized + Reflected + NotOutlives<'a>>(&'b self) -> &'b T {
self.try_borrow()
.unwrap_or_else(|_| panic!("Couldn't borrow Value as type {}", T::name()))
}
pub unsafe fn try_borrow_unsafe_mut<T: ?Sized + Reflected>(&mut self) -> Result<&mut T, Error> {
if Type::from::<T>() == self.ty() {
let mut ptr =
NonNull::<T>::from_raw_parts(self.raw_ptr(), *self.raw_meta().cast().as_ref());
Ok(ptr.as_mut())
} else {
Err(Error::wrong_type(Type::from::<T>(), self.ty()))
}
}
pub unsafe fn borrow_unsafe_mut<T: ?Sized + Reflected>(&mut self) -> &mut T {
self.try_borrow_unsafe_mut()
.unwrap_or_else(|_| panic!("Couldn't mutably borrow Value as type {}", T::name()))
}
pub fn try_borrow_mut<'b, T: ?Sized + Reflected + NotOutlives<'a>>(
&'b mut self,
) -> Result<&'b mut T, Error> {
unsafe { self.try_borrow_unsafe_mut() }
}
pub fn borrow_mut<'b, T: ?Sized + Reflected + NotOutlives<'a>>(&'b mut self) -> &'b mut T {
self.try_borrow_mut()
.unwrap_or_else(|_| panic!("Couldn't mutably borrow Value as type {}", T::name()))
}
pub fn as_ref(&self) -> Result<Value<'_>, Error> {
self.ty.as_ref(self)
}
pub fn as_mut(&mut self) -> Result<Value<'_>, Error> {
let ty = self.ty;
ty.as_mut(self)
}
}
impl<'a> fmt::Pointer for Value<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Pointer::fmt(&self.raw_ptr(), f)
}
}
impl<'a, T: Reflected + 'a> From<T> for Value<'a> {
default fn from(val: T) -> Value<'a> {
Value {
value: ValueKind::Owned(ErasedBox::new(val)),
ty: Type::from::<T>(),
_phantom: PhantomData,
}
}
}