Skip to main content

low_luajit/
lib.rs

1//! A lightweight library that aims to add low level but safe (as much as possible) bindings to the LuaJIT C API with no dependencies and no-std support.
2//!
3//! These bindings can be used to create dynamic Rust libraries that can be loaded and ran from LuaJIT like any other Lua module.
4//!
5//! Check out the [examples directory] for some examples.
6//!
7//! # Tutorial
8//!
9//! *(Replace instances of `crate_name` with the name of your crate!)*
10//!
11//! To create your module's entry point, add a special function named `luaopen_crate_name`:
12//!
13//! ```
14//! use low_luajit::{LuaState, AmountReturned};
15//!
16//! #[unsafe(no_mangle)]
17//! extern "C-unwind" fn luaopen_crate_name(mut lua: LuaState) -> AmountReturned {
18//!     // ...
19//!     return lua.return_any(1); // Can be 0, but anything higher than 1 will be discarded.
20//! }
21//! ```
22//!
23//! This is the function that will be called when Lua loads your module.
24//! It can return a value back to Lua which will be cached like any other module.
25//!
26//! While you can choose to return any value (including none), modules usually return a table of various functions your module can do.
27//! `low_luajit` has some functions to ease this process:
28//!
29//! ```
30//! use low_luajit::{LuaState, AmountReturned, reg};
31//!
32//! extern "C-unwind" fn foo(lua: LuaState) -> AmountReturned {
33//!     // ...
34//! # AmountReturned::NONE
35//! }
36//!
37//! extern "C-unwind" fn bar(lua: LuaState) -> AmountReturned {
38//!     // ...
39//! # AmountReturned::NONE
40//! }
41//!
42//! #[unsafe(no_mangle)]
43//! extern "C-unwind" fn luaopen_crate_name(lua: LuaState) -> AmountReturned {
44//!     lua.return_module(reg![foo, bar])
45//! }
46//! ```
47//!
48//! This above code will have your module be returned as a table containing the functions `foo` and `bar`, that Lua can call directly!
49//!
50//! Once your module is ready to be compiled, make sure to configure your crate to be of type `cdylib`:
51//!
52//! ```toml
53//! [package]
54//! name = "crate_name"
55//! # ...
56//!
57//! [lib]
58//! crate-type = ["cdylib"]
59//!
60//! [dependencies]
61//! low_luajit = "0.1"
62//! # ...
63//! ```
64//!
65//! And you're good to go!
66//! Move the resulting library file (`.dll` on Windows, `.so` on Linux)
67//! somewhere in the Lua code and you can now load and use your Rust library as a Lua module!
68//!
69//! ```lua
70//! local crate_name = require("crate_name")
71//!
72//! crate_name.foo()
73//! crate_name.bar()
74//! ```
75//!
76//! To check out how you can interact with the LuaJIT API further to continue implementing your module, see [`LuaState`].
77//!
78//! For some example modules to use as reference, check out the [examples directory].
79//!
80//! ## Linux addendum
81//!
82//! When compiling dynamic libraries on Linux, Cargo adds a `lib` prefix to the outputted file (`libcrate_name.so`),
83//! which trips Lua's default path solver.
84//!
85//! To resolve this, you can either modify the `package.cpath` variable:
86//!
87//! ```lua
88//! package.cpath = "./lib?.so;" .. package.cpath
89//! ```
90//!
91//! Or simply manually rename the file to `crate_name.so`.
92//!
93//! ## Windows addendum
94//!
95//! Windows is, for whatever reason, more strict on dynamic libraries, requiring symbols to be bound to a specific `.dll` name.
96//!
97//! The LuaJIT docs account for this:
98//!
99//! > Since Windows symbols are bound to a specific DLL name,
100//! > you need to link to the lua51.dll created by the LuaJIT build (do not rename the DLL).
101//! > You may link LuaJIT statically on Windows only if you don't intend to load Lua/C modules at runtime.
102//!
103//! Following this, when compiling on Windows the symbols are binded to `lua51.dll`,
104//! which should work for any program that is compliant with the LuaJIT docs and intends to allow loading external modules at runtime.
105//!
106//! # Error handling
107//!
108//! When Lua errors, the error is propagated upwards using a long jump,
109//! unwinding through any Rust or Lua code until it's caught by the protected environment
110//! (where it will be handled by whoever created said environment).
111//!
112//! *(You can create protected environments using [`LuaState::pcall`] or [`LuaState::cpcall`].)*
113//!
114//! If a long jump happens, any running Rust function will be left unfinished (any variable left will not be dropped),
115//! but it shouldn't cause any undefined behavior.
116//!
117//! When Lua errors outside of a protected environment, Rust should be able to catch the error and abort
118//! (though Lua programs normally always run within a protected environment).
119//!
120//! Rust panics behave mostly as usual, just often failing to fully unwind and thus aborting instead, which should be harmless.
121//!
122//! Functions that may trigger Lua errors will clarify the error conditions in their description.
123//! *(Except unlikely but possible stack/table overflows,
124//! just assume that any function that adds more elements without removing some can overflow if called many many times.)*
125//!
126//! If a function may trigger a Lua error in an undocumented way, let me know and I'll add the missing docs!
127//!
128//! # Planned features
129//!
130//! The main objective is a wrapper for the LuaJIT C API that tries to strike a good (but subjective) balance between lightweight and safe.
131//! Some C functions/types still need to be safely exposed currently.
132//!
133//! Following that, some slightly higher level utilities to ease some important yet repetitive tasks could be added,
134//! but they would still have to directly interact with the LuaJIT C API, with little overhead; a few ideas could be:
135//! - Simple iterator structs that just call the functions needed to properly iterate a table for you.
136//! - Potentially a safe way to use this library to just run LuaJIT within Rust instead (currently only possible via [`ffi`])?
137//! - Safe versions of the functions that can panic (or alternatively a safe wrapper around any function/closure)?
138//!
139//! If you'd like to create high level bindings instead, feel free to use this library as your base/dependency!
140//! *Just follow the license...*
141//!
142//! [examples directory]: https://git.blossomdevs.com/Rosy.iso/low-luajit/src/branch/main/examples
143
144#![no_std]
145
146pub mod ffi;
147mod lua;
148
149/// Internal auxiliary macro for mapping pointers to an `Option`.
150macro_rules! map_ptr {
151	($ptr:expr, $some:expr) => { if $ptr.is_null() { None } else { Some($some) } };
152}
153
154pub(crate) use map_ptr;
155
156/// Auxiliary conversion methods for [`CFunction`] and [`Hook`].
157///
158/// *([`CFunction`] and [`Hook`] are simple type definitions, so methods can't be implemented directly...)*
159pub trait SafeFn {
160	/// The raw version of this function type.
161	type RawFn;
162
163	/// Converts the given raw function into their safe version.
164	///
165	/// # Safety
166	///
167	/// `raw` must always be safe to call.
168	unsafe fn from_raw(raw: Self::RawFn) -> Self;
169
170	/// Converts this function into their raw version.
171	///
172	/// The returned function is safe to call as long as the function receives valid pointers for any pointer argument it may have.
173	fn to_raw(self) -> Self::RawFn;
174}
175
176pub use lua::{LuaState, LuaType, CFunction, AmountReturned, PCallError, LoadError, Writer, Reg, Hook, HookEvent, DebugInfo, /*buffer::StringBuffer*/};