pr47 0.0.3

A semi-experimental programming language. Still working in progress.
Documentation
use std::any::TypeId;
use std::convert::TryFrom;

use unchecked_unwrap::UncheckedUnwrap;

use crate::vm::{GcInfo, Pr47PtrNonNull, Pr47Ptr, Pr47DynBase};
use crate::error::Pr47Error;
use crate::util::type_traits::is_copy;

pub trait CastFromPr47<'a, T: 'a> {
    type CastResult = T;
    fn any_cast(ptr: &Pr47Ptr) -> Result<Self::CastResult, Pr47Error>;
}

impl<'a, T: 'a> CastFromPr47<'a, T> for Pr47Ptr<'a> {
    default fn any_cast(ptr: &Pr47Ptr) -> Result<Self::CastResult, Pr47Error> {
        Pr47PtrNonNull::try_from(ptr).map_or_else(
            |e| {
                Err(e)
            },
            |ptr| {
                <Pr47PtrNonNull as GetValueFromPtr<T>>::fetch_from_ptr(&ptr)
            }
        )
    }
}

impl<'a, T: 'a> CastFromPr47<'a, Option<T>> for Pr47Ptr<'a> {
    fn any_cast(ptr: &Pr47Ptr) -> Result<Self::CastResult, Pr47Error> {
        Pr47PtrNonNull::try_from(ptr).map_or_else(
            |_| {
                Ok(None)
            },
            |ptr| {
                Ok(Some(<Pr47PtrNonNull as GetValueFromPtr<T>>::fetch_from_ptr(&ptr)?))
            }
        )
    }
}

impl<'a, T: 'a, E: std::error::Error + 'static> CastFromPr47<'a, Result<T, E>> for Pr47Ptr<'a> {
    fn any_cast(_ptr: &Pr47Ptr) -> Result<Self::CastResult, Pr47Error> {
        unimplemented!("throwing out an Pr47 exception is not implemented yet")
    }
}

trait GetValueFromPtr<'a, T> {
    fn fetch_from_ptr(ptr: &'a Pr47PtrNonNull) -> Result<T, Pr47Error>;
}

impl<'a, T: 'a> GetValueFromPtr<'a, T> for Pr47PtrNonNull<'a> {
    default fn fetch_from_ptr(ptr: &'a Pr47PtrNonNull) -> Result<T, Pr47Error> {
        if is_copy::<T>() {
            <Pr47PtrNonNull as GetValueFromPtrCopy<T>>::fetch_from_ptr_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 GetValueFromPtrCopy<'a, T> {
    fn fetch_from_ptr_copy(ptr: &'a Pr47PtrNonNull<'a>) -> Result<T, Pr47Error>;
}

impl<'a, T: 'a> GetValueFromPtrCopy<'a, T> for Pr47PtrNonNull<'a> {
    default fn fetch_from_ptr_copy(_ptr: &'a Pr47PtrNonNull) -> Result<T, Pr47Error> {
        unreachable!("Naa-ah... wrong way.")
    }
}

impl<'a, T: 'a + Copy> GetValueFromPtrCopy<'a, T> for Pr47PtrNonNull<'a> {
    fn fetch_from_ptr_copy(ptr: &'a Pr47PtrNonNull) -> Result<T, Pr47Error> {
        unsafe {
            Ok(*ptr.data_ptr
                .as_ref()
                .cast(TypeId::of::<T>(), std::any::type_name::<T>())?
                .cast::<T>()
                .as_ref())
        }
    }
}

impl<'a, T: 'a + Pr47DynBase> GetValueFromPtr<'a, &'a T> for Pr47PtrNonNull<'a> {
    fn fetch_from_ptr(ptr: &'a Pr47PtrNonNull) -> Result<&'a T, Pr47Error> {
        unsafe {
            if ptr.gc_info_ptr.as_ref().load(std::sync::atomic::Ordering::SeqCst)
                != GcInfo::SharedWithHost.into() {
                Err(Pr47Error::NotBorrow)
            } else {
                Ok(ptr.data_ptr
                    .as_ref()
                    .cast(TypeId::of::<T>(), std::any::type_name::<T>())?
                    .cast::<T>()
                    .as_ptr()
                    .as_ref()
                    .unchecked_unwrap())
            }
        }
    }
}

impl<'a, T: 'a + Pr47DynBase> GetValueFromPtr<'a, &'a mut T> for Pr47PtrNonNull<'a> {
    fn fetch_from_ptr(ptr: &'a Pr47PtrNonNull) -> Result<&'a mut T, Pr47Error> {
        unsafe {
            if ptr.gc_info_ptr.as_ref().load(std::sync::atomic::Ordering::SeqCst)
                != GcInfo::MutSharedWithHost.into() {
                Err(Pr47Error::NotBorrow)
            } else {
                Ok(ptr.data_ptr
                    .as_ref()
                    .cast(TypeId::of::<T>(), std::any::type_name::<T>())?
                    .cast::<T>()
                    .as_ptr()
                    .as_mut()
                    .unchecked_unwrap())
            }
        }
    }
}