95 lines
3.2 KiB
Rust
95 lines
3.2 KiB
Rust
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<T> {
|
|
Err(lua_buffer), // __tag = 0
|
|
Ok(T), // __tag = 1
|
|
}
|
|
|
|
unsafe impl<T: Type> Type for lua_result<T> {
|
|
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::<Self>();
|
|
}
|
|
}
|
|
|
|
unsafe impl<T: Type> Cdef for lua_result<T> {
|
|
fn build(b: &mut CdefBuilder) {
|
|
b.field::<c_int>("__tag").inner_union(|b| {
|
|
(T::ty() != TypeType::Void).then(|| b.field::<T>("__value"));
|
|
b.field::<lua_buffer>("__err");
|
|
});
|
|
}
|
|
}
|
|
|
|
unsafe impl<T: IntoFfi, E: Display> IntoFfi for Result<T, E> {
|
|
type Into = lua_result<T::Into>;
|
|
|
|
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; "
|
|
)
|
|
})
|
|
}
|
|
}
|