#![allow(clippy::let_and_return)]
#![allow(clippy::approx_constant)]
#![allow(clippy::needless_return)]
#![allow(clippy::redundant_closure_call)]
#![allow(clippy::len_without_is_empty)]
#![allow(clippy::unnecessary_cast)]
use std::borrow::{Borrow, Cow};
use std::collections::LinkedList;
use std::ffi::{CStr, CString};
use std::fmt;
use std::io::Read;
use std::io::{self, Write};
use std::num::NonZeroI32;
pub use ::tlua_derive::*;
pub use ::tlua_derive::test;
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, CFunction, 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::BytesInLua;
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;
#[cfg(feature = "extra_impls")]
mod smol_str;
#[cfg(feature = "internal_test")]
pub mod test;
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) -> ReadResult<T, Self>
where
Self: Sized,
T: LuaRead<Self>,
{
T::lua_read(self)
}
#[inline(always)]
fn read_at<T>(self, index: i32) -> ReadResult<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) -> ReadResult<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 = NonZeroI32::new(-1).unwrap();
pub const NEGATIVE_TWO: NonZeroI32 = NonZeroI32::new(-2).unwrap();
pub trait LuaRead<L>: Sized {
#[inline(always)]
fn n_values_expected() -> i32 {
1
}
#[inline]
fn lua_read(lua: L) -> ReadResult<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) -> ReadResult<Self, L>
where
L: AsLua,
{
if let Some(index) = NonZeroI32::new(index) {
Self::lua_read_at_position(lua, index)
} else {
let e = WrongType::default()
.expected_type::<Self>()
.actual("no value");
Err((lua, e))
}
}
fn lua_read_at_position(lua: L, index: NonZeroI32) -> ReadResult<Self, L>;
}
pub type ReadResult<T, L> = Result<T, (L, WrongType)>;
impl<L: AsLua> LuaRead<L> for LuaState {
fn lua_read_at_maybe_zero_position(lua: L, _: i32) -> ReadResult<Self, L> {
Ok(lua.as_lua())
}
fn lua_read_at_position(lua: L, _: NonZeroI32) -> ReadResult<Self, L> {
Ok(lua.as_lua())
}
}
#[derive(Debug, thiserror::Error)]
pub enum LuaError {
#[error("syntax error: {0}")]
SyntaxError(String),
#[error("{0}")]
ExecutionError(Cow<'static, str>),
#[error("{0}")]
ReadError(#[from] io::Error),
#[error("{0}")]
WrongType(#[from] WrongType),
}
#[derive(Debug, thiserror::Error)]
pub struct WrongType {
when: &'static str,
rust_expected: String,
lua_actual: String,
subtypes: LinkedList<WrongType>,
}
impl<E> From<WrongType> for CallError<E> {
fn from(e: WrongType) -> Self {
Self::LuaError(e.into())
}
}
impl Default for WrongType {
fn default() -> Self {
Self {
when: "reading Lua value",
rust_expected: Default::default(),
lua_actual: Default::default(),
subtypes: Default::default(),
}
}
}
impl fmt::Display for WrongType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let subtype = crate::unwrap_or! { self.subtypes.front(),
write!(f, "failed ")?;
return display_leaf(self, f);
};
if subtype.subtypes.is_empty()
&& subtype.rust_expected == self.rust_expected
&& subtype.lua_actual == self.lua_actual
{
write!(f, "failed ")?;
return display_leaf(self, f);
}
if self.subtypes.len() == 1 {
write!(f, "{subtype}")?;
} else {
write!(f, "variant #1: {subtype}")?;
for (subtype, i) in self.subtypes.iter().skip(1).zip(2..) {
write!(f, "\nvariant #{i}: {subtype}")?;
}
}
write!(f, "\n while ")?;
display_leaf(self, f)?;
return Ok(());
fn display_leaf(wt: &WrongType, f: &mut fmt::Formatter) -> fmt::Result {
return write!(
f,
"{}: {} expected, got {}",
wt.when, wt.rust_expected, wt.lua_actual
);
}
}
}
impl WrongType {
#[inline(always)]
pub fn info(when: &'static str) -> Self {
Self {
when,
..Self::default()
}
}
#[inline(always)]
pub fn when(mut self, when: &'static str) -> Self {
self.when = when;
self
}
#[inline(always)]
pub fn expected_type<T>(mut self) -> Self {
self.rust_expected = std::any::type_name::<T>().into();
self
}
#[inline(always)]
pub fn expected(mut self, expected: impl Into<String>) -> Self {
self.rust_expected = expected.into();
self
}
#[inline(always)]
pub fn actual_single_lua<L: AsLua>(mut self, lua: L, index: NonZeroI32) -> Self {
let index = AbsoluteIndex::new(index, lua.as_lua());
self.lua_actual = typenames(lua, index, 1);
self
}
#[inline(always)]
#[track_caller]
pub fn actual_multiple_lua<L: AsLua>(self, lua: L, n_values: i32) -> Self {
self.actual_multiple_lua_at(lua, -n_values, n_values)
}
#[inline(always)]
#[track_caller]
pub fn actual_multiple_lua_at<L: AsLua>(
mut self,
lua: L,
start: impl Into<i32>,
n_values: i32,
) -> Self {
if let Some(start) = AbsoluteIndex::try_new(start, lua.as_lua()) {
self.lua_actual = typenames(lua, start, n_values as _);
} else {
self.lua_actual = "no values".into()
}
self
}
#[inline(always)]
pub fn actual(mut self, actual: impl Into<String>) -> Self {
self.lua_actual = actual.into();
self
}
#[inline(always)]
pub fn subtype(mut self, subtype: Self) -> Self {
self.subtypes.push_back(subtype);
self
}
#[inline(always)]
pub fn subtypes(mut self, subtypes: LinkedList<Self>) -> Self {
self.subtypes = subtypes;
self
}
}
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)
}
}
#[track_caller]
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 TempLua {
#[track_caller]
#[inline]
pub fn new() -> Self {
let lua = unsafe { ffi::luaL_newstate() };
if lua.is_null() {
panic!("lua_newstate failed");
}
extern "C-unwind" 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) -> ReadResult<Self, L> {
Ok(Self {
lua: lua.as_lua(),
on_drop: on_drop::Ignore,
})
}
fn lua_read_at_position(lua: L, _: NonZeroI32) -> ReadResult<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)).map_err(|(l, _)| l)
}
}
#[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 {
#[inline]
#[track_caller]
pub fn new<L>(index: NonZeroI32, lua: L) -> Self
where
L: AsLua,
{
Self::try_new(index, lua).expect("Invalid relative index")
}
#[inline]
pub fn try_new<L>(index: impl Into<i32>, lua: L) -> Option<Self>
where
L: AsLua,
{
let top = unsafe { ffi::lua_gettop(lua.as_lua()) };
let index = index.into();
if ffi::is_relative_index(index) {
NonZeroI32::new(top + index + 1).map(Self)
} else {
NonZeroI32::new(index).map(Self)
}
}
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()
}
}