use std::ptr::NonNull;
use super::{Managed, union_all::UnionAll, value::typed::TypedValueRet};
use crate::{
convert::ccall_types::{CCallArg, CCallReturn},
data::{
layout::valid_layout::ValidLayout,
managed::{datatype::DataType, private::ManagedPriv, value::Value},
types::{
abstract_type::{AnyType, RefTypeConstructor},
construct_type::ConstructType,
typecheck::Typecheck,
},
},
error::{CANNOT_DISPLAY_TYPE, JlrsError, JlrsResult, TypeError},
memory::{scope::LocalScopeExt, target::unrooted::Unrooted},
prelude::Target,
private::Private,
};
#[repr(C)]
union CCallRefInner<'scope, T> {
ptr_to_inline: NonNull<T>,
managed_type: Value<'scope, 'static>,
ptr_to_value: &'scope Value<'scope, 'static>,
}
#[repr(transparent)]
pub struct CCallRef<'scope, T>(CCallRefInner<'scope, T>);
impl<'scope, T> CCallRef<'scope, T>
where
T: ConstructType + ValidLayout,
{
#[inline]
pub fn as_ref(&self) -> JlrsResult<&'scope T> {
unsafe {
let unrooted = Unrooted::new();
let Some(base_type) = T::base_type(&unrooted) else {
Err(JlrsError::TypeError(TypeError::NoBaseType))?
};
if base_type.is::<DataType>() {
let base_dt = base_type.cast_unchecked::<DataType>();
if base_dt.is_inline_alloc() && T::valid_layout(base_type) {
return Ok(self.0.ptr_to_inline.as_ref());
}
} else if base_type.is::<UnionAll>() {
let base_ua = base_type.cast_unchecked::<UnionAll>();
let base_dt = base_ua.base_type();
if base_dt.is_inline_alloc() && T::valid_layout(base_type) {
return Ok(self.0.ptr_to_inline.as_ref());
}
}
Err(TypeError::IncompatibleBaseType {
base_type: base_type.display_string_or(CANNOT_DISPLAY_TYPE),
})?
}
}
#[inline]
pub fn as_ref_check_constructed<'target, Tgt>(&self, target: &Tgt) -> JlrsResult<&'scope T>
where
Tgt: Target<'target>,
{
target.with_local_scope::<_, 1>(|_, mut frame| unsafe {
let ty = T::construct_type(&mut frame);
if ty.is::<DataType>() {
let base_dt = ty.cast_unchecked::<DataType>();
if base_dt.is_inline_alloc() && T::valid_layout(ty) {
return Ok(self.0.ptr_to_inline.as_ref());
}
}
Err(TypeError::IncompatibleBaseType {
base_type: ty.display_string_or("<Cannot display type>"),
})?
})
}
}
impl<'scope, T> CCallRef<'scope, T> {
#[inline]
pub unsafe fn as_ref_unchecked(&self) -> &'scope T {
unsafe { self.0.ptr_to_inline.as_ref() }
}
#[inline]
pub unsafe fn as_ref_to_unchecked<U>(&self) -> &'scope U {
unsafe { self.0.ptr_to_inline.cast().as_ref() }
}
#[inline]
pub unsafe fn as_value_unchecked(&self) -> Value<'scope, 'static> {
unsafe { self.0.managed_type }
}
}
impl<'scope, T> CCallRef<'scope, T>
where
T: ConstructType,
{
#[inline]
pub fn as_ref_to<U: ValidLayout>(&self) -> JlrsResult<&U> {
unsafe {
let unrooted = Unrooted::new();
let Some(base_type) = T::base_type(&unrooted) else {
Err(JlrsError::TypeError(TypeError::NoBaseType))?
};
if base_type.is::<DataType>() {
let base_dt = base_type.cast_unchecked::<DataType>();
if base_dt.is_inline_alloc() && U::valid_layout(base_type) {
return Ok(self.0.ptr_to_inline.cast().as_ref());
}
} else if base_type.is::<UnionAll>() {
let base_ua = base_type.cast_unchecked::<UnionAll>();
let base_dt = base_ua.base_type();
if base_dt.is_inline_alloc() && U::valid_layout(base_type) {
return Ok(self.0.ptr_to_inline.cast().as_ref());
}
}
Err(TypeError::IncompatibleBaseType {
base_type: base_type.display_string_or("<Cannot display type>"),
})?
}
}
#[inline]
pub fn as_ref_to_check_constructed<'target, U: ValidLayout, Tgt: Target<'target>>(
&self,
target: Tgt,
) -> JlrsResult<&U> {
target.with_local_scope::<_, 1>(|_, mut frame| unsafe {
let ty = T::construct_type(&mut frame);
if ty.is::<DataType>() {
let base_dt = ty.cast_unchecked::<DataType>();
if base_dt.is_inline_alloc() && U::valid_layout(ty) {
return Ok(self.0.ptr_to_inline.cast().as_ref());
}
}
Err(TypeError::IncompatibleBaseType {
base_type: ty.display_string_or("<Cannot display type>"),
})?
})
}
}
impl<'scope, T> CCallRef<'scope, T>
where
T: ConstructType,
{
pub fn as_value(&self) -> JlrsResult<Value<'scope, 'static>> {
unsafe {
let unrooted = Unrooted::new();
let Some(base_type) = T::base_type(&unrooted) else {
Err(JlrsError::TypeError(TypeError::NoBaseType))?
};
if base_type == AnyType::base_type(&unrooted).unwrap() {
Err(TypeError::IncompatibleBaseType {
base_type: base_type.display_string_or("<Cannot display type>"),
})?
}
if base_type.is::<DataType>() {
let base_dt = base_type.cast_unchecked::<DataType>();
if !base_dt.is_concrete_type() || base_dt.mutable() {
return Ok(self.0.managed_type);
}
} else if base_type.is::<UnionAll>() {
let base_ua = base_type.cast_unchecked::<UnionAll>();
let base_dt = base_ua.base_type();
if !base_dt.is_concrete_type() || base_dt.mutable() {
return Ok(self.0.managed_type);
}
}
Err(TypeError::IncompatibleBaseType {
base_type: base_type.display_string_or("<Cannot display type>"),
})?
}
}
pub fn as_value_check_constructed<'target, Tgt: Target<'target>>(
&self,
target: &Tgt,
) -> JlrsResult<Value<'scope, 'static>> {
target.with_local_scope::<_, 1>(|_, mut frame| unsafe {
let ty = T::construct_type(&mut frame);
if ty.is::<DataType>() {
let base_dt = ty.cast_unchecked::<DataType>();
if !base_dt.is_concrete_type() || base_dt.mutable() {
return Ok(self.0.managed_type);
}
} else if ty.is::<UnionAll>() {
let base_ua = ty.cast_unchecked::<UnionAll>();
let base_dt = base_ua.base_type();
if !base_dt.is_concrete_type() || base_dt.mutable() {
return Ok(self.0.managed_type);
}
}
Err(TypeError::IncompatibleBaseType {
base_type: ty.display_string_or("<Cannot display type>"),
})?
})
}
}
impl<'scope> CCallRef<'scope, AnyType> {
#[inline]
pub fn as_value_ref(&self) -> &Value<'scope, 'static> {
unsafe { self.0.ptr_to_value }
}
}
impl<'scope, T> CCallRef<'scope, T>
where
T: Managed<'scope, 'static> + Typecheck + ConstructType,
{
#[inline]
pub fn as_managed(&self) -> JlrsResult<T> {
unsafe {
let unrooted = Unrooted::new();
let Some(base_type) = T::base_type(&unrooted) else {
Err(JlrsError::TypeError(TypeError::NoBaseType))?
};
if base_type == AnyType::base_type(&unrooted).unwrap() {
Err(TypeError::IncompatibleBaseType {
base_type: base_type.display_string_or("<Cannot display type>"),
})?
}
if base_type.is::<DataType>() {
let base_dt = base_type.cast_unchecked::<DataType>();
if base_dt.is_concrete_type() && base_dt.mutable() {
return self.0.managed_type.cast::<T>();
}
} else if base_type.is::<UnionAll>() {
let base_ua = base_type.cast_unchecked::<UnionAll>();
if base_ua.unwrap(Private) == UnionAll::namedtuple_type(&unrooted).unwrap(Private)
&& T::construct_type(unrooted).as_managed().unwrap(Private)
== base_type.unwrap(Private)
{
return self.0.managed_type.cast::<T>();
}
let base_dt = base_ua.base_type();
if base_dt.is_concrete_type() && base_dt.mutable() {
return self.0.managed_type.cast::<T>();
}
}
Err(TypeError::IncompatibleBaseType {
base_type: base_type.display_string_or("<Cannot display type>"),
})?
}
}
}
impl<'scope, T> CCallRef<'scope, T>
where
T: Managed<'scope, 'static>,
{
#[inline]
pub unsafe fn as_managed_unchecked(&self) -> T {
unsafe { self.0.managed_type.cast_unchecked::<T>() }
}
}
unsafe impl<'scope, T: ConstructType> CCallArg for CCallRef<'scope, T> {
type CCallArgType = RefTypeConstructor<T>;
type FunctionArgType = T;
}
#[repr(transparent)]
pub struct CCallRefRet<T: ConstructType>(TypedValueRet<T>);
impl<T: ConstructType> CCallRefRet<T> {
#[inline]
pub fn new(value: TypedValueRet<T>) -> Self {
CCallRefRet(value)
}
#[inline]
pub fn into_typed_value(self) -> TypedValueRet<T> {
self.0
}
}
unsafe impl<T: ConstructType> CCallReturn for CCallRefRet<T> {
type FunctionReturnType = T;
type CCallReturnType = RefTypeConstructor<T>;
type ReturnAs = Self;
#[inline]
unsafe fn return_or_throw(self) -> Self::ReturnAs {
self
}
}