Add ffi crate

This commit is contained in:
2025-06-19 17:02:12 +10:00
parent 86380a0957
commit 16ab2be6f4
15 changed files with 1718 additions and 10 deletions

View File

@@ -0,0 +1,80 @@
use crate::{CDef, CDefBuilder, FromFfi, ToFfi, Type, TypeBuilder, display};
use std::{ffi::c_int, fmt::Display, ptr};
#[repr(C)]
#[allow(non_camel_case_types)]
pub enum lua_option<T> {
None, // __tag = 0
Some(T), // __tag = 1
}
unsafe impl<T: Type> Type for lua_option<T> {
fn name() -> impl Display {
display!("option__{}", T::name())
}
fn cdecl(name: impl Display) -> impl Display {
display!("struct option__{} {name}", T::name())
}
fn build(b: &mut TypeBuilder) {
b.include::<T>().cdef::<Self>();
}
}
unsafe impl<T: Type> CDef for lua_option<T> {
fn build(b: &mut CDefBuilder) {
b.field::<c_int>("__tag").field::<T>("__value");
}
}
unsafe impl<T: FromFfi> FromFfi for Option<T> {
type From = *mut Self::FromValue; // pass by-ref
type FromValue = lua_option<T::FromValue>;
const ARG_KEEPALIVE: bool = T::ARG_KEEPALIVE;
fn prelude(arg: &str) -> impl Display {
let ct = Self::FromValue::name();
display!(
"if {arg} == nil then {arg} = {ct}(); else {}{arg} = {ct}(1, {arg}); end; ",
T::prelude(arg)
)
}
fn convert(from: Self::From) -> Self {
debug_assert!(
!from.is_null(),
"Option<T>::convert() called on a null lua_option<T>"
);
Self::convert_value(unsafe { ptr::replace(from, lua_option::None) })
}
fn convert_value(from: Self::FromValue) -> Self {
match from {
lua_option::Some(value) => Some(T::convert_value(value)),
lua_option::None => None,
}
}
}
unsafe impl<T: ToFfi> ToFfi for Option<T> {
type To = lua_option<T::To>;
fn convert(self) -> Self::To {
match self {
Some(value) => lua_option::Some(value.convert()),
None => lua_option::None,
}
}
fn postlude(ret: &str) -> impl Display {
// if we don't have a value, return nil. otherwise copy out the inner value immediately,
// forget the option cdata, then call postlude on the inner value.
display!(
"if {ret}.__tag == 0 then {ret} = nil; else {ret} = {ret}.__value; {}end; ",
T::postlude(ret)
)
}
}