use crate::{ __internal::{disp, display}, Cdef, CdefBuilder, IntoFfi, KEEP_FN, Type, TypeBuilder, TypeType, string::{DROP_BUFFER_FN, lua_buffer}, }; use std::{ffi::c_int, fmt::Display}; #[repr(C)] #[allow(non_camel_case_types)] pub enum lua_result { Err(lua_buffer), // __tag = 0 Ok(T), // __tag = 1 } unsafe impl Type for lua_result { fn name() -> impl Display { display!("result__{}", T::name()) } fn ty() -> TypeType { TypeType::Aggregate } fn cdecl(name: impl Display) -> impl Display { display!("struct {} {name}", Self::name()) } fn build(b: &mut TypeBuilder) { b.cdef::(); } } unsafe impl Cdef for lua_result { fn build(b: &mut CdefBuilder) { b.field::("__tag").inner_union(|b| { (T::ty() != TypeType::Void).then(|| b.field::("__value")); b.field::("__err"); }); } } unsafe impl IntoFfi for Result { type Into = lua_result; fn convert(self) -> Self::Into { match self { Ok(value) => lua_result::Ok(T::convert(value)), Err(err) => lua_result::Err(lua_buffer::new(err.to_string())), } } fn require_owned() -> bool { // lua_result is only used to transmit information about whether an operation succeeded or // not and is forgotten immediately after use, so there is no need for an owned result false } fn postlude(ret: &str) -> impl Display { disp(move |f| { write!(f, "if {ret}.__tag ~= 0 then ")?; match T::Into::ty() { TypeType::Void => write!(f, "{ret} = nil; ")?, // for void results, we don't have a __value TypeType::Primitive => { // can always copy primitives to stack write!(f, "{ret} = {ret}.__value; {}", T::postlude(ret))?; } TypeType::Aggregate => { let ct = T::Into::name(); if T::require_owned() { // inner value requires ownership; copy it into its own cdata and forget // result. write!(f, "{ret} = __new(__ct.{ct}, {ret}.__value); ")?; write!(f, "{}", T::postlude(ret))?; } else { // inner value is a "temporary" like a result itself and doesn't require // full ownership of itself. we just need to keep the result object alive // until its postlude completes. write!(f, "local {ret}_keep = {ret}; {ret} = {ret}.__value; ")?; write!(f, "do {}end; ", T::postlude(ret))?; write!(f, "__C.{KEEP_FN}({ret}_keep); ")?; // keep original result alive } } } write!( f, "else \ local {ret}_err = __intern({ret}.__err.__ptr, {ret}.__err.__len); \ __C.{DROP_BUFFER_FN}({ret}.__err); \ return error({ret}_err); \ end; " ) }) } }