#![allow(unsafe_code)]
use lean_rs_sys::object::{lean_box, lean_is_ctor, lean_is_scalar, lean_obj_tag, lean_unbox};
use crate::abi::structure::{alloc_ctor_with_objects, take_ctor_objects};
use crate::abi::traits::{IntoLean, TryFromLean, conversion_error};
use crate::error::LeanResult;
use crate::runtime::LeanRuntime;
use crate::runtime::obj::Obj;
impl<'lean, T> IntoLean<'lean> for Option<T>
where
T: IntoLean<'lean>,
{
fn into_lean(self, runtime: &'lean LeanRuntime) -> Obj<'lean> {
match self {
None => unsafe { Obj::from_owned_raw(runtime, lean_box(0)) },
Some(value) => alloc_ctor_with_objects(runtime, 1, [value.into_lean(runtime)]),
}
}
}
impl<'lean, T> TryFromLean<'lean> for Option<T>
where
T: TryFromLean<'lean>,
{
fn try_from_lean(obj: Obj<'lean>) -> LeanResult<Self> {
let ptr = obj.as_raw_borrowed();
if unsafe { lean_is_scalar(ptr) } {
let payload = unsafe { lean_unbox(ptr) };
return match payload {
0 => Ok(None),
other => Err(conversion_error(format!(
"expected Lean Option.none (scalar tag 0), found scalar payload {other}"
))),
};
}
if !unsafe { lean_is_ctor(ptr) } {
let found_tag = unsafe { lean_obj_tag(ptr) };
return Err(conversion_error(format!(
"expected Lean Option ctor or scalar tag, found object with tag {found_tag}"
)));
}
let tag = unsafe { lean_obj_tag(ptr) };
if tag == 1 {
let [inner] = take_ctor_objects::<1>(obj, 1, "Option::some")?;
Ok(Some(T::try_from_lean(inner)?))
} else {
Err(conversion_error(format!(
"expected Lean Option.some ctor (tag 1), found heap ctor with tag {tag}"
)))
}
}
}
impl<T> crate::abi::traits::sealed::SealedAbi for Option<T> {}
impl<'lean, T> crate::abi::traits::LeanAbi<'lean> for Option<T>
where
T: IntoLean<'lean> + TryFromLean<'lean>,
{
type CRepr = *mut lean_rs_sys::lean_object;
fn into_c(self, runtime: &'lean LeanRuntime) -> Self::CRepr {
self.into_lean(runtime).into_raw()
}
#[allow(
clippy::not_unsafe_ptr_arg_deref,
reason = "sealed trait — caller invariant documented on LeanAbi::from_c"
)]
fn from_c(c: Self::CRepr, runtime: &'lean LeanRuntime) -> LeanResult<Self> {
let obj = unsafe { Obj::from_owned_raw(runtime, c) };
Self::try_from_lean(obj)
}
}