diff --git a/.github/workflows/gh-pages.yml b/.github/workflows/gh-pages.yml
index be57730bb4..cb46800afb 100644
--- a/.github/workflows/gh-pages.yml
+++ b/.github/workflows/gh-pages.yml
@@ -173,7 +173,15 @@ jobs:
command: test
args: --features docs
- - name: FFI test (C & C#?)
+ - name: Install LuaJIT and LuaRocks (Ubuntu)
+ run: sudo apt-get install -y luajit
+ if: matrix.os == 'ubuntu-latest'
+
+ - name: Install LuaJIT and LuaRocks (macOS)
+ run: brew install luajit
+ if: matrix.os == 'macos-latest'
+
+ - name: FFI test (C & C# & Lua?)
run: make -C ffi_tests
if: runner.os != 'Windows'
env:
diff --git a/ffi_tests/generated.cffi b/ffi_tests/generated.cffi
index 2a28e32bb4..1d4127ff0d 100644
--- a/ffi_tests/generated.cffi
+++ b/ffi_tests/generated.cffi
@@ -20,6 +20,36 @@ typedef struct AnUnusedStruct {
Wow_t are_you_still_there;
} AnUnusedStruct_t;
+typedef struct {
+ float idx[3];
+} float_3_array_t;
+
+typedef struct {
+ uint64_t idx[5];
+} uint64_5_array_t;
+
+typedef struct {
+ uint8_t idx[1];
+} uint8_1_array_t;
+
+typedef struct {
+ uint8_1_array_t idx[2];
+} uint8_1_array_2_array_t;
+
+typedef struct {
+ uint8_1_array_2_array_t idx[3];
+} uint8_1_array_2_array_3_array_t;
+
+typedef struct ArraysStruct {
+ float_3_array_t floats;
+
+ uint64_5_array_t sizes;
+
+ uint8_1_array_2_array_t dim_2;
+
+ uint8_1_array_2_array_3_array_t dim_3;
+} ArraysStruct_t;
+
#define FOO ...
typedef enum Bar {
@@ -36,6 +66,34 @@ typedef struct next_generation {
void * (*cb)(bool);
} next_generation_t;
+typedef struct ConstGenericStruct_uint8_1 {
+ uint8_1_array_t data;
+} ConstGenericStruct_uint8_1_t;
+
+typedef struct {
+ uint8_t idx[2];
+} uint8_2_array_t;
+
+typedef struct ConstGenericStruct_uint8_2 {
+ uint8_2_array_t data;
+} ConstGenericStruct_uint8_2_t;
+
+typedef struct {
+ uint16_t idx[3];
+} uint16_3_array_t;
+
+typedef struct ConstGenericStruct_uint16_3 {
+ uint16_3_array_t data;
+} ConstGenericStruct_uint16_3_t;
+
+typedef struct SpecificConstGenericContainer {
+ ConstGenericStruct_uint8_1_t field1;
+
+ ConstGenericStruct_uint8_2_t field2;
+
+ ConstGenericStruct_uint16_3_t field3;
+} SpecificConstGenericContainer_t;
+
typedef enum triforce {
TRIFORCE_DIN,
TRIFORCE_FARORE,
diff --git a/ffi_tests/generated.cs b/ffi_tests/generated.cs
index 0d89dde0b6..701def83ec 100644
--- a/ffi_tests/generated.cs
+++ b/ffi_tests/generated.cs
@@ -33,6 +33,47 @@ public unsafe struct AnUnusedStruct_t {
public Wow_t are_you_still_there;
}
+[StructLayout(LayoutKind.Sequential, Size = 12)]
+public unsafe struct float_3_array_t {
+ public float _0;
+ public float _1;
+ public float _2;
+}
+
+[StructLayout(LayoutKind.Sequential, Size = 40)]
+public unsafe struct uint64_5_array_t {
+ public fixed UInt64 arr[5];
+}
+
+[StructLayout(LayoutKind.Sequential, Size = 1)]
+public unsafe struct uint8_1_array_t {
+ public fixed byte arr[1];
+}
+
+[StructLayout(LayoutKind.Sequential, Size = 2)]
+public unsafe struct uint8_1_array_2_array_t {
+ public uint8_1_array_t _0;
+ public uint8_1_array_t _1;
+}
+
+[StructLayout(LayoutKind.Sequential, Size = 6)]
+public unsafe struct uint8_1_array_2_array_3_array_t {
+ public uint8_1_array_2_array_t _0;
+ public uint8_1_array_2_array_t _1;
+ public uint8_1_array_2_array_t _2;
+}
+
+[StructLayout(LayoutKind.Sequential, Size = 64)]
+public unsafe struct ArraysStruct_t {
+ public float_3_array_t floats;
+
+ public uint64_5_array_t sizes;
+
+ public uint8_1_array_2_array_t dim_2;
+
+ public uint8_1_array_2_array_3_array_t dim_3;
+}
+
public unsafe partial class Ffi {
public const Int32 FOO = 42;
}
@@ -66,6 +107,40 @@ public unsafe struct next_generation_t {
public void_ptr_bool_fptr cb;
}
+[StructLayout(LayoutKind.Sequential, Size = 1)]
+public unsafe struct ConstGenericStruct_uint8_1_t {
+ public uint8_1_array_t data;
+}
+
+[StructLayout(LayoutKind.Sequential, Size = 2)]
+public unsafe struct uint8_2_array_t {
+ public fixed byte arr[2];
+}
+
+[StructLayout(LayoutKind.Sequential, Size = 2)]
+public unsafe struct ConstGenericStruct_uint8_2_t {
+ public uint8_2_array_t data;
+}
+
+[StructLayout(LayoutKind.Sequential, Size = 6)]
+public unsafe struct uint16_3_array_t {
+ public fixed UInt16 arr[3];
+}
+
+[StructLayout(LayoutKind.Sequential, Size = 6)]
+public unsafe struct ConstGenericStruct_uint16_3_t {
+ public uint16_3_array_t data;
+}
+
+[StructLayout(LayoutKind.Sequential, Size = 10)]
+public unsafe struct SpecificConstGenericContainer_t {
+ public ConstGenericStruct_uint8_1_t field1;
+
+ public ConstGenericStruct_uint8_2_t field2;
+
+ public ConstGenericStruct_uint16_3_t field3;
+}
+
///
/// Hello, World!
///
diff --git a/ffi_tests/generated.h b/ffi_tests/generated.h
index 65b74c4136..3ee96ec1d3 100644
--- a/ffi_tests/generated.h
+++ b/ffi_tests/generated.h
@@ -39,6 +39,41 @@ typedef struct AnUnusedStruct {
Wow_t are_you_still_there;
} AnUnusedStruct_t;
+typedef struct {
+ float idx[3];
+} float_3_array_t;
+
+typedef struct {
+ uint64_t idx[5];
+} uint64_5_array_t;
+
+typedef struct {
+ uint8_t idx[1];
+} uint8_1_array_t;
+
+typedef struct {
+ uint8_1_array_t idx[2];
+} uint8_1_array_2_array_t;
+
+typedef struct {
+ uint8_1_array_2_array_t idx[3];
+} uint8_1_array_2_array_3_array_t;
+
+/** */
+typedef struct ArraysStruct {
+ /** */
+ float_3_array_t floats;
+
+ /** */
+ uint64_5_array_t sizes;
+
+ /** */
+ uint8_1_array_2_array_t dim_2;
+
+ /** */
+ uint8_1_array_2_array_3_array_t dim_3;
+} ArraysStruct_t;
+
/** */
#define FOO ((int32_t) 42)
@@ -76,6 +111,44 @@ typedef struct next_generation {
void * (*cb)(bool);
} next_generation_t;
+/** */
+typedef struct ConstGenericStruct_uint8_1 {
+ /** */
+ uint8_1_array_t data;
+} ConstGenericStruct_uint8_1_t;
+
+typedef struct {
+ uint8_t idx[2];
+} uint8_2_array_t;
+
+/** */
+typedef struct ConstGenericStruct_uint8_2 {
+ /** */
+ uint8_2_array_t data;
+} ConstGenericStruct_uint8_2_t;
+
+typedef struct {
+ uint16_t idx[3];
+} uint16_3_array_t;
+
+/** */
+typedef struct ConstGenericStruct_uint16_3 {
+ /** */
+ uint16_3_array_t data;
+} ConstGenericStruct_uint16_3_t;
+
+/** */
+typedef struct SpecificConstGenericContainer {
+ /** */
+ ConstGenericStruct_uint8_1_t field1;
+
+ /** */
+ ConstGenericStruct_uint8_2_t field2;
+
+ /** */
+ ConstGenericStruct_uint16_3_t field3;
+} SpecificConstGenericContainer_t;
+
/** \brief
* Hello, `World`!
*/
diff --git a/ffi_tests/generated.lua b/ffi_tests/generated.lua
new file mode 100644
index 0000000000..2619397f72
--- /dev/null
+++ b/ffi_tests/generated.lua
@@ -0,0 +1,338 @@
+-- File auto-generated by `::safer_ffi`.
+--
+-- Do not manually edit this file.
+
+local ffi = require "ffi"
+
+ffi.cdef [[
+
+//
+// enum has the same ABI as `uint8_t`
+typedef enum Wow {
+ //
+ WOW_LEROY,
+ //
+ WOW_JENKINS,
+}; typedef uint8_t Wow_t;
+
+//
+typedef struct AnUnusedStruct {
+ //
+ Wow_t are_you_still_there;
+} AnUnusedStruct_t;
+
+//
+typedef struct ArraysStruct {
+ //
+ float floats[3];
+
+ //
+ uint64_t sizes[5];
+
+ //
+ uint8_t dim_2[2][1];
+
+ //
+ uint8_t dim_3[3][2][1];
+} ArraysStruct_t;
+
+//
+static const int32_t FOO = 42;
+
+//
+// enum has the same ABI as `int8_t`
+typedef enum Bar {
+ //
+ BAR_A = 43,
+ //
+ BAR_B = 42,
+}; typedef int8_t Bar_t;
+
+// Hello, `World`!
+typedef struct next_generation {
+ // I test some `gen`-eration.
+ Bar_t gen;
+
+ // with function pointers and everything!
+ void * (*cb)(bool);
+} next_generation_t;
+
+//
+typedef struct ConstGenericStruct_uint8_1 {
+ //
+ uint8_t data[1];
+} ConstGenericStruct_uint8_1_t;
+
+//
+typedef struct ConstGenericStruct_uint8_2 {
+ //
+ uint8_t data[2];
+} ConstGenericStruct_uint8_2_t;
+
+//
+typedef struct ConstGenericStruct_uint16_3 {
+ //
+ uint16_t data[3];
+} ConstGenericStruct_uint16_3_t;
+
+//
+typedef struct SpecificConstGenericContainer {
+ //
+ ConstGenericStruct_uint8_1_t field1;
+
+ //
+ ConstGenericStruct_uint8_2_t field2;
+
+ //
+ ConstGenericStruct_uint16_3_t field3;
+} SpecificConstGenericContainer_t;
+
+// Hello, `World`!
+// enum has the same ABI as `uint8_t`
+typedef enum triforce {
+ //
+ TRIFORCE_DIN = 3,
+ //
+ TRIFORCE_FARORE = 1,
+ //
+ TRIFORCE_NARYU,
+}; typedef uint8_t triforce_t;
+
+// https://github.com/getditto/safer_ffi/issues/45
+int32_t
+_issue_45 (
+ int32_t __arg_0);
+
+//
+int32_t
+async_get_ft (void);
+
+// `Arc Ret>`
+typedef struct ArcDynFn0_void {
+ //
+ void * env_ptr;
+
+ //
+ void (*call)(void *);
+
+ //
+ void (*release)(void *);
+
+ //
+ void (*retain)(void *);
+} ArcDynFn0_void_t;
+
+//
+void
+call_in_the_background (
+ ArcDynFn0_void_t f);
+
+// This is a `#[repr(C)]` enum, which leads to a classic enum def.
+typedef enum SomeReprCEnum {
+ // This is some variant.
+ SOME_REPR_C_ENUM_SOME_VARIANT,
+} SomeReprCEnum_t;
+
+//
+void
+check_SomeReprCEnum (
+ SomeReprCEnum_t _baz);
+
+//
+void
+check_bar (
+ Bar_t _bar);
+
+// Concatenate the two input strings into a new one.
+//
+// The returned string must be freed using `free_char_p`.
+char *
+concat (
+ char const * fst,
+ char const * snd);
+
+// Frees a string created by `concat`.
+void
+free_char_p (
+ char * _string);
+
+//
+typedef struct foo foo_t;
+
+//
+void
+free_foo (
+ foo_t * foo);
+
+// `&'lt [T]` but with a guaranteed `#[repr(C)]` layout.
+//
+// # C layout (for some given type T)
+//
+// ```c
+// typedef struct {
+// // Cannot be NULL
+// T * ptr;
+// size_t len;
+// } slice_T;
+// ```
+//
+// # Nullable pointer?
+//
+// If you want to support the above typedef, but where the `ptr` field is
+// allowed to be `NULL` (with the contents of `len` then being undefined)
+// use the `Option< slice_ptr<_> >` type.
+typedef struct slice_ref_int32 {
+ // Pointer to the first element (if any).
+ int32_t const * ptr;
+
+ // Element count
+ size_t len;
+} slice_ref_int32_t;
+
+// Returns a pointer to the maximum integer of the input slice, or `NULL` if
+// it is empty.
+int32_t const *
+max (
+ slice_ref_int32_t xs);
+
+//
+foo_t *
+new_foo (void);
+
+//
+int32_t
+read_foo (
+ foo_t const * foo);
+
+//
+uint16_t (*
+returns_a_fn_ptr (void))(uint8_t);
+
+// The layout of `core::task::wake::Context` is opaque/subject to changes.
+typedef struct Opaque_Context Opaque_Context_t;
+
+//
+ArcDynFn0_void_t
+rust_future_task_context_get_waker (
+ Opaque_Context_t const * task_context);
+
+//
+void
+rust_future_task_context_wake (
+ Opaque_Context_t const * task_context);
+
+//
+typedef struct Erased Erased_t;
+
+// An FFI-safe `Poll<()>`.
+// enum has the same ABI as `int8_t`
+typedef enum PollFuture {
+ //
+ POLL_FUTURE_COMPLETED = 0,
+ //
+ POLL_FUTURE_PENDING = -1,
+}; typedef int8_t PollFuture_t;
+
+//
+typedef struct FfiFutureVTable {
+ //
+ void (*release_vptr)(Erased_t *);
+
+ //
+ PollFuture_t (*dyn_poll)(Erased_t *, Opaque_Context_t *);
+} FfiFutureVTable_t;
+
+//
+typedef struct VirtualPtr__Erased_ptr_FfiFutureVTable {
+ //
+ Erased_t * ptr;
+
+ //
+ FfiFutureVTable_t vtable;
+} VirtualPtr__Erased_ptr_FfiFutureVTable_t;
+
+// `Box Ret>`
+typedef struct BoxDynFnMut0_void {
+ //
+ void * env_ptr;
+
+ //
+ void (*call)(void *);
+
+ //
+ void (*free)(void *);
+} BoxDynFnMut0_void_t;
+
+//
+typedef struct DropGlueVTable {
+ //
+ void (*release_vptr)(Erased_t *);
+} DropGlueVTable_t;
+
+//
+typedef struct VirtualPtr__Erased_ptr_DropGlueVTable {
+ //
+ Erased_t * ptr;
+
+ //
+ DropGlueVTable_t vtable;
+} VirtualPtr__Erased_ptr_DropGlueVTable_t;
+
+//
+typedef struct FfiFutureExecutorVTable {
+ //
+ void (*release_vptr)(Erased_t *);
+
+ //
+ Erased_t * (*retain_vptr)(Erased_t const *);
+
+ //
+ VirtualPtr__Erased_ptr_FfiFutureVTable_t (*dyn_spawn)(Erased_t const *, VirtualPtr__Erased_ptr_FfiFutureVTable_t);
+
+ //
+ VirtualPtr__Erased_ptr_FfiFutureVTable_t (*dyn_spawn_blocking)(Erased_t const *, BoxDynFnMut0_void_t);
+
+ //
+ void (*dyn_block_on)(Erased_t const *, VirtualPtr__Erased_ptr_FfiFutureVTable_t);
+
+ //
+ VirtualPtr__Erased_ptr_DropGlueVTable_t (*dyn_enter)(Erased_t const *);
+} FfiFutureExecutorVTable_t;
+
+//
+typedef struct VirtualPtr__Erased_ptr_FfiFutureExecutorVTable {
+ //
+ Erased_t * ptr;
+
+ //
+ FfiFutureExecutorVTable_t vtable;
+} VirtualPtr__Erased_ptr_FfiFutureExecutorVTable_t;
+
+//
+int32_t
+test_spawner (
+ VirtualPtr__Erased_ptr_FfiFutureExecutorVTable_t executor);
+
+// `&'lt mut (dyn 'lt + Send + FnMut(A1) -> Ret)`
+typedef struct RefDynFnMut1_void_char_const_ptr {
+ //
+ void * env_ptr;
+
+ //
+ void (*call)(void *, char const *);
+} RefDynFnMut1_void_char_const_ptr_t;
+
+// Same as `concat`, but with a callback-based API to auto-free the created
+// string.
+void
+with_concat (
+ char const * fst,
+ char const * snd,
+ RefDynFnMut1_void_char_const_ptr_t cb);
+
+//
+bool
+with_foo (
+ void (*cb)(foo_t *));
+
+]]
\ No newline at end of file
diff --git a/ffi_tests/src/lib.rs b/ffi_tests/src/lib.rs
index 74ddc58a4e..4d72956e81 100644
--- a/ffi_tests/src/lib.rs
+++ b/ffi_tests/src/lib.rs
@@ -211,13 +211,46 @@ pub struct AnUnusedStruct {
are_you_still_there: Wow,
}
+#[ffi_export]
+#[derive_ReprC]
+#[repr(C)]
+pub struct ArraysStruct {
+ floats: [f32; 3],
+ sizes: [u64; 5],
+ dim_2: [[u8; 1]; 2],
+ dim_3: [[[u8; 1]; 2]; 3],
+}
+
+#[derive_ReprC]
+#[repr(C)]
+pub struct ConstGenericStruct {
+ data: [T; M]
+}
+
+#[ffi_export]
+#[derive_ReprC]
+#[repr(C)]
+pub struct SpecificConstGenericContainer {
+ field1: ConstGenericStruct<1, u8>,
+ field2: ConstGenericStruct<2, u8>,
+ field3: ConstGenericStruct<3, u16>,
+}
+
#[safer_ffi::cfg_headers]
#[test]
fn generate_headers ()
-> ::std::io::Result<()>
{
use ::safer_ffi::headers::Language::*;
- for &(language, ext) in &[(C, "h"), (CSharp, "cs"), (Python, "cffi")] {
+
+ let languages = &[
+ (C, "h"),
+ (CSharp, "cs"),
+ (Lua, "lua"),
+ (Python, "cffi"),
+ ];
+
+ for &(language, ext) in languages {
let builder =
::safer_ffi::headers::builder()
.with_language(language)
diff --git a/ffi_tests/tests/lua/generated.lua b/ffi_tests/tests/lua/generated.lua
new file mode 120000
index 0000000000..301e861214
--- /dev/null
+++ b/ffi_tests/tests/lua/generated.lua
@@ -0,0 +1 @@
+../../generated.lua
\ No newline at end of file
diff --git a/ffi_tests/tests/lua/tests.lua b/ffi_tests/tests/lua/tests.lua
new file mode 100644
index 0000000000..3da0be6e43
--- /dev/null
+++ b/ffi_tests/tests/lua/tests.lua
@@ -0,0 +1,132 @@
+local ffi = require("ffi")
+local generated = require("generated")
+local lib = ffi.load "libffi_tests"
+
+function test_concat()
+ local s1 = "Hello, "
+ local s2 = "World!"
+ local p = lib.concat(s1, s2);
+ local res = ffi.string(p)
+ lib.free_char_p(p);
+ assert(res == s1 .. s2)
+end
+
+function test_max()
+ local arr = { -27, -42, 9, -8 }
+ local c_arr = ffi.new("int32_t[?]", #arr, arr)
+ local c_slice = ffi.new("slice_ref_int32_t", c_arr, #arr)
+ local p = lib.max(c_slice);
+ assert(p ~= ffi.NULL)
+ assert(p[0] == 9)
+end
+
+function test_max_empty()
+ local arr = {}
+ local c_arr = ffi.new("int32_t[?]", #arr, arr)
+ local c_slice = ffi.new("slice_ref_int32_t", c_arr, #arr)
+ local p = lib.max(c_slice);
+ assert(p == ffi.NULL)
+end
+
+function test_foo()
+ local foo = lib.new_foo()
+ assert(lib.read_foo(foo) == 42)
+ local called = false;
+ lib.with_foo(function()
+ assert(lib.read_foo(foo) == 42)
+ called = true
+ end)
+ assert(called == true)
+ lib.free_foo(foo)
+ lib.free_foo(nil)
+end
+
+function test_constant()
+ assert(lib.FOO == 42)
+end
+
+function test_currified_thing()
+ assert(lib.returns_a_fn_ptr()(0x42) == 0x4200)
+end
+
+function test_enum_int_constant()
+ assert(lib.TRIFORCE_DIN == 3)
+ assert(lib.TRIFORCE_FARORE == 1)
+ assert(lib.TRIFORCE_NARYU == 2)
+end
+
+function test_arrays_struct()
+ local a = ffi.new("ArraysStruct_t")
+ a.floats = ffi.new("float[3]", {7, 8, 9})
+ a.sizes = ffi.new("size_t[5]", {5, 4, 3, 2, 1})
+ a.dim_2 = ffi.new("uint8_t[2][1]", {{1}, {2}})
+ a.dim_3 = ffi.new("uint8_t[3][2][1]", {{{1}, {1}}, {{2}, {2}}, {{3}, {3}}})
+
+ assert(a.floats[0] == 7)
+ assert(a.floats[1] == 8)
+ assert(a.floats[2] == 9)
+
+ assert(a.sizes[0] == 5)
+ assert(a.sizes[1] == 4)
+ assert(a.sizes[2] == 3)
+ assert(a.sizes[3] == 2)
+ assert(a.sizes[4] == 1)
+
+ assert(a.dim_2[0][0] == 1)
+ assert(a.dim_2[1][0] == 2)
+
+ assert(a.dim_3[0][0][0] == 1)
+ assert(a.dim_3[0][1][0] == 1)
+ assert(a.dim_3[1][0][0] == 2)
+ assert(a.dim_3[1][1][0] == 2)
+ assert(a.dim_3[2][0][0] == 3)
+ assert(a.dim_3[2][1][0] == 3)
+end
+
+function test_const_generics_struct()
+ local a = ffi.new("SpecificConstGenericContainer_t")
+ a.field1 = ffi.new("ConstGenericStruct_uint8_1_t", ffi.new("uint8_t[1]", {1}))
+ a.field2 = ffi.new("ConstGenericStruct_uint8_2_t", ffi.new("uint8_t[2]", {1, 2}))
+ a.field3 = ffi.new("ConstGenericStruct_uint16_3_t", ffi.new("uint16_t[3]", {1, 2, 3}))
+
+ assert(a.field1.data[0] == 1)
+ assert(a.field2.data[0] == 1)
+ assert(a.field2.data[1] == 2)
+ assert(a.field3.data[0] == 1)
+ assert(a.field3.data[1] == 2)
+ assert(a.field3.data[2] == 3)
+end
+
+function run_tests()
+ local tests = {
+ test_concat,
+ test_max,
+ test_max_empty,
+ test_foo,
+ test_constant,
+ test_currified_thing,
+ test_enum_int_constant,
+ test_arrays_struct,
+ test_const_generics_struct,
+ }
+
+ local passed = 0
+ local failed = 0
+ for _, test in ipairs(tests) do
+ local status, err = pcall(test)
+ if status then
+ passed = passed + 1
+ else
+ failed = failed + 1
+ print("Failed: " .. err)
+ end
+ end
+
+ print(string.format("Passed: %d, Failed: %d", passed, failed))
+
+ if failed > 0 then
+ os.exit(1)
+ end
+end
+
+run_tests()
\ No newline at end of file
diff --git a/ffi_tests/tests/main.rs b/ffi_tests/tests/main.rs
index abb8bd4b21..e644be0820 100644
--- a/ffi_tests/tests/main.rs
+++ b/ffi_tests/tests/main.rs
@@ -81,3 +81,20 @@ fn test_csharp_code ()
.success()
);
}
+
+#[test]
+fn test_lua_code()
+{
+ let output = ::std::process::Command::new("luajit")
+ .current_dir(concat!(env!("CARGO_MANIFEST_DIR"), "/tests/lua"))
+ .arg("tests.lua")
+ .output()
+ .expect("Failed to run Lua tests");
+
+ assert!(
+ output.status.success(),
+ "Lua tests failed with output:\n\nSTDOUT:\n{}\n\nSTDERR:\n{}",
+ String::from_utf8_lossy(&output.stdout),
+ String::from_utf8_lossy(&output.stderr)
+ );
+}
diff --git a/src/c_char.rs b/src/c_char.rs
index 404ef10d29..8bb88841e3 100644
--- a/src/c_char.rs
+++ b/src/c_char.rs
@@ -87,6 +87,15 @@ impl LegacyCType
"byte".into()
}
}
+
+ __cfg_lua__! {
+ fn lua_define_self (
+ _: &'_ mut dyn crate::headers::Definer,
+ ) -> io::Result<()>
+ {
+ Ok(())
+ }
+ }
} type OPAQUE_KIND = crate::layout::OpaqueKind::Concrete; }
from_CType_impl_ReprC! {
diff --git a/src/headers/_mod.rs b/src/headers/_mod.rs
index c1f7cc6c9b..675f3b3d17 100644
--- a/src/headers/_mod.rs
+++ b/src/headers/_mod.rs
@@ -343,7 +343,15 @@ impl Builder<'_, WhereTo> {
fn write_banner (&'_ self, definer: &'_ mut dyn Definer)
-> io::Result<()>
{
- let banner: &'_ str = self.banner.unwrap_or(concat!(
+ let lang = self.language.unwrap_or(Language::C);
+
+ let banner: &'_ str = self.banner.unwrap_or(match lang {
+ Language::Lua => concat!(
+ "-- File auto-generated by `::safer_ffi`.\n",
+ "--\n",
+ "-- Do not manually edit this file.\n",
+ ),
+ _ => concat!(
"/*! \\file */\n",
"/*******************************************\n",
" * *\n",
@@ -352,7 +360,9 @@ impl Builder<'_, WhereTo> {
" * Do not manually edit this file. *\n",
" * *\n",
" *******************************************/\n",
- ));
+ ),
+ });
+
writeln!(definer.out(), "{}", banner)
}
@@ -375,6 +385,8 @@ impl Builder<'_, WhereTo> {
RustLib = Self::lib_name(),
),
+ | Language::Lua => writeln!(definer.out(), include_str!("templates/lua/_prelude.lua")),
+
#[cfg(feature = "python-headers")]
// CHECKME
| Language::Python => Ok(()),
@@ -387,6 +399,13 @@ impl Builder<'_, WhereTo> {
{
let stable_header = self.stable_header.unwrap_or(true);
let lang = self.language.unwrap_or(Language::C);
+
+ // skip adding int/bool headers for Lua
+ if lang == Language::Lua {
+ definer.insert("__int_headers__");
+ definer.insert("bool");
+ }
+
let _naming_convention =
self.naming_convention
.as_ref()
@@ -434,6 +453,11 @@ impl Builder<'_, WhereTo> {
PkgName = pkg_name,
)
},
+
+ | Language::Lua => {
+ write!(definer.out(), include_str!("templates/lua/epilogue.lua"))
+ },
+
#[cfg(feature = "python-headers")]
// CHECKME
| Language::Python => Ok(()),
@@ -502,6 +526,10 @@ enum Language {
/// C#
CSharp,
+
+ /// Lua
+ Lua,
+
/// Python (experimental).
#[cfg(feature = "python-headers")]
Python,
@@ -530,6 +558,9 @@ hidden_export! {
| Language::CSharp => {
::define_self(&crate::headers::languages::CSharp, definer)
},
+ | Language::Lua => {
+ ::define_self(&crate::headers::languages::Lua, definer)
+ },
#[cfg(feature = "python-headers")]
| Language::Python => {
::define_self(&crate::headers::languages::Python, definer)
@@ -557,6 +588,7 @@ fn __define_fn__ (
let dyn_lang: &dyn HeaderLanguage = match lang {
| Language::C => &languages::C,
| Language::CSharp => &languages::CSharp,
+ | Language::Lua => &languages::Lua,
#[cfg(feature = "python-headers")]
| Language::Python => &languages::Python,
};
@@ -593,6 +625,11 @@ hidden_export! {
| Language::CSharp => write!(out,
"{} (", f_name.trim(),
),
+
+ | Language::Lua => write!(out,
+ "{} (", f_name.trim(),
+ ),
+
#[cfg(feature = "python-headers")]
| Language::Python => write!(out,
"{} (", f_name.trim(),
@@ -627,6 +664,12 @@ hidden_export! {
.unwrap_or("")
,
),
+
+ | Language::Lua => write!(out,
+ "\n {}",
+ Arg::CLayout::name_wrapping_var(&crate::headers::languages::Lua, arg_name),
+ ),
+
#[cfg(feature = "python-headers")]
| Language::Python => write!(out,
"\n {}",
@@ -673,6 +716,17 @@ hidden_export! {
,
)
},
+
+ | Language::Lua => {
+ if fname_and_args.ends_with("(") {
+ fname_and_args.push_str("void");
+ }
+ writeln!(out,
+ "{});\n",
+ Ret::CLayout::name_wrapping_var(&crate::headers::languages::Lua, &fname_and_args),
+ )
+ },
+
#[cfg(feature = "python-headers")]
| Language::Python => {
if fname_and_args.ends_with("(") {
diff --git a/src/headers/languages/lua.rs b/src/headers/languages/lua.rs
new file mode 100644
index 0000000000..88f939e05c
--- /dev/null
+++ b/src/headers/languages/lua.rs
@@ -0,0 +1,226 @@
+#![cfg_attr(rustfmt, rustfmt::skip)]
+
+use super::*;
+
+pub struct Lua;
+
+impl HeaderLanguage for Lua {
+ fn emit_docs (
+ self: &'_ Self,
+ ctx: &'_ mut dyn Definer,
+ docs: Docs<'_>,
+ indent: &'_ Indentation,
+ ) -> io::Result<()>
+ {
+ mk_out!(indent, ctx.out());
+
+ if docs.is_empty() {
+ out!(("// "));
+ return Ok(());
+ }
+
+ for line in docs.iter().copied().map(str::trim) {
+ let sep = if line.is_empty() { "" } else { " " };
+ out!(("//{sep}{line}"));
+ }
+
+ Ok(())
+ }
+
+ fn emit_simple_enum (
+ self: &'_ Self,
+ ctx: &'_ mut dyn Definer,
+ docs: Docs<'_>,
+ self_ty: &'_ dyn PhantomCType,
+ backing_integer: Option<&dyn PhantomCType>,
+ variants: &'_ [EnumVariant<'_>],
+ ) -> io::Result<()>
+ {
+ let ref indent = Indentation::new(4 /* ctx.indent_width() */);
+ mk_out!(indent, ctx.out());
+
+ let ref intn_t =
+ backing_integer.map(|it| it.name(self))
+ ;
+
+ self.emit_docs(ctx, docs, indent)?;
+
+ let ref short_name = self_ty.short_name();
+ let ref full_ty_name = self_ty.name(self);
+
+ if let Some(intn_t) = intn_t {
+ out!((
+ "// enum has the same ABI as `{intn_t}`"
+ "typedef enum {short_name} {{"
+ ));
+ } else {
+ out!(("typedef enum {short_name} {{"));
+ }
+
+ if let _ = indent.scope() {
+ for v in variants {
+ self.emit_docs(ctx, v.docs, indent)?;
+ let variant_name = crate::utils::screaming_case(short_name, v.name) /* ctx.adjust_variant_name(
+ Language::C,
+ enum_name,
+ v.name,
+ ) */;
+ if let Some(value) = v.discriminant {
+ out!(("{variant_name} = {value:?},"));
+ } else {
+ out!(("{variant_name},"));
+ }
+ }
+ }
+
+ if let Some(intn_t) = intn_t {
+ out!(("}}; typedef {intn_t} {full_ty_name};"));
+ } else {
+ out!(("}} {full_ty_name};"));
+ }
+
+ out!("\n");
+
+ Ok(())
+ }
+
+ fn emit_struct (
+ self: &'_ Self,
+ ctx: &'_ mut dyn Definer,
+ docs: Docs<'_>,
+ self_ty: &'_ dyn PhantomCType,
+ fields: &'_ [StructField<'_>]
+ ) -> io::Result<()>
+ {
+ let ref indent = Indentation::new(4 /* ctx.indent_width() */);
+ mk_out!(indent, ctx.out());
+ let short_name = self_ty.short_name();
+ let full_ty_name = self_ty.name(self);
+
+ if self_ty.size() == 0 {
+ panic!("C does not support zero-sized structs!")
+ }
+
+ self.emit_docs(ctx, docs, indent)?;
+ out!(("typedef struct {short_name} {{"));
+ if let _ = indent.scope() {
+ let ref mut first = true;
+ for &StructField { docs, name, ty } in fields {
+ // Skip ZSTs
+ if ty.size() == 0 {
+ if ty.align() > 1 {
+ panic!("Zero-sized fields must have an alignment of `1`");
+ } else {
+ continue;
+ }
+ }
+ if mem::take(first).not() {
+ out!("\n");
+ }
+ self.emit_docs(ctx, docs, indent)?;
+ out!(
+ ("{};"),
+ ty.name_wrapping_var(self, name)
+ );
+ }
+ }
+ out!(("}} {full_ty_name};"));
+
+ out!("\n");
+ Ok(())
+ }
+
+ fn emit_opaque_type (
+ self: &'_ Self,
+ ctx: &'_ mut dyn Definer,
+ docs: Docs<'_>,
+ self_ty: &'_ dyn PhantomCType,
+ ) -> io::Result<()>
+ {
+ let ref indent = Indentation::new(4 /* ctx.indent_width() */);
+ mk_out!(indent, ctx.out());
+ let short_name = self_ty.short_name();
+ let full_ty_name = self_ty.name(self);
+
+ self.emit_docs(ctx, docs, indent)?;
+ out!(("typedef struct {short_name} {full_ty_name};"));
+
+ out!("\n");
+ Ok(())
+ }
+
+ fn emit_function (
+ self: &'_ Self,
+ ctx: &'_ mut dyn Definer,
+ docs: Docs<'_>,
+ fname: &'_ str,
+ args: &'_ [FunctionArg<'_>],
+ ret_ty: &'_ dyn PhantomCType,
+ ) -> io::Result<()>
+ {
+ let ref indent = Indentation::new(4 /* ctx.indent_width() */);
+
+ self.emit_docs(ctx, docs, indent)?;
+
+ let ref fn_sig_but_for_ret_type: String = {
+ let mut buf = Vec::::new();
+ mk_out!(indent, buf);
+
+ out!(
+ "\n{indent}{fn}{fname} (",
+ fn = if cfg!(feature = "c-headers-with-fn-style") {
+ "/* fn */ "
+ } else {
+ ""
+ },
+ );
+ let mut first = true;
+ if let _ = indent.scope() {
+ for arg in args {
+ if mem::take(&mut first).not() {
+ out!(",");
+ }
+ out!("\n{indent}{}", arg.ty.name_wrapping_var(self, arg.name))
+ }
+ if first {
+ out!("void");
+ }
+ }
+ out!(")");
+ String::from_utf8(buf).unwrap()
+ };
+
+ mk_out!(indent, ctx.out());
+ out!(
+ ("{};"), ret_ty.name_wrapping_var(self, fn_sig_but_for_ret_type)
+ );
+
+ out!("\n");
+ Ok(())
+ }
+
+ fn emit_constant (
+ self: &'_ Self,
+ ctx: &'_ mut dyn Definer,
+ docs: Docs<'_>,
+ name: &'_ str,
+ ty: &'_ dyn PhantomCType,
+ value: &'_ dyn ::core::fmt::Debug,
+ ) -> io::Result<()>
+ {
+ let ref indent = Indentation::new(4 /* ctx.indent_width() */);
+ mk_out!(indent, ctx.out());
+
+ self.emit_docs(ctx, docs, indent)?;
+ let ty = ty.name(self);
+ match ty.as_str() {
+ "int32_t" | "uint32_t" | "int16_t" | "uint16_t" | "int8_t" | "uint8_t" => {
+ out!(("static const {ty} {name} = {value:?};"));
+ },
+ _ => panic!("Lua doesn't support this const type: {}", ty),
+ }
+
+ out!("\n");
+ Ok(())
+ }
+}
\ No newline at end of file
diff --git a/src/headers/languages/mod.rs b/src/headers/languages/mod.rs
index f73c51fbea..6026b8aaa9 100644
--- a/src/headers/languages/mod.rs
+++ b/src/headers/languages/mod.rs
@@ -25,6 +25,11 @@ __cfg_python__! {
mod python;
}
+__cfg_lua__! {
+ pub use lua::Lua;
+ mod lua;
+}
+
pub
struct Indentation {
depth: ::core::cell::Cell,
diff --git a/src/headers/templates/lua/_prelude.lua b/src/headers/templates/lua/_prelude.lua
new file mode 100644
index 0000000000..b899289df3
--- /dev/null
+++ b/src/headers/templates/lua/_prelude.lua
@@ -0,0 +1,3 @@
+local ffi = require "ffi"
+
+ffi.cdef [[
diff --git a/src/headers/templates/lua/epilogue.lua b/src/headers/templates/lua/epilogue.lua
new file mode 100644
index 0000000000..cdf229759f
--- /dev/null
+++ b/src/headers/templates/lua/epilogue.lua
@@ -0,0 +1 @@
+]]
\ No newline at end of file
diff --git a/src/layout/_mod.rs b/src/layout/_mod.rs
index c46a586c80..206aeb0f74 100644
--- a/src/layout/_mod.rs
+++ b/src/layout/_mod.rs
@@ -119,6 +119,9 @@ impl CType for T {
| _case if language.is::() => {
::csharp_define_self(definer)
},
+ | _case if language.is::() => {
+ ::lua_define_self(definer)
+ },
#[cfg(feature = "python-headers")]
| _case if language.is::() => {
::c_define_self(definer)
@@ -149,6 +152,9 @@ impl CType for T {
let sep = if var_name.is_empty() { "" } else { " " };
format!("{}{sep}{var_name}", Self::csharp_ty())
},
+ | _case if language.is::() => {
+ ::lua_var(var_name)
+ },
#[cfg(feature = "python-headers")]
| _case if language.is::() => {
::c_var(var_name).to_string()
@@ -561,6 +567,21 @@ unsafe trait LegacyCType
)
}
}
+
+
+ __cfg_lua__! {
+ /// Extra typedef code (_e.g._ `[LayoutKind.Sequential] struct ...`)
+ fn lua_define_self (definer: &'_ mut dyn Definer)
+ -> io::Result<()>
+ ;
+
+ /// Convenience function for formatting `{ty} {var}` in Lua.
+ fn lua_var (var_name: &'_ str)
+ -> rust::String
+ {
+ Self::c_var(var_name).to_string()
+ }
+ }
}
}
diff --git a/src/layout/impls.rs b/src/layout/impls.rs
index d02646d07d..5d15966c02 100644
--- a/src/layout/impls.rs
+++ b/src/layout/impls.rs
@@ -81,150 +81,8 @@ const _: () = { macro_rules! impl_CTypes {
impl_CTypes! { @fns
(A9, A8, A7, A6, A5, A4, A3, A2, A1)
}
- #[cfg(docs)] impl_CTypes! { @arrays 1 2 } #[cfg(not(docs))]
- impl_CTypes! { @arrays
- // 0
- 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
- 26 27 28 29 30 31 32 40 48 50 60 64 70 75 80 90 96 100 125 128 192
- 200 250 256 300 400 500 512 600 700 750 800 900 1000 1024
- }
);
- (
- @arrays
- $($N:tt)*
- ) => ($(
- // LegacyCType
- /// Simplified for lighter documentation, but the actual impls
- /// range **from `1` up to `32`, plus a bunch of significant
- /// lengths up to `1024`**.
- unsafe // Safety: Rust arrays _are_ `#[repr(C)]`
- impl- LegacyCType
- for [Item; $N]
- { __cfg_headers__! {
- fn c_short_name_fmt (fmt: &'_ mut fmt::Formatter<'_>)
- -> fmt::Result
- {
- // item_N_array
- write!(fmt,
- concat!("{}_", stringify!($N), "_array"),
- Item::short_name(),
- )
- }
-
- fn c_define_self (definer: &'_ mut dyn Definer)
- -> io::Result<()>
- {
- let ref me = Self::c_var("").to_string();
- definer.define_once(
- me,
- &mut |definer| {
- Item::define_self(&crate::headers::languages::C, definer)?;
- writeln!(definer.out(),
- concat!(
- "typedef struct {{\n",
- " {inline_array};\n",
- "}} {me};\n",
- ),
- inline_array = Item::name_wrapping_var(&crate::headers::languages::C, concat!(
- "idx[", stringify!($N), "]",
- )),
- me = me,
- )
- }
- )
- }
-
- fn c_var_fmt (
- fmt: &'_ mut fmt::Formatter<'_>,
- var_name: &'_ str,
- ) -> fmt::Result
- {
- // _e.g._, item_N_array_t
- write!(fmt,
- "{}_t{sep}{}",
- Self::c_short_name(),
- var_name,
- sep = if var_name.is_empty() { "" } else { " " },
- )
- }
-
- __cfg_csharp__! {
- fn csharp_define_self (definer: &'_ mut dyn Definer)
- -> io::Result<()>
- {
- let ref me = Self::csharp_ty();
- Item::define_self(&crate::headers::languages::CSharp, definer)?;
- definer.define_once(me, &mut |definer| {
- let array_items = {
- // Poor man's specialization to use `fixed` arrays.
- if [
- "bool",
- "u8", "u16", "u32", "u64", "usize",
- "i8", "i16", "i32", "i64", "isize",
- "float", "double",
- ].contains(&::core::any::type_name::
- ())
- {
- format!(
- " public fixed {ItemTy} arr[{N}];\n",
- ItemTy = Item::name(&crate::headers::languages::CSharp),
- N = $N,
- // no need for a marshaler here
- )
- } else {
- // Sadly for the general case fixed arrays are
- // not supported.
- (0 .. $N)
- .map(|i| format!(
- " \
- {marshaler}\
- public {ItemTy} _{i};\n",
- ItemTy = Item::name(&crate::headers::languages::CSharp),
- i = i,
- marshaler =
- Item::csharp_marshaler()
- .map(|m| format!("[MarshalAs({})]\n ", m))
- .as_deref()
- .unwrap_or("")
- ,
- ))
- .collect::()
- }
- };
- writeln!(definer.out(),
- concat!(
- "[StructLayout(LayoutKind.Sequential, Size = {size})]\n",
- "public unsafe struct {me} {{\n",
- "{array_items}",
- "}}\n",
- ),
- me = me,
- array_items = array_items,
- size = mem::size_of::(),
- )
- })
- }
- }
- } type OPAQUE_KIND = OpaqueKind::Concrete; }
-
- // ReprC
- /// Simplified for lighter documentation, but the actual impls
- /// range **from `1` up to `32`, plus a bunch of significant
- /// lengths up to `1024`**.
- unsafe
- impl
- ReprC
- for [Item; $N]
- {
- type CLayout = [Item::CLayout; $N];
-
- #[inline]
- fn is_valid (it: &'_ Self::CLayout)
- -> bool
- {
- it.iter().all(Item::is_valid)
- }
- }
- )*);
(@fns
(
@@ -358,6 +216,17 @@ const _: () = { macro_rules! impl_CTypes {
Some("UnmanagedType.FunctionPtr".into())
}
}
+
+ __cfg_lua__! {
+ fn lua_define_self (definer: &'_ mut dyn Definer)
+ -> io::Result<()>
+ {
+ Ret::define_self(&crate::headers::languages::Lua, definer)?; $(
+ $An::define_self(&crate::headers::languages::Lua, definer)?; $(
+ $Ai::define_self(&crate::headers::languages::Lua, definer)?; )*)?
+ Ok(())
+ }
+ }
} type OPAQUE_KIND = OpaqueKind::Concrete; }
/// Simplified for lighter documentation, but the actual impls include
@@ -555,6 +424,15 @@ const _: () = { macro_rules! impl_CTypes {
$CSharpInt.into()
}
}
+
+ __cfg_lua__! {
+ fn lua_define_self (
+ _: &'_ mut dyn crate::headers::Definer,
+ ) -> io::Result<()>
+ {
+ Ok(())
+ }
+ }
} type OPAQUE_KIND = OpaqueKind::Concrete; }
from_CType_impl_ReprC! { $RustInt }
)*);
@@ -608,6 +486,15 @@ const _: () = { macro_rules! impl_CTypes {
$Cty.into()
}
}
+
+ __cfg_lua__! {
+ fn lua_define_self (
+ _: &'_ mut dyn crate::headers::Definer,
+ ) -> io::Result<()>
+ {
+ Ok(())
+ }
+ }
} type OPAQUE_KIND = OpaqueKind::Concrete; }
from_CType_impl_ReprC! { $fN }
)*);
@@ -666,6 +553,15 @@ const _: () = { macro_rules! impl_CTypes {
format!("{} /*const*/ *", T::name(&crate::headers::languages::CSharp))
}
}
+
+ __cfg_lua__! {
+ fn lua_define_self (definer: &'_ mut dyn $crate::headers::Definer)
+ -> $crate::ඞ::io::Result<()>
+ {
+ T::define_self(&crate::headers::languages::Lua, definer)?;
+ Ok(())
+ }
+ }
} type OPAQUE_KIND = OpaqueKind::Concrete; }
unsafe
@@ -724,6 +620,14 @@ const _: () = { macro_rules! impl_CTypes {
format!("{} *", T::name(&crate::headers::languages::CSharp))
}
}
+
+ __cfg_lua__! {
+ fn lua_define_self (definer: &'_ mut dyn $crate::headers::Definer)
+ -> $crate::ඞ::io::Result<()>
+ {
+ T::define_self(&crate::headers::languages::Lua, definer)
+ }
+ }
} type OPAQUE_KIND = OpaqueKind::Concrete; }
unsafe
impl ReprC
@@ -907,6 +811,15 @@ unsafe
"bool".into()
}
}
+
+ __cfg_lua__! {
+ fn lua_define_self (
+ _: &'_ mut dyn crate::headers::Definer,
+ ) -> io::Result<()>
+ {
+ Ok(())
+ }
+ }
}
type OPAQUE_KIND = OpaqueKind::Concrete;
@@ -984,6 +897,15 @@ unsafe
Some("UnmanagedType.SysInt".into())
}
}
+
+ __cfg_lua__! {
+ fn lua_define_self (
+ _: &'_ mut dyn crate::headers::Definer,
+ ) -> io::Result<()>
+ {
+ Ok(())
+ }
+ }
}
type OPAQUE_KIND = OpaqueKind::Concrete;
@@ -1201,3 +1123,145 @@ match_! {(
}
)*
)}}
+
+
+/// Arrays of const size `N`
+unsafe // Safety: Rust arrays _are_ `#[repr(C)]`
+impl
- LegacyCType
+ for [Item; N]
+{ __cfg_headers__! {
+ fn c_short_name_fmt (fmt: &'_ mut fmt::Formatter<'_>)
+ -> fmt::Result
+ {
+ // item_N_array
+ write!(fmt, "{}_{}_array", Item::short_name(), N)
+ }
+
+ fn c_define_self (definer: &'_ mut dyn Definer)
+ -> io::Result<()>
+ {
+ let ref me = Self::c_var("").to_string();
+ definer.define_once(
+ me,
+ &mut |definer| {
+ Item::define_self(&crate::headers::languages::C, definer)?;
+ writeln!(definer.out(),
+ concat!(
+ "typedef struct {{\n",
+ " {inline_array};\n",
+ "}} {me};\n",
+ ),
+ inline_array = Item::name_wrapping_var(
+ &crate::headers::languages::C,
+ &format!("idx[{}]", N)
+ ),
+ me = me,
+ )
+ }
+ )
+ }
+
+ fn c_var_fmt (
+ fmt: &'_ mut fmt::Formatter<'_>,
+ var_name: &'_ str,
+ ) -> fmt::Result
+ {
+ // _e.g._, item_N_array_t
+ write!(fmt,
+ "{}_t{sep}{}",
+ Self::c_short_name(),
+ var_name,
+ sep = if var_name.is_empty() { "" } else { " " },
+ )
+ }
+
+ __cfg_csharp__! {
+ fn csharp_define_self (definer: &'_ mut dyn Definer)
+ -> io::Result<()>
+ {
+ let ref me = Self::csharp_ty();
+ Item::define_self(&crate::headers::languages::CSharp, definer)?;
+ definer.define_once(me, &mut |definer| {
+ let array_items = {
+ // Poor man's specialization to use `fixed` arrays.
+ if [
+ "bool",
+ "u8", "u16", "u32", "u64", "usize",
+ "i8", "i16", "i32", "i64", "isize",
+ "float", "double",
+ ].contains(&::core::any::type_name::
- ())
+ {
+ format!(
+ " public fixed {ItemTy} arr[{N}];\n",
+ ItemTy = Item::name(&crate::headers::languages::CSharp),
+ N = N,
+ // no need for a marshaler here
+ )
+ } else {
+ // Sadly for the general case fixed arrays are
+ // not supported.
+ (0 .. N)
+ .map(|i| format!(
+ " \
+ {marshaler}\
+ public {ItemTy} _{i};\n",
+ ItemTy = Item::name(&crate::headers::languages::CSharp),
+ i = i,
+ marshaler =
+ Item::csharp_marshaler()
+ .map(|m| format!("[MarshalAs({})]\n ", m))
+ .as_deref()
+ .unwrap_or("")
+ ,
+ ))
+ .collect::()
+ }
+ };
+ writeln!(definer.out(),
+ concat!(
+ "[StructLayout(LayoutKind.Sequential, Size = {size})]\n",
+ "public unsafe struct {me} {{\n",
+ "{array_items}",
+ "}}\n",
+ ),
+ me = me,
+ array_items = array_items,
+ size = mem::size_of::(),
+ )
+ })
+ }
+ }
+
+ __cfg_lua__! {
+ fn lua_define_self (definer: &'_ mut dyn Definer)
+ -> io::Result<()>
+ {
+ Item::define_self(&crate::headers::languages::Lua, definer)
+ }
+
+ fn lua_var(var_name: &'_ str) -> rust::String {
+ let item_name = Item::name(&crate::headers::languages::Lua);
+ let sep = if var_name.is_empty() { "" } else { " " };
+ let (base_type, dimensions) = match item_name.find('[') {
+ Some(index) => (&item_name[0..index], &item_name[index..]),
+ None => (item_name.as_str(), ""),
+ };
+
+ format!("{base_type}{sep}{var_name}[{N}]{dimensions}")
+ }
+ }
+} type OPAQUE_KIND = OpaqueKind::Concrete; }
+
+unsafe
+impl
- ReprC
+ for [Item; N]
+{
+ type CLayout = [Item::CLayout; N];
+
+ #[inline]
+ fn is_valid (it: &'_ Self::CLayout)
+ -> bool
+ {
+ it.iter().all(Item::is_valid)
+ }
+}
\ No newline at end of file
diff --git a/src/layout/macros.rs b/src/layout/macros.rs
index 1638d523ad..d40088f636 100644
--- a/src/layout/macros.rs
+++ b/src/layout/macros.rs
@@ -61,6 +61,22 @@ macro_rules! __cfg_python__ {(
// Nothing
)}
+#[cfg(feature = "headers")]
+#[macro_export] #[doc(hidden)]
+macro_rules! __cfg_lua__ {(
+ $($item:item)*
+) => (
+ $($item)*
+)}
+
+#[cfg(not(feature = "headers"))]
+#[macro_export] #[doc(hidden)]
+macro_rules! __cfg_lua__ {(
+ $($item:item)*
+) => (
+ // Nothing
+)}
+
/// Safely implement [`CType`][`trait@crate::layout::LegacyCType`]
/// for a `#[repr(C)]` struct **when all its fields are `CType`**.
///
diff --git a/src/proc_macro/derives/c_type/struct_.rs b/src/proc_macro/derives/c_type/struct_.rs
index 655971138c..a5249486fa 100644
--- a/src/proc_macro/derives/c_type/struct_.rs
+++ b/src/proc_macro/derives/c_type/struct_.rs
@@ -56,6 +56,9 @@ fn derive (
let EachGenericTy =
generics.type_params().map(|it| &it.ident)
;
+ let EachConstParam =
+ generics.const_params().map(|param| ¶m.ident)
+ ;
let ref EachFieldTy =
fields.iter().vmap(|Field { ty, .. }| ty)
;
@@ -70,17 +73,12 @@ fn derive (
fn short_name ()
-> #ඞ::String
{
- let mut _ret =
- <#ඞ::String as #ඞ::From<_>>::from(#StructName_str)
- ;
+ let mut _ret = #ඞ::format!("{}", #StructName_str);
+ #(
+ _ret.push_str(ඞ::format!("_{}", <#CLayoutOf<#EachGenericTy> as #CType>::short_name()));
+ )*
#(
- #ඞ::fmt::Write::write_fmt(
- &mut _ret,
- #ඞ::format_args!(
- "_{}",
- <#CLayoutOf<#EachGenericTy> as #CType>::short_name(),
- ),
- ).unwrap();
+ _ret.push_str(ඞ::format!("_{}", #EachConstParam));
)*
_ret
}
diff --git a/src/proc_macro/ffi_export/const_.rs b/src/proc_macro/ffi_export/const_.rs
index 18744e5c17..6624174741 100644
--- a/src/proc_macro/ffi_export/const_.rs
+++ b/src/proc_macro/ffi_export/const_.rs
@@ -49,6 +49,7 @@ fn handle (
match lang {
| Language::C => &languages::C,
| Language::CSharp => &languages::CSharp,
+ | Language::Lua => &languages::Lua,
| Language::Python => &languages::Python,
}
;
diff --git a/src/slice.rs b/src/slice.rs
index a275783610..2676361b9c 100644
--- a/src/slice.rs
+++ b/src/slice.rs
@@ -737,3 +737,15 @@ const _: () = {
}
}
};
+
+impl<'lt> slice_ref<'lt, u8> {
+ pub fn try_as_str(&'lt self) -> Result<&'lt str, std::str::Utf8Error> {
+ std::str::from_utf8(self.as_slice())
+ }
+}
+
+impl<'lt> From<&'lt str> for slice_ref<'lt, u8> {
+ fn from(s: &'lt str) -> Self {
+ s.as_bytes().into()
+ }
+}
\ No newline at end of file
diff --git a/src/string/slice.rs b/src/string/slice.rs
index 10dbace571..845adab11c 100644
--- a/src/string/slice.rs
+++ b/src/string/slice.rs
@@ -155,6 +155,15 @@ impl<'lt> str_ref<'lt> {
)
}
}
+
+ #[inline]
+ pub
+ fn try_as_str(self: str_ref<'lt>) -> Result<&'lt str, core::str::Utf8Error> {
+ core::str::from_utf8(unsafe { slice::from_raw_parts(
+ self.0.as_ptr(),
+ self.0.len(),
+ )})
+ }
}
impl<'lt> Deref
@@ -189,4 +198,4 @@ impl fmt::Debug
{
::fmt(self, fmt)
}
-}
+}
\ No newline at end of file
diff --git a/src/tuple.rs b/src/tuple.rs
index d7ee1709a8..d29344cd83 100644
--- a/src/tuple.rs
+++ b/src/tuple.rs
@@ -60,6 +60,15 @@ impl LegacyCType
"void".into()
}
}
+
+ __cfg_lua__! {
+ fn lua_define_self (
+ _: &'_ mut dyn crate::headers::Definer,
+ ) -> io::Result<()>
+ {
+ Ok(())
+ }
+ }
} type OPAQUE_KIND = crate::layout::OpaqueKind::Concrete; }
from_CType_impl_ReprC! { CVoid }
diff --git a/tests/layout_macros.rs b/tests/layout_macros.rs
index a6f7046ea0..d0ee46c084 100644
--- a/tests/layout_macros.rs
+++ b/tests/layout_macros.rs
@@ -320,6 +320,7 @@ fn generate_headers ()
in &[
C,
CSharp,
+ Lua,
]
{
::safer_ffi::headers::builder()