Impl generic From/IntoFfi for Option
This commit is contained in:
parent
530a1530ba
commit
30596d9331
@ -7,6 +7,10 @@ authors.workspace = true
|
||||
homepage.workspace = true
|
||||
repository.workspace = true
|
||||
|
||||
[features]
|
||||
option_ref_abi = []
|
||||
option_string_abi = []
|
||||
|
||||
[dependencies]
|
||||
bstr = "1.12.0"
|
||||
luaffi_impl = { path = "../luaffi_impl" }
|
||||
|
@ -11,13 +11,13 @@ use std::{
|
||||
mem,
|
||||
};
|
||||
|
||||
pub mod future;
|
||||
pub mod string;
|
||||
|
||||
#[doc(hidden)]
|
||||
#[path = "./internal.rs"]
|
||||
pub mod __internal;
|
||||
pub mod future;
|
||||
pub mod option;
|
||||
pub mod result;
|
||||
pub mod string;
|
||||
|
||||
// Dummy function to ensure that strings passed to Rust via wrapper objects will not be
|
||||
// garbage-collected until the end of the function (used in string.rs when string marshalling is
|
||||
@ -857,7 +857,9 @@ macro_rules! impl_ptr_intoabi {
|
||||
|
||||
impl_ptr_intoabi!(*const T);
|
||||
impl_ptr_intoabi!(*mut T);
|
||||
#[cfg(feature = "option_ref_abi")] // disabled because it conflicts with the generic Option<T> impl
|
||||
impl_ptr_intoabi!(Option<&'static T>);
|
||||
#[cfg(feature = "option_ref_abi")]
|
||||
impl_ptr_intoabi!(Option<&'static mut T>);
|
||||
|
||||
//
|
||||
|
84
crates/luaffi/src/option.rs
Normal file
84
crates/luaffi/src/option.rs
Normal file
@ -0,0 +1,84 @@
|
||||
use crate::{
|
||||
__internal::{disp, display},
|
||||
Cdef, CdefBuilder, IntoFfi, KEEP_FN, Type, TypeBuilder, TypeType,
|
||||
};
|
||||
use std::{ffi::c_int, fmt::Display};
|
||||
|
||||
#[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 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_option<T> {
|
||||
fn build(b: &mut CdefBuilder) {
|
||||
b.field::<c_int>("__tag");
|
||||
(T::ty() != TypeType::Void).then(|| b.field::<T>("__value"));
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<T: IntoFfi> IntoFfi for Option<T> {
|
||||
type Into = lua_option<T::Into>;
|
||||
|
||||
fn convert(self) -> Self::Into {
|
||||
match self {
|
||||
Some(value) => lua_option::Some(T::convert(value)),
|
||||
None => lua_option::None,
|
||||
}
|
||||
}
|
||||
|
||||
fn require_owned() -> bool {
|
||||
// lua_option is only used to transmit information about whether we have a value or not and
|
||||
// is forgotten immediately after use, so there is no need for an owned option
|
||||
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 options, 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
|
||||
// option.
|
||||
write!(f, "{ret} = __new(__ct.{ct}, {ret}.__value); ")?;
|
||||
write!(f, "{}", T::postlude(ret))?;
|
||||
} else {
|
||||
// inner value is a "temporary" like an option itself and doesn't require
|
||||
// full ownership of itself. we just need to keep the option 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 option alive
|
||||
}
|
||||
}
|
||||
}
|
||||
write!(f, "else {ret} = nil; end; ")
|
||||
})
|
||||
}
|
||||
}
|
@ -72,12 +72,12 @@ unsafe impl<T: IntoFfi, E: Display> IntoFfi for Result<T, E> {
|
||||
write!(f, "{ret} = __new(__ct.{ct}, {ret}.__value); ")?;
|
||||
write!(f, "{}", T::postlude(ret))?;
|
||||
} else {
|
||||
// inner value is a "temporary" 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} = {ret}; {ret} = {ret}.__value; ")?;
|
||||
// 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 original result alive
|
||||
write!(f, "__C.{KEEP_FN}({ret}_keep); ")?; // keep original result alive
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ use crate::{
|
||||
};
|
||||
use bstr::{BStr, BString};
|
||||
use luaffi_impl::{cdef, metatype};
|
||||
use std::{fmt::Display, mem::ManuallyDrop, ptr, slice};
|
||||
use std::{fmt::Display, mem::ManuallyDrop, slice};
|
||||
|
||||
pub(crate) const IS_UTF8_FN: &str = "luaffi_is_utf8";
|
||||
pub(crate) const DROP_BUFFER_FN: &str = "luaffi_drop_buffer";
|
||||
@ -42,6 +42,7 @@ impl lua_buf {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "option_string_ffi")]
|
||||
pub(crate) fn null() -> Self {
|
||||
Self {
|
||||
__ptr: ptr::null(),
|
||||
@ -71,6 +72,7 @@ impl lua_buffer {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "option_string_ffi")]
|
||||
pub(crate) fn null() -> Self {
|
||||
Self {
|
||||
__ptr: ptr::null_mut(),
|
||||
@ -228,56 +230,64 @@ 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;
|
||||
// `Option<String>: From/IntoFfi` isn't implemented because it conflicts with the generic
|
||||
// `Option<T>: From/IntoFfi` impl and rust doesn't have specialisation yet (and probably not anytime
|
||||
// soon). this is fine for now because we have specialisation for string-like parameters implemented
|
||||
// in the #[metatype] macro already, and string returns wrapped in `Option<T>` isn't much additional
|
||||
// overhead.
|
||||
#[cfg(feature = "option_string_abi")]
|
||||
mod impl_option_string {
|
||||
macro_rules! impl_optional_from {
|
||||
($ty:ty) => {
|
||||
unsafe impl<'s> FromFfi for Option<$ty> {
|
||||
type From = <$ty as FromFfi>::From;
|
||||
|
||||
fn require_keepalive() -> bool {
|
||||
<$ty as FromFfi>::require_keepalive()
|
||||
}
|
||||
fn require_keepalive() -> bool {
|
||||
<$ty as FromFfi>::require_keepalive()
|
||||
}
|
||||
|
||||
fn prelude(arg: &str) -> impl Display {
|
||||
// just pass a null pointer if argument is nil
|
||||
display!(
|
||||
"if {arg} ~= nil then {}end; ",
|
||||
<$ty as FromFfi>::prelude(arg)
|
||||
)
|
||||
}
|
||||
fn prelude(arg: &str) -> impl Display {
|
||||
// just pass a null pointer if argument is nil
|
||||
display!(
|
||||
"if {arg} ~= nil then {}end; ",
|
||||
<$ty as FromFfi>::prelude(arg)
|
||||
)
|
||||
}
|
||||
|
||||
fn convert(from: Self::From) -> Self {
|
||||
from.map(|s| <$ty as FromFfi>::convert(Some(s)))
|
||||
fn convert(from: Self::From) -> Self {
|
||||
from.map(|s| <$ty as FromFfi>::convert(Some(s)))
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
impl_optional_from!(&'s [u8]);
|
||||
impl_optional_from!(&'s BStr);
|
||||
impl_optional_from!(&'s str);
|
||||
|
||||
macro_rules! impl_optional_into {
|
||||
($ty:ty, $null:expr) => {
|
||||
unsafe impl IntoFfi for Option<$ty> {
|
||||
type Into = <$ty as IntoFfi>::Into;
|
||||
|
||||
fn convert(self) -> Self::Into {
|
||||
self.map_or($null, <$ty as IntoFfi>::convert)
|
||||
}
|
||||
|
||||
fn postlude(ret: &str) -> impl Display {
|
||||
display!(
|
||||
"if {ret}.__ptr == nil then {ret} = nil; else {}end; ",
|
||||
<$ty as IntoFfi>::postlude(ret)
|
||||
)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
impl_optional_from!(&'s [u8]);
|
||||
impl_optional_from!(&'s BStr);
|
||||
impl_optional_from!(&'s str);
|
||||
|
||||
macro_rules! impl_optional_into {
|
||||
($ty:ty, $null:expr) => {
|
||||
unsafe impl IntoFfi for Option<$ty> {
|
||||
type Into = <$ty as IntoFfi>::Into;
|
||||
|
||||
fn convert(self) -> Self::Into {
|
||||
self.map_or($null, <$ty as IntoFfi>::convert)
|
||||
}
|
||||
|
||||
fn postlude(ret: &str) -> impl Display {
|
||||
display!(
|
||||
"if {ret}.__ptr == nil then {ret} = nil; else {}end; ",
|
||||
<$ty as IntoFfi>::postlude(ret)
|
||||
)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
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());
|
||||
|
Loading…
x
Reference in New Issue
Block a user