use std::borrow::{Borrow, Cow};
use std::error::Error;
use std::ffi::{CStr, CString};
use std::fmt;
use std::io::Error as IoError;
use std::io::Read;
use std::io::{self, Write};
use std::num::NonZeroI32;
pub use ::tlua_derive::*;
pub use any::{AnyHashableLuaValue, AnyLuaString, AnyLuaValue};
pub use cdata::{AsCData, CData, CDataOnStack};
pub use functions_write::{
function0, function1, function10, function2, function3, function4, function5, function6,
function7, function8, function9, protected_call, Function, InsideCallback, Throw,
};
pub use lua_functions::LuaFunction;
pub use lua_functions::{LuaCode, LuaCodeFromReader};
pub use lua_tables::{LuaTable, LuaTableIterator};
pub use object::{
Call, CallError, Callable, Index, Indexable, IndexableRW, MethodCallError, NewIndex, Object,
};
pub use rust_tables::{PushIterError, PushIterErrorOf, TableFromIter};
pub use tuples::{AsTable, TuplePushError};
pub use userdata::UserdataOnStack;
pub use userdata::{push_some_userdata, push_userdata, read_userdata};
pub use values::{False, Nil, Null, Strict, StringInLua, ToString, True, Typename};
#[deprecated = "Use `CallError` instead"]
pub type LuaFunctionCallError<E> = CallError<E>;
pub type LuaTableMap = std::collections::HashMap<AnyHashableLuaValue, AnyLuaValue>;
pub type LuaSequence = Vec<AnyLuaValue>;
mod any;
mod cdata;
pub mod debug;
pub mod ffi;
mod functions_write;
mod lua_functions;
mod lua_tables;
mod macros;
mod object;
mod rust_tables;
mod tuples;
mod userdata;
pub mod util;
mod values;
pub type LuaState = *mut ffi::lua_State;
pub type StaticLua = Lua<on_drop::Ignore>;
pub type TempLua = Lua<on_drop::Close>;
pub type LuaThread = Lua<on_drop::Unref>;
#[derive(Debug)]
pub struct Lua<OnDrop>
where
OnDrop: on_drop::OnDrop,
{
lua: LuaState,
on_drop: OnDrop,
}
mod on_drop {
use crate::{ffi, LuaState};
pub trait OnDrop {
fn on_drop(&mut self, l: LuaState);
}
#[derive(Debug)]
pub struct Ignore;
impl OnDrop for Ignore {
fn on_drop(&mut self, _: LuaState) {}
}
#[derive(Debug)]
pub struct Close;
impl OnDrop for Close {
fn on_drop(&mut self, l: LuaState) {
unsafe { ffi::lua_close(l) }
}
}
#[derive(Debug)]
pub struct Unref(pub i32);
impl OnDrop for Unref {
fn on_drop(&mut self, l: LuaState) {
unsafe { ffi::luaL_unref(l, ffi::LUA_REGISTRYINDEX, self.0) }
}
}
}
pub struct PushGuard<L>
where
L: AsLua,
{
lua: L,
top: i32,
size: i32,
}
impl<L> std::fmt::Debug for PushGuard<L>
where
L: AsLua,
L: std::fmt::Debug,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let start = unsafe {
AbsoluteIndex::new_unchecked(NonZeroI32::new(self.top - self.size + 1).unwrap())
};
f.debug_struct("PushGuard")
.field("lua", &self.lua)
.field("size", &self.size)
.field(
"lua_type",
&typenames(self.lua.as_lua(), start, self.size as _),
)
.finish()
}
}
impl<L: AsLua> PushGuard<L> {
#[inline]
pub unsafe fn new(lua: L, size: i32) -> Self {
PushGuard {
top: ffi::lua_gettop(lua.as_lua()),
lua,
size: size as _,
}
}
#[inline]
pub fn assert_one_and_forget(self) -> i32 {
assert_eq!(self.size, 1);
self.forget_internal()
}
#[inline]
pub fn size(&self) -> i32 {
self.size
}
#[inline]
pub unsafe fn forget(self) -> i32 {
self.forget_internal()
}
#[inline]
fn forget_internal(mut self) -> i32 {
let size = self.size;
self.size = 0;
size
}
#[inline]
pub fn into_inner(self) -> L {
use std::{
mem::{self, MaybeUninit},
ptr,
};
let mut res = MaybeUninit::uninit();
unsafe {
ptr::copy_nonoverlapping(&self.lua, res.as_mut_ptr(), 1);
if self.size != 0 {
ffi::lua_pop(self.lua.as_lua(), self.size as _);
}
};
mem::forget(self);
unsafe { res.assume_init() }
}
}
pub trait AsLua {
fn as_lua(&self) -> *mut ffi::lua_State;
#[inline(always)]
fn try_push<T>(self, v: T) -> Result<PushGuard<Self>, (<T as PushInto<Self>>::Err, Self)>
where
Self: Sized,
T: PushInto<Self>,
{
v.push_into_lua(self)
}
#[inline(always)]
fn push<T>(self, v: T) -> PushGuard<Self>
where
Self: Sized,
T: PushInto<Self>,
<T as PushInto<Self>>::Err: Into<Void>,
{
v.push_into_no_err(self)
}
#[inline(always)]
fn try_push_one<T>(self, v: T) -> Result<PushGuard<Self>, (<T as PushInto<Self>>::Err, Self)>
where
Self: Sized,
T: PushOneInto<Self>,
{
v.push_into_lua(self)
}
#[inline(always)]
fn push_one<T>(self, v: T) -> PushGuard<Self>
where
Self: Sized,
T: PushOneInto<Self>,
<T as PushInto<Self>>::Err: Into<Void>,
{
v.push_into_no_err(self)
}
#[inline(always)]
fn push_iter<I>(self, iterator: I) -> Result<PushGuard<Self>, Self>
where
Self: Sized,
I: Iterator,
<I as Iterator>::Item: PushInto<LuaState>,
<<I as Iterator>::Item as PushInto<LuaState>>::Err: Into<Void>,
{
rust_tables::push_iter(self, iterator).map_err(|(_, lua)| lua)
}
#[inline(always)]
fn try_push_iter<I>(self, iterator: I) -> Result<PushGuard<Self>, (PushIterErrorOf<I>, Self)>
where
Self: Sized,
I: Iterator,
<I as Iterator>::Item: PushInto<LuaState>,
{
rust_tables::push_iter(self, iterator)
}
#[inline(always)]
fn read<T>(self) -> Result<T, Self>
where
Self: Sized,
T: LuaRead<Self>,
{
T::lua_read(self)
}
#[inline(always)]
fn read_at<T>(self, index: i32) -> Result<T, Self>
where
Self: Sized,
T: LuaRead<Self>,
{
T::lua_read_at_maybe_zero_position(self, index)
}
#[inline(always)]
fn read_at_nz<T>(self, index: NonZeroI32) -> Result<T, Self>
where
Self: Sized,
T: LuaRead<Self>,
{
T::lua_read_at_position(self, index)
}
#[track_caller]
#[inline(always)]
fn pcall<F, R>(&self, f: F) -> Result<R, LuaError>
where
F: FnOnce(StaticLua) -> R,
{
protected_call(self, f)
}
}
impl<T> AsLua for &'_ T
where
T: ?Sized + AsLua,
{
fn as_lua(&self) -> *mut ffi::lua_State {
T::as_lua(self)
}
}
impl<D> AsLua for Lua<D>
where
D: on_drop::OnDrop,
{
#[inline]
fn as_lua(&self) -> *mut ffi::lua_State {
self.lua
}
}
impl AsLua for *mut ffi::lua_State {
fn as_lua(&self) -> *mut ffi::lua_State {
*self
}
}
impl<L> AsLua for PushGuard<L>
where
L: AsLua,
{
#[inline]
fn as_lua(&self) -> *mut ffi::lua_State {
self.lua.as_lua()
}
}
pub type PushResult<L, P> = Result<PushGuard<L>, (<P as Push<L>>::Err, L)>;
pub trait Push<L: AsLua> {
type Err;
fn push_to_lua(&self, lua: L) -> Result<PushGuard<L>, (Self::Err, L)>;
#[inline]
fn push_no_err(&self, lua: L) -> PushGuard<L>
where
<Self as Push<L>>::Err: Into<Void>,
{
match self.push_to_lua(lua) {
Ok(p) => p,
Err(_) => unreachable!("no way to instantiate Void"),
}
}
}
impl<T, L> Push<L> for &'_ T
where
L: AsLua,
T: ?Sized,
T: Push<L>,
{
type Err = T::Err;
fn push_to_lua(&self, lua: L) -> Result<PushGuard<L>, (Self::Err, L)> {
T::push_to_lua(*self, lua)
}
}
pub trait PushOne<L: AsLua>: Push<L> {}
impl<T, L> PushOne<L> for &'_ T
where
L: AsLua,
T: ?Sized,
T: PushOne<L>,
{
}
pub type PushIntoResult<L, P> = Result<PushGuard<L>, (<P as PushInto<L>>::Err, L)>;
pub trait PushInto<L>
where
L: AsLua,
{
type Err;
fn push_into_lua(self, lua: L) -> Result<PushGuard<L>, (Self::Err, L)>;
#[inline]
fn push_into_no_err(self, lua: L) -> PushGuard<L>
where
Self: Sized,
<Self as PushInto<L>>::Err: Into<Void>,
{
match self.push_into_lua(lua) {
Ok(p) => p,
Err(_) => unreachable!("no way to instantiate Void"),
}
}
}
impl<T, L> PushInto<L> for &'_ T
where
L: AsLua,
T: ?Sized,
T: Push<L>,
{
type Err = T::Err;
fn push_into_lua(self, lua: L) -> Result<PushGuard<L>, (Self::Err, L)> {
self.push_to_lua(lua)
}
}
pub trait PushOneInto<L: AsLua>: PushInto<L> {}
impl<T, L> PushOneInto<L> for &'_ T
where
L: AsLua,
T: ?Sized,
T: PushOne<L>,
{
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum Void {}
impl fmt::Display for Void {
fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result {
unreachable!("Void cannot be instantiated")
}
}
pub const NEGATIVE_ONE: NonZeroI32 = unsafe { NonZeroI32::new_unchecked(-1) };
pub const NEGATIVE_TWO: NonZeroI32 = unsafe { NonZeroI32::new_unchecked(-2) };
pub trait LuaRead<L>: Sized {
#[inline(always)]
fn n_values_expected() -> i32 {
1
}
#[inline]
fn lua_read(lua: L) -> Result<Self, L> {
let index = NonZeroI32::new(-Self::n_values_expected()).expect("Invalid n_values_expected");
Self::lua_read_at_position(lua, index)
}
fn lua_read_at_maybe_zero_position(lua: L, index: i32) -> Result<Self, L> {
if let Some(index) = NonZeroI32::new(index) {
Self::lua_read_at_position(lua, index)
} else {
Err(lua)
}
}
fn lua_read_at_position(lua: L, index: NonZeroI32) -> Result<Self, L>;
}
impl<L: AsLua> LuaRead<L> for LuaState {
fn lua_read_at_maybe_zero_position(lua: L, _: i32) -> Result<Self, L> {
Ok(lua.as_lua())
}
fn lua_read_at_position(lua: L, _: NonZeroI32) -> Result<Self, L> {
Ok(lua.as_lua())
}
}
#[derive(Debug)]
pub enum LuaError {
SyntaxError(String),
ExecutionError(Cow<'static, str>),
ReadError(IoError),
WrongType {
when: &'static str,
rust_expected: String,
lua_actual: String,
},
}
impl LuaError {
pub fn wrong_type_returned<T, L: AsLua>(lua: L, n_values: i32) -> Self {
Self::wrong_type::<T, _>(lua, n_values, "Wrong type returned by Lua")
}
pub fn wrong_type_passed<T, L: AsLua>(lua: L, n_values: i32) -> Self {
Self::wrong_type::<T, _>(lua, n_values, "Wrong type passed into rust callback")
}
pub fn wrong_type<T, L: AsLua>(lua: L, n_values: i32, when: &'static str) -> Self {
let nz = unsafe { NonZeroI32::new_unchecked(-n_values) };
let start = AbsoluteIndex::new(nz, lua.as_lua());
Self::WrongType {
when,
rust_expected: std::any::type_name::<T>().into(),
lua_actual: typenames(lua, start, n_values as _),
}
}
}
pub fn typename(lua: impl AsLua, index: i32) -> &'static CStr {
unsafe {
let lua_type = ffi::lua_type(lua.as_lua(), index);
let typename = ffi::lua_typename(lua.as_lua(), lua_type);
CStr::from_ptr(typename)
}
}
pub fn typenames(lua: impl AsLua, start: AbsoluteIndex, count: u32) -> String {
let l_ptr = lua.as_lua();
let single_typename = |i| typename(l_ptr, i as _).to_string_lossy();
let start = start.get();
match count {
0 => return "()".into(),
1 => return single_typename(start).into_owned(),
_ => {}
}
let mut res = Vec::with_capacity(32);
write!(res, "(").expect("writing to vec cannot fail");
let end = start + count - 1;
for i in start..end {
write!(res, "{}, ", single_typename(i)).expect("writing to vec cannot fail");
}
write!(res, "{})", single_typename(end)).expect("writing to vec cannot fail");
unsafe { String::from_utf8_unchecked(res) }
}
impl fmt::Display for LuaError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use LuaError::*;
match self {
SyntaxError(s) => write!(f, "Syntax error: {}", s),
ExecutionError(s) => write!(f, "Execution error: {}", s),
ReadError(e) => write!(f, "Read error: {}", e),
WrongType {
when,
rust_expected,
lua_actual,
} => {
write!(
f,
"{}: {} expected, got {}",
when, rust_expected, lua_actual
)
}
}
}
}
impl Error for LuaError {
fn description(&self) -> &str {
use LuaError::*;
match *self {
SyntaxError(ref s) => s,
ExecutionError(ref s) => s,
ReadError(_) => "read error",
WrongType { when, .. } => when,
}
}
fn cause(&self) -> Option<&dyn Error> {
use LuaError::*;
match *self {
SyntaxError(_) => None,
ExecutionError(_) => None,
ReadError(ref e) => Some(e),
WrongType { .. } => None,
}
}
}
impl From<io::Error> for LuaError {
fn from(e: io::Error) -> Self {
LuaError::ReadError(e)
}
}
impl TempLua {
#[track_caller]
#[inline]
pub fn new() -> Self {
let lua = unsafe { ffi::luaL_newstate() };
if lua.is_null() {
panic!("lua_newstate failed");
}
extern "C" fn panic(lua: *mut ffi::lua_State) -> libc::c_int {
let err = unsafe { ffi::lua_tostring(lua, -1) };
let err = unsafe { CStr::from_ptr(err) };
let err = String::from_utf8(err.to_bytes().to_vec()).unwrap();
panic!("PANIC: unprotected error in call to Lua API ({})\n", err);
}
unsafe { ffi::lua_atpanic(lua, panic) };
unsafe { Self::from_existing(lua) }
}
#[inline]
pub unsafe fn from_existing<T>(lua: *mut T) -> Self {
Self {
lua: lua as _,
on_drop: on_drop::Close,
}
}
}
impl StaticLua {
#[inline]
pub unsafe fn from_static<T>(lua: *mut T) -> Self {
Self {
lua: lua as _,
on_drop: on_drop::Ignore,
}
}
pub fn new_thread(&self) -> LuaThread {
unsafe {
let lua = ffi::lua_newthread(self.as_lua());
let r = ffi::luaL_ref(self.as_lua(), ffi::LUA_REGISTRYINDEX);
LuaThread {
lua,
on_drop: on_drop::Unref(r),
}
}
}
}
impl<L: AsLua> LuaRead<L> for StaticLua {
fn lua_read_at_maybe_zero_position(lua: L, _: i32) -> Result<Self, L> {
Ok(Self {
lua: lua.as_lua(),
on_drop: on_drop::Ignore,
})
}
fn lua_read_at_position(lua: L, _: NonZeroI32) -> Result<Self, L> {
Ok(Self {
lua: lua.as_lua(),
on_drop: on_drop::Ignore,
})
}
}
impl<OnDrop> Lua<OnDrop>
where
OnDrop: on_drop::OnDrop,
{
#[inline]
pub fn openlibs(&self) {
unsafe { ffi::luaL_openlibs(self.lua) }
}
#[inline]
pub fn open_base(&self) {
unsafe { ffi::luaopen_base(self.lua) }
}
#[inline]
pub fn open_bit(&self) {
unsafe { ffi::luaopen_bit(self.lua) }
}
#[inline]
pub fn open_debug(&self) {
unsafe { ffi::luaopen_debug(self.lua) }
}
#[inline]
pub fn open_io(&self) {
unsafe { ffi::luaopen_io(self.lua) }
}
#[inline]
pub fn open_math(&self) {
unsafe { ffi::luaopen_math(self.lua) }
}
#[inline]
pub fn open_os(&self) {
unsafe { ffi::luaopen_os(self.lua) }
}
#[inline]
pub fn open_package(&self) {
unsafe { ffi::luaopen_package(self.lua) }
}
#[inline]
pub fn open_string(&self) {
unsafe { ffi::luaopen_string(self.lua) }
}
#[inline]
pub fn open_table(&self) {
unsafe { ffi::luaopen_table(self.lua) }
}
#[track_caller]
#[inline(always)]
pub fn eval<'lua, T>(&'lua self, code: &str) -> Result<T, LuaError>
where
T: LuaRead<PushGuard<LuaFunction<PushGuard<&'lua Self>>>>,
{
LuaFunction::load(self, code)?.into_call()
}
#[track_caller]
#[inline(always)]
pub fn eval_with<'lua, A, T>(&'lua self, code: &str, args: A) -> Result<T, CallError<A::Err>>
where
A: PushInto<LuaState>,
T: LuaRead<PushGuard<LuaFunction<PushGuard<&'lua Self>>>>,
{
LuaFunction::load(self, code)?.into_call_with_args(args)
}
#[track_caller]
#[inline(always)]
pub fn exec(&self, code: &str) -> Result<(), LuaError> {
LuaFunction::load(self, code)?.into_call()
}
#[track_caller]
#[inline(always)]
pub fn exec_with<A>(&self, code: &str, args: A) -> Result<(), CallError<A::Err>>
where
A: PushInto<LuaState>,
{
LuaFunction::load(self, code)?.into_call_with_args(args)
}
#[track_caller]
#[inline(always)]
pub fn eval_from<'lua, T>(&'lua self, code: impl Read) -> Result<T, LuaError>
where
T: LuaRead<PushGuard<LuaFunction<PushGuard<&'lua Self>>>>,
{
LuaFunction::load_from_reader(self, code)?.into_call()
}
#[track_caller]
#[inline(always)]
pub fn exec_from(&self, code: impl Read) -> Result<(), LuaError> {
LuaFunction::load_from_reader(self, code)?.into_call()
}
#[inline]
pub fn get<'lua, V, I>(&'lua self, index: I) -> Option<V>
where
I: Borrow<str>,
V: LuaRead<PushGuard<&'lua Self>>,
{
let index = CString::new(index.borrow()).unwrap();
unsafe {
ffi::lua_getglobal(self.lua, index.as_ptr());
V::lua_read(PushGuard::new(self, 1)).ok()
}
}
#[inline]
pub fn into_get<V, I>(self, index: I) -> Result<V, PushGuard<Self>>
where
I: Borrow<str>,
V: LuaRead<PushGuard<Self>>,
{
let index = CString::new(index.borrow()).unwrap();
unsafe {
ffi::lua_getglobal(self.lua, index.as_ptr());
V::lua_read(PushGuard::new(self, 1))
}
}
#[inline]
pub fn set<'lua, I, V>(&'lua self, index: I, value: V)
where
I: Borrow<str>,
V: PushOneInto<&'lua Self>,
<V as PushInto<&'lua Self>>::Err: Into<Void>,
{
match self.checked_set(index, value) {
Ok(_) => (),
Err(_) => unreachable!(),
}
}
#[inline]
pub fn checked_set<'lua, I, V>(
&'lua self,
index: I,
value: V,
) -> Result<(), <V as PushInto<&'lua Self>>::Err>
where
I: Borrow<str>,
V: PushOneInto<&'lua Self>,
{
unsafe {
ffi::lua_pushglobaltable(self.lua);
self.as_lua().push(index.borrow()).assert_one_and_forget();
match self.try_push(value) {
Ok(pushed) => {
assert_eq!(pushed.size, 1);
pushed.forget()
}
Err((err, lua)) => {
ffi::lua_pop(lua.as_lua(), 2);
return Err(err);
}
};
ffi::lua_settable(self.lua, -3);
ffi::lua_pop(self.lua, 1);
Ok(())
}
}
#[inline]
pub fn empty_array<I>(&self, index: I) -> LuaTable<PushGuard<&Self>>
where
I: Borrow<str>,
{
unsafe {
ffi::lua_pushglobaltable(self.as_lua());
match index.borrow().push_to_lua(self.as_lua()) {
Ok(pushed) => pushed.forget(),
Err(_) => unreachable!(),
};
ffi::lua_newtable(self.as_lua());
ffi::lua_settable(self.as_lua(), -3);
ffi::lua_pop(self.as_lua(), 1);
self.get(index).unwrap()
}
}
#[inline]
pub fn globals_table(&self) -> LuaTable<PushGuard<&Self>> {
unsafe {
ffi::lua_pushglobaltable(self.lua);
let guard = PushGuard::new(self, 1);
LuaRead::lua_read(guard).ok().unwrap()
}
}
}
impl Default for TempLua {
fn default() -> Self {
Self::new()
}
}
impl<T> Drop for Lua<T>
where
T: on_drop::OnDrop,
{
#[inline]
fn drop(&mut self) {
self.on_drop.on_drop(self.lua)
}
}
impl<L: AsLua> Drop for PushGuard<L> {
#[inline]
fn drop(&mut self) {
if self.size != 0 {
unsafe {
ffi::lua_pop(self.lua.as_lua(), self.size as _);
}
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct AbsoluteIndex(NonZeroI32);
impl AbsoluteIndex {
pub fn new<L>(index: NonZeroI32, lua: L) -> Self
where
L: AsLua,
{
let top = unsafe { ffi::lua_gettop(lua.as_lua()) };
let i = index.get();
if ffi::is_relative_index(i) {
Self(NonZeroI32::new(top + i + 1).expect("Invalid relative index"))
} else {
Self(index)
}
}
pub unsafe fn new_unchecked(index: NonZeroI32) -> Self {
Self(index)
}
pub fn get(&self) -> u32 {
self.0.get() as _
}
}
impl From<AbsoluteIndex> for i32 {
fn from(index: AbsoluteIndex) -> i32 {
index.0.get()
}
}