#![allow(unsafe_code)]
use lean_rs_sys::array::{lean_alloc_array, lean_array_cptr, lean_array_set_core, lean_array_size};
use lean_rs_sys::object::{lean_is_array, lean_is_scalar, lean_obj_tag};
use lean_rs_sys::refcount::lean_inc;
use crate::abi::traits::{IntoLean, TryFromLean, conversion_error};
use crate::error::{LeanError, LeanResult};
use crate::runtime::LeanRuntime;
use crate::runtime::obj::Obj;
pub(crate) fn from_iter_exact<'lean, T, I>(runtime: &'lean LeanRuntime, iter: I) -> Obj<'lean>
where
T: IntoLean<'lean>,
I: IntoIterator<Item = T>,
I::IntoIter: ExactSizeIterator,
{
let iter = iter.into_iter();
let len = iter.len();
unsafe {
let raw = lean_alloc_array(len, len);
for (i, value) in iter.enumerate() {
lean_array_set_core(raw, i, value.into_lean(runtime).into_raw());
}
Obj::from_owned_raw(runtime, raw)
}
}
impl<'lean, T> IntoLean<'lean> for Vec<T>
where
T: IntoLean<'lean>,
{
fn into_lean(self, runtime: &'lean LeanRuntime) -> Obj<'lean> {
from_iter_exact(runtime, self)
}
}
impl<'lean, T> TryFromLean<'lean> for Vec<T>
where
T: TryFromLean<'lean>,
{
fn try_from_lean(obj: Obj<'lean>) -> LeanResult<Self> {
require_array(&obj)?;
let runtime = obj.runtime();
let ptr = obj.as_raw_borrowed();
let size = unsafe { lean_array_size(ptr) };
let slots = unsafe { lean_array_cptr(ptr) };
tracing::trace!(
target: "lean_rs",
shape = "array",
len = size,
"lean_rs.abi.decode",
);
let mut out: Self = Self::with_capacity(size);
for i in 0..size {
unsafe {
let elem_ptr = *slots.add(i);
lean_inc(elem_ptr);
let elem = Obj::from_owned_raw(runtime, elem_ptr);
out.push(T::try_from_lean(elem)?);
}
}
Ok(out)
}
}
fn require_array(obj: &Obj<'_>) -> LeanResult<()> {
let ptr = obj.as_raw_borrowed();
if unsafe { lean_is_scalar(ptr) } {
return Err(wrong_kind_scalar());
}
if unsafe { lean_is_array(ptr) } {
Ok(())
} else {
let found_tag = unsafe { lean_obj_tag(ptr) };
Err(wrong_kind_heap(found_tag))
}
}
fn wrong_kind_scalar() -> LeanError {
conversion_error("expected Lean Array, found scalar-tagged object")
}
fn wrong_kind_heap(found_tag: u32) -> LeanError {
conversion_error(format!("expected Lean Array, found object with tag {found_tag}"))
}
impl<T> crate::abi::traits::sealed::SealedAbi for Vec<T> {}
impl<'lean, T> crate::abi::traits::LeanAbi<'lean> for Vec<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)
}
}