Refactor luaffi proc-macro

This commit is contained in:
2025-06-24 19:30:39 +10:00
parent f8e7b8ae62
commit 3dd375b071
6 changed files with 740 additions and 522 deletions

View File

@@ -1,6 +1,6 @@
use crate::{
__internal::{display, type_id},
Cdef, CdefBuilder, FfiReturnConvention, IntoFfi, Metatype, MetatypeBuilder, Type, TypeBuilder,
Cdef, CdefBuilder, IntoFfi, Metatype, MetatypeBuilder, Type, TypeBuilder, TypeType,
UnsafeExternCFn,
};
use luaify::luaify;
@@ -43,7 +43,7 @@ pub struct lua_future<F: Future<Output: IntoFfi>> {
sig: Signature,
poll: fn(Pin<&mut Self>, cx: &mut Context) -> Poll<()>,
state: State<F>,
take: unsafe extern "C" fn(&mut Self) -> <F::Output as IntoFfi>::To,
take: unsafe extern "C" fn(&mut Self) -> <F::Output as IntoFfi>::Into,
drop: unsafe extern "C" fn(&mut Self),
}
@@ -94,7 +94,7 @@ impl<F: Future<Output: IntoFfi>> lua_future<F> {
}
}
unsafe extern "C" fn take(&mut self) -> <F::Output as IntoFfi>::To {
unsafe extern "C" fn take(&mut self) -> <F::Output as IntoFfi>::Into {
// `fut:__take()` returns the fulfilled value by-value (not by out-param) because if we
// preallocate a cdata for the out-param and the thread for some reason gets dropped and
// never resumed, the GC could call the destructor on an uninitialised cdata.
@@ -136,8 +136,12 @@ unsafe impl<F: Future<Output: IntoFfi> + 'static> Type for lua_future<F> {
display!("future__{:x}", type_id::<F>())
}
fn ty() -> TypeType {
TypeType::Aggregate
}
fn cdecl(name: impl Display) -> impl Display {
display!("struct future__{:x} {name}", type_id::<F>())
display!("struct {} {name}", Self::name())
}
fn build(s: &mut TypeBuilder) {
@@ -148,7 +152,7 @@ unsafe impl<F: Future<Output: IntoFfi> + 'static> Type for lua_future<F> {
unsafe impl<F: Future<Output: IntoFfi> + 'static> Cdef for lua_future<F> {
fn build(s: &mut CdefBuilder) {
s.field_opaque(mem::offset_of!(Self, take)) // opaque .sig, .poll and .state
.field::<UnsafeExternCFn<(&mut Self,), <F::Output as IntoFfi>::To>>("__take")
.field::<UnsafeExternCFn<(&mut Self,), <F::Output as IntoFfi>::Into>>("__take")
.field::<UnsafeExternCFn<(&mut Self,), ()>>("__drop");
}
}
@@ -162,24 +166,25 @@ unsafe impl<F: Future<Output: IntoFfi> + 'static> Metatype for lua_future<F> {
}
unsafe impl<F: Future<Output: IntoFfi> + 'static> IntoFfi for lua_future<F> {
type To = lua_future<F>;
type Into = lua_future<F>;
fn convert(self) -> Self::To {
fn convert(self) -> Self::Into {
self
}
fn postlude(ret: &str, _conv: FfiReturnConvention) -> impl Display {
fn postlude(ret: &str) -> impl Display {
// When returning a future from Rust to Lua, yield it immediately to the runtime which will
// poll it to completion in the background, then take the fulfilled value once the thread
// gets resumed. Lua user code should never to worry about awaiting futures.
//
// Once the current thread gets resumed and we take the future's fulfilled value, we clear
// the finaliser on the future and forget it (there is nothing to call drop on).
// the finaliser on the future and forget it (there is nothing to drop once the value is
// taken).
//
// `coroutine.yield` is cached as `yield` and `ffi.gc` as `gc` in locals (see lib.rs)
// `coroutine.yield` is cached as `__yield` and `ffi.gc` as `__gc` in locals (see lib.rs)
display!(
"yield({ret}); {ret} = gc({ret}, nil):__take(); {}",
<F::Output as IntoFfi>::postlude(ret, FfiReturnConvention::ByValue)
"__yield({ret}); {ret} = __gc({ret}, nil):__take(); {}",
<F::Output as IntoFfi>::postlude(ret)
)
}
}

View File

@@ -25,7 +25,7 @@ pub mod result;
// `ffi.keep(obj)`.
//
// https://github.com/LuaJIT/LuaJIT/issues/1167
pub const KEEP_FN: &str = "luaffi_keep";
pub(crate) const KEEP_FN: &str = "luaffi_keep";
#[unsafe(export_name = "luaffi_keep")]
extern "C" fn __keep(_ptr: *const c_void) {}
export![__keep];
@@ -151,6 +151,7 @@ impl Registry {
}
pub fn declare<T: Type>(&mut self, name: impl Display) -> &mut Self {
assert!(T::ty() != TypeType::Void, "cannot declare void type");
self.include::<T>()
.funcs
.insert(name.to_string())
@@ -159,6 +160,7 @@ impl Registry {
}
pub fn preload<T: Type>(&mut self, name: impl Display) -> &mut Self {
assert!(T::ty() != TypeType::Void, "cannot declare void type");
self.include::<T>();
let ct = T::name();
writeln!(
@@ -189,6 +191,8 @@ impl Display for Registry {
pub unsafe trait Type {
fn name() -> impl Display;
fn ty() -> TypeType;
fn cdecl(name: impl Display) -> impl Display;
fn extern_cdecl(name: impl Display) -> impl Display {
Self::cdecl(name)
@@ -197,6 +201,13 @@ pub unsafe trait Type {
fn build(b: &mut TypeBuilder);
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum TypeType {
Void,
Primitive,
Aggregate,
}
#[derive(Debug)]
pub struct TypeBuilder<'r> {
registry: &'r mut Registry,
@@ -253,6 +264,7 @@ impl<'r> CdefBuilder<'r> {
}
pub fn field<T: Type>(&mut self, name: impl Display) -> &mut Self {
assert!(T::ty() != TypeType::Void, "cannot declare void field");
self.registry.include::<T>();
self.field_raw(T::cdecl(name))
}
@@ -390,21 +402,13 @@ pub unsafe trait FromFfi: Sized {
}
pub unsafe trait IntoFfi: Sized {
type To: Type + Sized;
type Into: Type + Sized;
fn postlude(_ret: &str, _conv: FfiReturnConvention) -> impl Display {
fn postlude(_ret: &str) -> impl Display {
""
}
fn convert(self) -> Self::To;
}
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
pub enum FfiReturnConvention {
Void,
#[default]
ByValue,
ByOutParam,
fn convert(self) -> Self::Into;
}
#[derive(Debug)]
@@ -428,6 +432,11 @@ impl<'r, 'm> MetatypeMethodBuilder<'r, 'm> {
}
pub fn param<T: FromFfi>(&mut self, name: impl Display) -> &mut Self {
assert!(
T::From::ty() != TypeType::Void,
"cannot declare void parameter"
);
(!self.params.is_empty()).then(|| self.params.push_str(", "));
(!self.args.is_empty()).then(|| self.args.push_str(", "));
write!(self.params, "{name}").unwrap();
@@ -468,7 +477,7 @@ impl<'r, 'm> MetatypeMethodBuilder<'r, 'm> {
self
}
pub fn call<T: IntoFfi>(&mut self, func: impl Display, ret: FfiReturnConvention) {
pub fn call<T: IntoFfi>(&mut self, func: impl Display) {
let Self {
metatype,
params,
@@ -480,21 +489,21 @@ impl<'r, 'm> MetatypeMethodBuilder<'r, 'm> {
let lua = &mut metatype.lua;
write!(lua, "function({params}) {prelude}").unwrap();
match ret {
FfiReturnConvention::Void => {
match T::Into::ty() {
TypeType::Void => {
write!(lua, "__C.{func}({args}); {postlude}end").unwrap();
}
FfiReturnConvention::ByValue => {
let check = T::postlude("__res", ret);
TypeType::Primitive => {
let check = T::postlude("__res");
write!(
lua,
"local __res = __C.{func}({args}); {check}{postlude}return __res; end"
)
.unwrap();
}
FfiReturnConvention::ByOutParam => {
let ct = T::To::name();
let check = T::postlude("__res", ret);
TypeType::Aggregate => {
let ct = T::Into::name();
let check = T::postlude("__res");
write!(lua, "local __res = __new(__ct.{ct}); __C.{func}(__res").unwrap();
if !args.is_empty() {
write!(lua, ", {args}").unwrap();
@@ -505,15 +514,51 @@ impl<'r, 'm> MetatypeMethodBuilder<'r, 'm> {
}
}
macro_rules! impl_primitive {
($rtype:ty, $ctype:expr) => {
unsafe impl Type for $rtype {
//
// SAFETY: Unit type return maps to a C void return, which is a nil return in lua. There is no
// equivalent to passing a unit type as an argument in C.
//
macro_rules! impl_void {
($rty:ty) => {
unsafe impl Type for $rty {
fn name() -> impl Display {
$ctype
"void"
}
fn ty() -> TypeType {
TypeType::Void
}
fn cdecl(name: impl Display) -> impl Display {
display!("{} {name}", $ctype)
display!("void {name}")
}
fn build(_b: &mut TypeBuilder) {}
}
unsafe impl IntoFfi for $rty {
type Into = ();
fn convert(self) -> Self::Into {}
}
};
}
impl_void!(());
impl_void!(c_void);
macro_rules! impl_primitive {
($rty:ty, $cty:expr) => {
unsafe impl Type for $rty {
fn name() -> impl Display {
$cty
}
fn ty() -> TypeType {
TypeType::Primitive
}
fn cdecl(name: impl Display) -> impl Display {
display!("{} {name}", $cty)
}
fn build(_b: &mut TypeBuilder) {}
@@ -521,8 +566,6 @@ macro_rules! impl_primitive {
};
}
impl_primitive!((), "void");
impl_primitive!(c_void, "void");
impl_primitive!(bool, "bool");
impl_primitive!(u8, "uint8_t");
impl_primitive!(u16, "uint16_t");
@@ -537,24 +580,6 @@ impl_primitive!(isize, "intptr_t");
impl_primitive!(c_float, "float");
impl_primitive!(c_double, "double");
unsafe impl IntoFfi for () {
//
// SAFETY: Unit type return maps to a C void return, which is a nil return in lua. There is no
// equivalent to passing a unit type as an argument in C. `c_void` cannot be returned from rust
// so it should return the unit type instead.
//
type To = ();
fn convert(self) -> Self::To {}
fn postlude(_ret: &str, conv: FfiReturnConvention) -> impl Display {
assert!(
conv == FfiReturnConvention::Void,
"void type cannot be instantiated"
);
""
}
}
unsafe impl FromFfi for bool {
type From = bool;
@@ -570,24 +595,16 @@ unsafe impl FromFfi for bool {
}
unsafe impl IntoFfi for bool {
type To = bool;
type Into = bool;
fn convert(self) -> Self::To {
fn convert(self) -> Self::Into {
self
}
fn postlude(ret: &str, conv: FfiReturnConvention) -> impl Display {
disp(move |f| match conv {
FfiReturnConvention::Void => unreachable!(),
FfiReturnConvention::ByValue => Ok(()),
FfiReturnConvention::ByOutParam => write!(f, "{ret} = {ret} ~= 0; "),
})
}
}
macro_rules! impl_number_fromabi {
($rtype:ty) => {
unsafe impl FromFfi for $rtype {
($rty:ty) => {
unsafe impl FromFfi for $rty {
type From = Self;
fn prelude(arg: &str) -> impl Display {
@@ -615,21 +632,13 @@ impl_number_fromabi!(f32);
impl_number_fromabi!(f64);
macro_rules! impl_number_intoabi {
($rtype:ty) => {
unsafe impl IntoFfi for $rtype {
type To = Self;
($rty:ty) => {
unsafe impl IntoFfi for $rty {
type Into = Self;
fn convert(self) -> Self::To {
fn convert(self) -> Self::Into {
self
}
fn postlude(ret: &str, conv: FfiReturnConvention) -> impl Display {
disp(move |f| match conv {
FfiReturnConvention::Void => unreachable!(),
FfiReturnConvention::ByValue => Ok(()),
FfiReturnConvention::ByOutParam => write!(f, "{ret} = tonumber({ret}); "),
})
}
}
};
}
@@ -648,21 +657,20 @@ impl_number_intoabi!(c_float);
impl_number_intoabi!(c_double);
macro_rules! impl_bigint_intoabi {
($rtype:ty) => {
unsafe impl IntoFfi for $rtype {
type To = Self;
($rty:ty) => {
unsafe impl IntoFfi for $rty {
type Into = Self;
fn convert(self) -> Self::To {
fn convert(self) -> Self::Into {
self
}
fn postlude(ret: &str, conv: FfiReturnConvention) -> impl Display {
disp(move |f| match conv {
FfiReturnConvention::Void => unreachable!(),
FfiReturnConvention::ByValue | FfiReturnConvention::ByOutParam => {
write!(f, "{ret} = tonumber({ret}); ")
}
})
fn postlude(ret: &str) -> impl Display {
// this isn't "correct" per se, but it's much more ergonomic to work with numbers in
// lua than with long longs wrapped in cdata. we gracefully accept the loss of
// precision here and that 53 bits of precision for big integers are enough. (the
// vain of Lua 5.3 integer subtype ;D )
display!("{ret} = tonumber({ret}); ")
}
}
};
@@ -682,6 +690,10 @@ macro_rules! impl_const_ptr {
display!("const_{}_ptr", T::name())
}
fn ty() -> TypeType {
TypeType::Primitive
}
fn cdecl(name: impl Display) -> impl Display {
T::cdecl(display!("const *{name}"))
}
@@ -704,6 +716,10 @@ macro_rules! impl_mut_ptr {
display!("{}_ptr", T::name())
}
fn ty() -> TypeType {
TypeType::Primitive
}
fn cdecl(name: impl Display) -> impl Display {
T::cdecl(display!("*{name}"))
}
@@ -753,13 +769,13 @@ impl_ptr_fromabi!(Option<&mut T>);
macro_rules! impl_ptr_intoabi {
($ty:ty) => {
unsafe impl<T: Type> IntoFfi for $ty {
type To = Self;
type Into = Self;
fn convert(self) -> Self::To {
fn convert(self) -> Self::Into {
self
}
fn postlude(ret: &str, _conv: FfiReturnConvention) -> impl Display {
fn postlude(ret: &str) -> impl Display {
display!("if {ret} == nil then {ret} = nil; end; ")
}
}
@@ -822,9 +838,9 @@ impl_ref_fromabi!(&'s mut T);
macro_rules! impl_ref_intoabi {
($ty:ty) => {
unsafe impl<T: Type> IntoFfi for $ty {
type To = Self;
type Into = Self;
fn convert(self) -> Self::To {
fn convert(self) -> Self::Into {
self
}
}
@@ -845,6 +861,10 @@ unsafe impl<T: Type> Type for [T] {
display!("{}_arr", T::name())
}
fn ty() -> TypeType {
TypeType::Aggregate
}
fn cdecl(name: impl Display) -> impl Display {
display!("{name}[]")
}
@@ -859,6 +879,10 @@ unsafe impl<T: Type, const N: usize> Type for [T; N] {
display!("{}_arr{N}", T::name())
}
fn ty() -> TypeType {
TypeType::Aggregate
}
fn cdecl(name: impl Display) -> impl Display {
display!("{name}[{N}]")
}
@@ -891,6 +915,10 @@ macro_rules! impl_function {
}))
}
fn ty() -> TypeType {
TypeType::Primitive
}
fn cdecl(name: impl Display) -> impl Display {
$ret::cdecl(disp(move |f| Ok({
let mut _n = 0;

View File

@@ -1,13 +1,13 @@
use crate::{
__internal::{disp, display, export},
FfiReturnConvention, FromFfi, IntoFfi,
FromFfi, IntoFfi,
};
use bstr::{BStr, BString};
use luaffi_impl::{cdef, metatype};
use std::{fmt::Display, mem::ManuallyDrop, ptr, slice};
pub const IS_UTF8_FN: &str = "luaffi_is_utf8";
pub const DROP_BUFFER_FN: &str = "luaffi_drop_buffer";
pub(crate) const IS_UTF8_FN: &str = "luaffi_is_utf8";
pub(crate) const DROP_BUFFER_FN: &str = "luaffi_drop_buffer";
#[unsafe(export_name = "luaffi_is_utf8")]
unsafe extern "C" fn __is_utf8(ptr: *const u8, len: usize) -> bool {
@@ -33,7 +33,16 @@ pub struct lua_buf {
#[metatype]
impl lua_buf {
fn null() -> Self {
// this takes a slice and decomposes it into its raw parts. caller should ensure the result is
// used only as long as the original buffer is still alive.
pub(crate) fn new(s: &[u8]) -> Self {
Self {
__ptr: s.as_ptr(),
__len: s.len(),
}
}
pub(crate) fn null() -> Self {
Self {
__ptr: ptr::null(),
__len: 0,
@@ -41,6 +50,36 @@ impl lua_buf {
}
}
#[derive(Debug, Clone, Copy)]
#[cdef]
pub struct lua_buffer {
__ptr: *mut u8,
__len: usize,
__cap: usize,
}
#[metatype]
impl lua_buffer {
// this takes ownership of the Vec and decomposes it into its raw parts. the result must be
// dropped by `__drop_buffer` (see [`DROP_BUFFER_FN`]).
pub(crate) fn new(s: impl Into<Vec<u8>>) -> Self {
let s = s.into();
Self {
__cap: s.capacity(),
__len: s.len(),
__ptr: ManuallyDrop::new(s).as_mut_ptr(),
}
}
pub(crate) fn null() -> Self {
Self {
__ptr: ptr::null_mut(),
__len: 0,
__cap: 0,
}
}
}
unsafe impl<'s> FromFfi for &'s [u8] {
type From = Option<&'s lua_buf>;
@@ -55,6 +94,8 @@ unsafe impl<'s> FromFfi for &'s [u8] {
f,
r#"assert(type({arg}) == "string", "string expected in argument '{arg}', got " .. type({arg})); "#
)?;
// SAFETY: the lua_buf is only valid for as long as the string is alive. we've ensured
// that it is alive for at least the duration of the ffi call via `require_keepalive()`.
write!(f, "{arg} = __new(__ct.lua_buf, {arg}, #{arg}); end; ")
})
}
@@ -68,22 +109,6 @@ unsafe impl<'s> FromFfi for &'s [u8] {
}
}
unsafe impl<'s> FromFfi for &'s BStr {
type From = <&'s [u8] as FromFfi>::From;
fn require_keepalive() -> bool {
<&[u8] as FromFfi>::require_keepalive()
}
fn prelude(arg: &str) -> impl Display {
<&[u8] as FromFfi>::prelude(arg)
}
fn convert(from: Self::From) -> Self {
<&[u8] as FromFfi>::convert(from).into()
}
}
unsafe impl<'s> FromFfi for &'s str {
type From = Option<&'s lua_buf>;
@@ -121,7 +146,77 @@ unsafe impl<'s> FromFfi for &'s str {
}
}
macro_rules! impl_optional_ref_from {
unsafe impl IntoFfi for &'static [u8] {
type Into = lua_buf;
fn convert(self) -> Self::Into {
// SAFETY: the slice is 'static so the resulting lua_buf is always valid
lua_buf::new(self)
}
fn postlude(ret: &str) -> impl Display {
display!("{ret} = __intern({ret}.__ptr, {ret}.__len)")
}
}
unsafe impl IntoFfi for Vec<u8> {
type Into = lua_buffer;
fn convert(self) -> Self::Into {
lua_buffer::new(self)
}
fn postlude(ret: &str) -> impl Display {
display!(
"do local __{ret} = {ret}; {ret} = __intern({ret}.__ptr, {ret}.__len); __C.{DROP_BUFFER_FN}(__{ret}); end; "
)
}
}
macro_rules! impl_from_via {
($ty:ty, $via:ty) => {
unsafe impl<'s> FromFfi for $ty {
type From = <$via as FromFfi>::From;
fn require_keepalive() -> bool {
<$via as FromFfi>::require_keepalive()
}
fn prelude(arg: &str) -> impl Display {
<$via as FromFfi>::prelude(arg)
}
fn convert(from: Self::From) -> Self {
<$via as FromFfi>::convert(from).into()
}
}
};
}
impl_from_via!(&'s BStr, &'s [u8]);
macro_rules! impl_into_via {
($ty:ty, $via:ty) => {
unsafe impl IntoFfi for $ty {
type Into = <$via as IntoFfi>::Into;
fn convert(self) -> Self::Into {
<$via as IntoFfi>::convert(self.into())
}
fn postlude(ret: &str) -> impl Display {
<$via as IntoFfi>::postlude(ret)
}
}
};
}
impl_into_via!(&'static BStr, &'static [u8]);
impl_into_via!(&'static str, &'static BStr);
impl_into_via!(BString, Vec<u8>);
impl_into_via!(String, BString);
macro_rules! impl_optional_from {
($ty:ty) => {
unsafe impl<'s> FromFfi for Option<$ty> {
type From = <$ty as FromFfi>::From;
@@ -131,7 +226,7 @@ macro_rules! impl_optional_ref_from {
}
fn prelude(arg: &str) -> impl Display {
// avoid constructing a `lua_buf` at all for nil arguments
// just pass a null pointer if argument is nil
display!(
"if {arg} ~= nil then {}end; ",
<$ty as FromFfi>::prelude(arg)
@@ -145,152 +240,32 @@ macro_rules! impl_optional_ref_from {
};
}
impl_optional_ref_from!(&'s [u8]);
impl_optional_ref_from!(&'s BStr);
impl_optional_ref_from!(&'s str);
unsafe impl IntoFfi for &'static [u8] {
type To = lua_buf;
fn convert(self) -> Self::To {
lua_buf {
__ptr: self.as_ptr(),
__len: self.len(),
}
}
fn postlude(ret: &str, _conv: FfiReturnConvention) -> impl Display {
display!("{ret} = __intern({ret}.__ptr, {ret}.__len)")
}
}
unsafe impl IntoFfi for &'static BStr {
type To = <&'static [u8] as IntoFfi>::To;
fn convert(self) -> Self::To {
<&[u8] as IntoFfi>::convert(self.as_ref())
}
fn postlude(ret: &str, conv: FfiReturnConvention) -> impl Display {
<&[u8] as IntoFfi>::postlude(ret, conv)
}
}
unsafe impl IntoFfi for &'static str {
type To = <&'static [u8] as IntoFfi>::To;
fn convert(self) -> Self::To {
<&[u8] as IntoFfi>::convert(self.as_bytes())
}
fn postlude(ret: &str, conv: FfiReturnConvention) -> impl Display {
<&[u8] as IntoFfi>::postlude(ret, conv)
}
}
macro_rules! impl_optional_ref_into {
($ty:ty) => {
unsafe impl IntoFfi for Option<$ty> {
type To = <$ty as IntoFfi>::To;
fn convert(self) -> Self::To {
self.map_or(lua_buf::null(), <$ty as IntoFfi>::convert)
}
fn postlude(ret: &str, conv: FfiReturnConvention) -> impl Display {
display!(
"if {ret}.__ptr == nil then {ret} = nil; else {}end; ",
<$ty as IntoFfi>::postlude(ret, conv)
)
}
}
};
}
impl_optional_ref_into!(&'static [u8]);
impl_optional_ref_into!(&'static BStr);
impl_optional_ref_into!(&'static str);
#[derive(Debug, Clone, Copy)]
#[cdef]
pub struct lua_buffer {
__ptr: *mut u8,
__len: usize,
__cap: usize,
}
#[metatype]
impl lua_buffer {
fn null() -> Self {
Self {
__ptr: ptr::null_mut(),
__len: 0,
__cap: 0,
}
}
}
unsafe impl IntoFfi for Vec<u8> {
type To = lua_buffer;
fn convert(self) -> Self::To {
lua_buffer {
__cap: self.capacity(),
__len: self.len(),
__ptr: ManuallyDrop::new(self).as_mut_ptr(),
}
}
fn postlude(ret: &str, _conv: FfiReturnConvention) -> impl Display {
display!(
"do local __{ret} = {ret}; {ret} = __intern({ret}.__ptr, {ret}.__len); __C.{DROP_BUFFER_FN}(__{ret}); end; "
)
}
}
unsafe impl IntoFfi for BString {
type To = <Vec<u8> as IntoFfi>::To;
fn convert(self) -> Self::To {
<Vec<u8> as IntoFfi>::convert(self.into())
}
fn postlude(ret: &str, conv: FfiReturnConvention) -> impl Display {
<Vec<u8> as IntoFfi>::postlude(ret, conv)
}
}
unsafe impl IntoFfi for String {
type To = <Vec<u8> as IntoFfi>::To;
fn convert(self) -> Self::To {
<Vec<u8> as IntoFfi>::convert(self.into_bytes())
}
fn postlude(ret: &str, conv: FfiReturnConvention) -> impl Display {
<Vec<u8> as IntoFfi>::postlude(ret, conv)
}
}
impl_optional_from!(&'s [u8]);
impl_optional_from!(&'s BStr);
impl_optional_from!(&'s str);
macro_rules! impl_optional_into {
($ty:ty) => {
($ty:ty, $null:expr) => {
unsafe impl IntoFfi for Option<$ty> {
type To = <$ty as IntoFfi>::To;
type Into = <$ty as IntoFfi>::Into;
fn convert(self) -> Self::To {
self.map_or(lua_buffer::null(), <$ty as IntoFfi>::convert)
fn convert(self) -> Self::Into {
self.map_or($null, <$ty as IntoFfi>::convert)
}
fn postlude(ret: &str, conv: FfiReturnConvention) -> impl Display {
fn postlude(ret: &str) -> impl Display {
display!(
"if {ret}.__ptr == nil then {ret} = nil; else {}end; ",
<$ty as IntoFfi>::postlude(ret, conv)
<$ty as IntoFfi>::postlude(ret)
)
}
}
};
}
impl_optional_into!(Vec<u8>);
impl_optional_into!(BString);
impl_optional_into!(String);
impl_optional_into!(&'static [u8], lua_buf::null());
impl_optional_into!(&'static BStr, lua_buf::null());
impl_optional_into!(&'static str, lua_buf::null());
impl_optional_into!(Vec<u8>, lua_buffer::null());
impl_optional_into!(BString, lua_buffer::null());
impl_optional_into!(String, lua_buffer::null());