use crate::{ __internal::{disp, display}, FfiReturnConvention, FromFfi, IS_UTF8_FN, ToFfi, Type, }; use bstr::BStr; use luaffi_impl::{cdef, metatype}; use std::{fmt::Display, slice}; #[derive(Debug, Clone, Copy)] #[cdef] pub struct lua_buf { __ptr: *const u8, __len: usize, } #[metatype] impl lua_buf {} // not implemented yet #[derive(Debug, Clone, Copy)] #[cdef] pub struct lua_buffer { __ptr: *const u8, __len: usize, __cap: usize, } #[metatype] impl lua_buffer {} unsafe impl<'s> FromFfi for &'s [u8] { type From = lua_buf; type FromArg = Option<&'s Self::From>; fn require_keepalive() -> bool { true } fn prelude(arg: &str) -> impl Display { // this converts string arguments to a `lua_buf` with a pointer to the string and its length disp(move |f| { let ct = Self::From::name(); write!( f, r#"assert(type({arg}) == "string", "string expected in argument '{arg}', got " .. type({arg})); "# )?; write!(f, "{arg} = __new(__ct.{ct}, {arg}, #{arg}); end; ") }) } fn convert(from: Self::From) -> Self { // SAFETY: we already checked that the string is nonnull from the lua side debug_assert!(!from.__ptr.is_null()); unsafe { slice::from_raw_parts(from.__ptr, from.__len) } } fn convert_arg(from: Self::FromArg) -> Self { // SAFETY: we already checked that the string is nonnull from the lua side debug_assert!(from.is_some()); unsafe { FromFfi::convert(*from.unwrap_unchecked()) } } } unsafe impl<'s> FromFfi for &'s BStr { type From = <&'s [u8] as FromFfi>::From; type FromArg = <&'s [u8] as FromFfi>::FromArg; fn require_keepalive() -> bool { <&'s [u8] as FromFfi>::require_keepalive() } fn prelude(arg: &str) -> impl Display { <&'s [u8] as FromFfi>::prelude(arg) } fn convert(from: Self::From) -> Self { <&'s [u8] as FromFfi>::convert(from).into() } fn convert_arg(from: Self::FromArg) -> Self { <&'s [u8] as FromFfi>::convert_arg(from).into() } } unsafe impl<'s> FromFfi for &'s str { type From = lua_buf; type FromArg = Option<&'s Self::From>; fn require_keepalive() -> bool { true } fn prelude(arg: &str) -> impl Display { disp(move |f| { let ct = Self::From::name(); write!( f, r#"assert(type({arg}) == "string", "string expected in argument '{arg}', got " .. type({arg})); "# )?; write!( f, r#"assert(__C.{IS_UTF8_FN}({arg}, #{arg}), "argument '{arg}' must be a valid utf-8 string"); "# )?; write!(f, "{arg} = __new(__ct.{ct}, {arg}, #{arg}); ") }) } fn convert(from: Self::From) -> Self { // SAFETY: we already checked that the string is nonnull and valid utf8 from the lua side debug_assert!(!from.__ptr.is_null()); let s = unsafe { slice::from_raw_parts(from.__ptr, from.__len) }; debug_assert!( std::str::from_utf8(s).is_ok(), "<&str>::convert() called on an invalid utf8 string when it was checked to be valid" ); unsafe { std::str::from_utf8_unchecked(s) } } fn convert_arg(from: Self::FromArg) -> Self { // SAFETY: we already checked that the string is nonnull and valid utf8 from the lua side debug_assert!(from.is_some()); unsafe { FromFfi::convert(*from.unwrap_unchecked()) } } } unsafe impl ToFfi 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 ToFfi for &'static BStr { type To = <&'static [u8] as ToFfi>::To; fn convert(self) -> Self::To { <&'static [u8] as ToFfi>::convert(self.as_ref()) } fn postlude(ret: &str, conv: FfiReturnConvention) -> impl Display { <&'static [u8] as ToFfi>::postlude(ret, conv) } } unsafe impl ToFfi for &'static str { type To = <&'static [u8] as ToFfi>::To; fn convert(self) -> Self::To { <&'static [u8] as ToFfi>::convert(self.as_bytes()) } fn postlude(ret: &str, conv: FfiReturnConvention) -> impl Display { <&'static [u8] as ToFfi>::postlude(ret, conv) } }