Add ffi crate
This commit is contained in:
80
crates/luaffi/src/option.rs
Normal file
80
crates/luaffi/src/option.rs
Normal 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)
|
||||
)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user