Implement calling rust functions from lua, make the Table struct support metatables
This commit is contained in:
parent
19ee453172
commit
71199bc905
|
@ -1,6 +1,6 @@
|
|||
use std::ffi::CStr;
|
||||
|
||||
use crate::{LuaRef, FromLuaStack, State, PushToLuaStack};
|
||||
use crate::{FromLuaStack, LuaRef, PushToLuaStack, StackGuard, State};
|
||||
|
||||
use mlua_sys as lua;
|
||||
|
||||
|
@ -25,12 +25,21 @@ impl<'a> PushToLuaStack for Function<'a> {
|
|||
}
|
||||
|
||||
impl<'a> Function<'a> {
|
||||
pub fn from_ref(state: &'a State, lref: LuaRef) -> Self {
|
||||
Self {
|
||||
state,
|
||||
lref,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn exec<A, R>(&self, args: A) -> crate::Result<R>
|
||||
where
|
||||
A: FunctionArgs,
|
||||
R: FunctionResults<'a>,
|
||||
A: PushToLuaStackMulti<'a>,
|
||||
R: FromLuaStackMulti<'a>,
|
||||
{
|
||||
unsafe {
|
||||
let _g = StackGuard::new(self.state);
|
||||
|
||||
self.push_to_lua_stack(self.state)?;
|
||||
args.push_args_to_lua_stack(self.state)?;
|
||||
|
||||
|
@ -50,12 +59,12 @@ impl<'a> Function<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
pub trait FunctionArgs {
|
||||
pub trait PushToLuaStackMulti<'a> {
|
||||
fn len(&self) -> usize;
|
||||
fn push_args_to_lua_stack(&self, state: &State) -> crate::Result<()>;
|
||||
}
|
||||
|
||||
impl<'a, T> FunctionArgs for T
|
||||
impl<'a, T> PushToLuaStackMulti<'a> for T
|
||||
where
|
||||
T: PushToLuaStack,
|
||||
{
|
||||
|
@ -72,14 +81,14 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
pub trait FunctionResults<'a>: Sized {
|
||||
pub trait FromLuaStackMulti<'a>: Sized {
|
||||
fn len() -> usize;
|
||||
fn results_from_lua_stack(state: &'a State) -> crate::Result<Self>;
|
||||
}
|
||||
|
||||
impl<'a> FunctionResults<'a> for () {
|
||||
impl<'a> FromLuaStackMulti<'a> for () {
|
||||
fn len() -> usize {
|
||||
0
|
||||
1
|
||||
}
|
||||
|
||||
fn results_from_lua_stack(_state: &'a State) -> crate::Result<Self> {
|
||||
|
@ -87,7 +96,7 @@ impl<'a> FunctionResults<'a> for () {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, T: FromLuaStack<'a>> FunctionResults<'a> for T {
|
||||
impl<'a, T: FromLuaStack<'a>> FromLuaStackMulti<'a> for T {
|
||||
fn len() -> usize {
|
||||
1
|
||||
}
|
||||
|
@ -100,7 +109,7 @@ impl<'a, T: FromLuaStack<'a>> FunctionResults<'a> for T {
|
|||
macro_rules! impl_function_arg_tuple {
|
||||
( $count: expr, $first: tt, $( $name: tt ),+ ) => (
|
||||
#[allow(non_snake_case)]
|
||||
impl<$first: PushToLuaStack, $($name: PushToLuaStack,)+> FunctionArgs for ($first, $($name,)+) {
|
||||
impl<'a, $first: PushToLuaStack, $($name: PushToLuaStack,)+> PushToLuaStackMulti<'a> for ($first, $($name,)+) {
|
||||
fn len(&self) -> usize {
|
||||
// this will end up generating $count - 1 - 1 - 1... hopefully the compiler will
|
||||
// optimize that out
|
||||
|
@ -119,7 +128,7 @@ macro_rules! impl_function_arg_tuple {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, $first: FromLuaStack<'a>, $($name: FromLuaStack<'a>,)+> FunctionResults<'a> for ($first, $($name,)+) {
|
||||
impl<'a, $first: FromLuaStack<'a>, $($name: FromLuaStack<'a>,)+> FromLuaStackMulti<'a> for ($first, $($name,)+) {
|
||||
fn len() -> usize {
|
||||
$count
|
||||
}
|
||||
|
@ -132,10 +141,10 @@ macro_rules! impl_function_arg_tuple {
|
|||
impl_function_arg_tuple!( $count - 1, $( $name ),+ );
|
||||
);
|
||||
|
||||
// implements FunctionArgs and FunctionResults for a tuple with a single element
|
||||
// implements PushToLuaStackMulti and FromLuaStackMulti for a tuple with a single element
|
||||
( $count: expr, $only: tt ) => {
|
||||
#[allow(non_snake_case)]
|
||||
impl<$only: PushToLuaStack> FunctionArgs for ($only,) {
|
||||
impl<'a, $only: PushToLuaStack> PushToLuaStackMulti<'a> for ($only,) {
|
||||
fn len(&self) -> usize {
|
||||
1
|
||||
}
|
||||
|
@ -151,7 +160,7 @@ macro_rules! impl_function_arg_tuple {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, $only: FromLuaStack<'a>> FunctionResults<'a> for ($only,) {
|
||||
impl<'a, $only: FromLuaStack<'a>> FromLuaStackMulti<'a> for ($only,) {
|
||||
fn len() -> usize {
|
||||
1
|
||||
}
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
use crate::State;
|
||||
|
||||
use mlua_sys as lua;
|
||||
|
||||
/// A stack guard will protect the LuaStack from leaks.
|
||||
///
|
||||
/// When its first created, it keeps note of how large the stack is and when its dropped,
|
||||
/// it will pop all new values from the top of the stack.
|
||||
pub struct StackGuard<'a> {
|
||||
state: &'a State,
|
||||
top: i32,
|
||||
}
|
||||
|
||||
impl<'a> Drop for StackGuard<'a> {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
let s = self.state.state_ptr();
|
||||
let now_top = lua::lua_gettop(s);
|
||||
|
||||
if now_top > self.top {
|
||||
lua::lua_pop(s, now_top - self.top);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> StackGuard<'a> {
|
||||
pub fn new(state: &'a State) -> Self {
|
||||
let top = unsafe { lua::lua_gettop(state.state_ptr()) };
|
||||
|
||||
Self {
|
||||
state,
|
||||
top
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn lua_error_guard<F, R>(lua: &State, func: F) -> R
|
||||
where
|
||||
F: Fn() -> crate::Result<R>
|
||||
{
|
||||
match func() {
|
||||
Ok(v) => v,
|
||||
Err(e) => e.throw_lua(lua.state_ptr())
|
||||
}
|
||||
}
|
130
src/main.rs
130
src/main.rs
|
@ -1,4 +1,4 @@
|
|||
use std::{sync::Arc, ffi::CStr};
|
||||
use std::{ffi::CStr, str::Utf8Error, sync::Arc};
|
||||
|
||||
use lua::{lua_typename, lua_type};
|
||||
use mlua_sys as lua;
|
||||
|
@ -15,14 +15,73 @@ use function::*;
|
|||
pub mod value;
|
||||
use value::*;
|
||||
|
||||
pub mod guard;
|
||||
use guard::*;
|
||||
|
||||
/* struct RustFn {
|
||||
|
||||
} */
|
||||
|
||||
/* struct RustFnUpvalue {
|
||||
|
||||
} */
|
||||
|
||||
///
|
||||
pub fn ptr_to_string(ptr: *const i8) -> std::result::Result<String, Utf8Error> {
|
||||
let c = unsafe { CStr::from_ptr(ptr) };
|
||||
let s= c.to_str()?;
|
||||
Ok(s.to_string())
|
||||
}
|
||||
|
||||
|
||||
fn main() -> Result<()> {
|
||||
let lua = State::new();
|
||||
lua.expose_libraries(&[StdLibrary::Debug]);
|
||||
|
||||
let globals = lua.globals()?;
|
||||
|
||||
let a = |lua: &State, (num,): (i32,)| -> Result<i32> {
|
||||
println!("Rust got number from lua: {}", num);
|
||||
Ok(999)
|
||||
};
|
||||
|
||||
let f = lua.create_function(a)?;
|
||||
globals.set("native_test", f)?;
|
||||
|
||||
//let tbl = lua.create_table()?;
|
||||
|
||||
let vec2_add = lua.create_function(|lua: &State, (a, b): (Table, Table)| -> Result<Table> {
|
||||
let ax: i32 = a.get("x")?;
|
||||
let ay: i32 = a.get("y")?;
|
||||
|
||||
let bx: i32 = b.get("x")?;
|
||||
let by: i32 = b.get("y")?;
|
||||
|
||||
let rx = ax + bx;
|
||||
let ry = ay + by;
|
||||
|
||||
let mt = lua.create_meta_table("Vec2")?;
|
||||
mt.set("x", rx)?;
|
||||
mt.set("y", ry)?;
|
||||
Ok(mt)
|
||||
})?;
|
||||
|
||||
|
||||
let mt = lua.create_meta_table("Vec2")?;
|
||||
mt.set("x", 50)?;
|
||||
mt.set("y", 50)?;
|
||||
mt.set_meta("__add", vec2_add)?;
|
||||
globals.set("pos1", mt)?;
|
||||
|
||||
let mt = lua.create_meta_table("Vec2")?;
|
||||
mt.set("x", 25)?;
|
||||
mt.set("y", 25)?;
|
||||
globals.set("pos2", mt)?;
|
||||
|
||||
let tbl = lua.create_table()?;
|
||||
tbl.set("x", 10)?;
|
||||
|
||||
let globals = lua.globals()?;
|
||||
//let globals = lua.globals()?;
|
||||
globals.set("X", tbl)?;
|
||||
|
||||
lua.execute(r#"
|
||||
|
@ -47,14 +106,6 @@ fn main() -> Result<()> {
|
|||
end
|
||||
end]]--
|
||||
|
||||
print("x is " .. X.x)
|
||||
|
||||
cool_num = 50
|
||||
|
||||
function say_number(num)
|
||||
print("I'm lua and I said " .. num)
|
||||
end
|
||||
|
||||
function multiply_print(a, b)
|
||||
print(a .. " * " .. b .. " = " .. a*b)
|
||||
end
|
||||
|
@ -62,6 +113,21 @@ fn main() -> Result<()> {
|
|||
function multiply_ret(a, b)
|
||||
return a * b
|
||||
end
|
||||
|
||||
function say_number(a)
|
||||
print("Lua says " .. a)
|
||||
end
|
||||
|
||||
cool_num = 50
|
||||
|
||||
local res = native_test(50)
|
||||
print("Lua got " .. res .. " back from rust!")
|
||||
|
||||
print("Pos1 is (" .. pos1.x .. ", " .. pos1.y .. ")")
|
||||
print("Pos2 is (" .. pos2.x .. ", " .. pos2.y .. ")")
|
||||
|
||||
local add_pos = pos1 + pos2
|
||||
print("Pos1 + pos2 is (" .. add_pos.x .. ", " .. add_pos.y .. ")")
|
||||
"#).unwrap();
|
||||
|
||||
let num = globals.get::<_, i32>("cool_num")?;
|
||||
|
@ -99,8 +165,10 @@ impl LuaRef {
|
|||
/// Creates a reference to what is at the top of the stack.
|
||||
pub unsafe fn from_stack(state: &State) -> Result<Self> {
|
||||
let s = state.state_ptr();
|
||||
let t = lua::lua_gettop(s);
|
||||
|
||||
let r = lua::luaL_ref(s, lua::LUA_REGISTRYINDEX);
|
||||
let t = lua::lua_gettop(s);
|
||||
if r == lua::LUA_REFNIL {
|
||||
Err(Error::Nil)
|
||||
} else {
|
||||
|
@ -115,7 +183,13 @@ impl PushToLuaStack for LuaRef {
|
|||
|
||||
unsafe {
|
||||
state.ensure_stack(1)?;
|
||||
lua::lua_rawgeti(s, lua::LUA_REGISTRYINDEX, *self.0 as i64);
|
||||
let top = lua::lua_gettop(s);
|
||||
let ty = lua::lua_rawgeti(s, lua::LUA_REGISTRYINDEX, *self.0 as i64);
|
||||
let new_top = lua::lua_gettop(s);
|
||||
|
||||
if ty == lua::LUA_TNIL || ty == lua::LUA_TNONE || top == new_top {
|
||||
return Err(Error::Nil);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -124,16 +198,26 @@ impl PushToLuaStack for LuaRef {
|
|||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum Error {
|
||||
#[error("Lua runtime error: {0}")]
|
||||
/// An error returned from lua
|
||||
#[error("Lua runtime error: {0}")]
|
||||
Runtime(String),
|
||||
#[error("Ran out of memory when attempting to use `lua_checkstack`")]
|
||||
/// Ran into a not enough memory error when trying to grow the lua stack.
|
||||
#[error("Ran out of memory when attempting to use `lua_checkstack`")]
|
||||
Oom,
|
||||
#[error("Ran into a nill value on the stack")]
|
||||
Nil,
|
||||
#[error("Unexpected type, expected {0} but got {1}")]
|
||||
UnexpectedType(String, String)
|
||||
UnexpectedType(String, String),
|
||||
#[error("Bad argument provided to {func:?}! Argument #{arg_index} (name: {arg_name:?}), cause: {error}")]
|
||||
BadArgument {
|
||||
func: Option<String>,
|
||||
arg_index: i32,
|
||||
arg_name: Option<String>,
|
||||
/// the error that describes what was wrong for this argument
|
||||
error: Arc<Error>
|
||||
},
|
||||
#[error("There is already a registry entry with the key {0}")]
|
||||
RegistryConflict(String)
|
||||
}
|
||||
|
||||
impl Error {
|
||||
|
@ -144,6 +228,16 @@ impl Error {
|
|||
pub fn unexpected_type(expected: &str, got: &str) -> Self {
|
||||
Self::UnexpectedType(expected.to_string(), got.to_string())
|
||||
}
|
||||
|
||||
/// Throw the error in lua.
|
||||
///
|
||||
/// This method never returns
|
||||
pub unsafe fn throw_lua(self, lua: *mut lua::lua_State) -> ! {
|
||||
let msg = format!("{}", self);
|
||||
let msg_c = msg.as_ptr() as *const i8;
|
||||
lua::luaL_error(lua, msg_c);
|
||||
panic!("never gets here");
|
||||
}
|
||||
}
|
||||
|
||||
/// A result for use with lua functions
|
||||
|
@ -157,6 +251,12 @@ pub trait FromLuaStack<'a>: Sized {
|
|||
unsafe fn from_lua_stack(state: &'a State) -> Result<Self>;
|
||||
}
|
||||
|
||||
/* impl<'a> FromLuaStack<'a> for () {
|
||||
unsafe fn from_lua_stack(state: &'a State) -> Result<Self> {
|
||||
Ok(())
|
||||
}
|
||||
} */
|
||||
|
||||
/// Implements PushToLuaStack for a number
|
||||
macro_rules! impl_push_to_lua_stack_number {
|
||||
($ty: ident) => {
|
||||
|
@ -176,7 +276,7 @@ impl<'a> FromLuaStack<'a> for i32 {
|
|||
|
||||
if lua::lua_isnumber(s, -1) == 1 {
|
||||
let v = lua::lua_tonumber(s, -1) as i32;
|
||||
lua::lua_pop(s, -1);
|
||||
lua::lua_pop(s, 1);
|
||||
Ok(v)
|
||||
} else {
|
||||
let lua_ty = lua_type(s, -1);
|
||||
|
|
114
src/state.rs
114
src/state.rs
|
@ -1,9 +1,9 @@
|
|||
use core::ffi;
|
||||
use std::{ptr::NonNull, ffi::{CString, CStr}};
|
||||
use std::{ffi::{CString, CStr}, mem, ptr::{self, NonNull}};
|
||||
|
||||
use mlua_sys as lua;
|
||||
|
||||
use crate::{Table, Result, Error, LuaRef};
|
||||
use crate::{lua_error_guard, Error, FromLuaStack, FromLuaStackMulti, Function, LuaRef, PushToLuaStack, PushToLuaStackMulti, Result, Table};
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum StdLibrary {
|
||||
|
@ -92,6 +92,12 @@ impl State {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn from_ptr(lua: *mut lua::lua_State) -> Self {
|
||||
Self {
|
||||
lua: unsafe { NonNull::new_unchecked(lua) }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn state_ptr(&self) -> *mut lua::lua_State {
|
||||
self.lua.as_ptr()
|
||||
}
|
||||
|
@ -142,6 +148,10 @@ impl State {
|
|||
Table::new(self)
|
||||
}
|
||||
|
||||
pub fn create_meta_table<'a>(&'a self, name: &str) -> Result<Table<'a>> {
|
||||
Table::new_meta_table(self, name)
|
||||
}
|
||||
|
||||
/// This called `lua_checkstack` and returns a result.
|
||||
///
|
||||
/// The result will be Ok if the stack has space for `n` values.
|
||||
|
@ -157,7 +167,105 @@ impl State {
|
|||
unsafe {
|
||||
let s = self.state_ptr();
|
||||
lua::lua_rawgeti(s, lua::LUA_REGISTRYINDEX, lua::LUA_RIDX_GLOBALS);
|
||||
Table::with_ref(self, LuaRef::from_stack(self)?)
|
||||
Table::with_ref(self, LuaRef::from_stack(self)?, None)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn print_stack(state: &State) {
|
||||
let s = state.state_ptr();
|
||||
let t = lua::lua_gettop(s);
|
||||
|
||||
for i in 1..(t+1) {
|
||||
let ty = lua::lua_type(s, i);
|
||||
let tyname = lua::lua_typename(s, ty);
|
||||
let tyname = CStr::from_ptr(tyname);
|
||||
let tyname = tyname.to_str().unwrap();
|
||||
println!("{}: {}", i, tyname);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_function<'a, A, R, F>(&'a self, f: F) -> Result<Function<'a>>
|
||||
where
|
||||
A: FromLuaStackMulti<'a>,
|
||||
R: PushToLuaStackMulti<'a>,
|
||||
F: Fn(&'a State, A) -> Result<R> + 'static,
|
||||
{
|
||||
unsafe extern "C-unwind" fn rust_closure(s: *mut lua::lua_State) -> i32 {
|
||||
// ensure validity of data
|
||||
let upv_idx = lua::lua_upvalueindex(1);
|
||||
let ltype = lua::lua_type(s, upv_idx);
|
||||
match ltype {
|
||||
lua::LUA_TUSERDATA => {
|
||||
let ud_ptr = lua::lua_touserdata(s, upv_idx);
|
||||
|
||||
if ud_ptr.is_null() {
|
||||
crate::Error::runtime("null upvalue provided to luacclosure")
|
||||
.throw_lua(s);
|
||||
}
|
||||
|
||||
let data_ptr = ud_ptr as *mut ClosureData;
|
||||
|
||||
let top = lua::lua_gettop(s);
|
||||
let wrap = &(*data_ptr).wrapper_fn;
|
||||
let s = (*data_ptr).state;
|
||||
|
||||
wrap(s, top)
|
||||
},
|
||||
_ => {
|
||||
let name = CStr::from_ptr(lua::lua_typename(s, ltype));
|
||||
if let Ok(n_str) = name.to_str() {
|
||||
crate::Error::Runtime(format!("unexpected type ({}) provided to luacclosure", n_str))
|
||||
.throw_lua(s);
|
||||
} else {
|
||||
crate::Error::runtime("unexpected type provided to luacclosure")
|
||||
.throw_lua(s);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct ClosureData<'a> {
|
||||
wrapper_fn: Box<dyn Fn(&'a State, i32) -> i32>,
|
||||
state: &'a State,
|
||||
}
|
||||
|
||||
let wrapper_fn = move |lua: &State, narg: i32| -> i32 {
|
||||
unsafe {
|
||||
let lua: &State = mem::transmute(lua); // transmute lifetimes
|
||||
|
||||
if narg != A::len() as i32 {
|
||||
Error::Runtime(format!("incorrect number of arguments provided to lua function, expected {}", A::len()))
|
||||
.throw_lua(lua.state_ptr());
|
||||
}
|
||||
|
||||
lua_error_guard(lua, || {
|
||||
let args = A::results_from_lua_stack(lua)?;
|
||||
|
||||
let r = f(lua, args)?;
|
||||
r.push_args_to_lua_stack(lua)?;
|
||||
Ok(r.len() as i32)
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
let data = ClosureData {
|
||||
wrapper_fn: Box::new(wrapper_fn),
|
||||
state: self,
|
||||
};
|
||||
|
||||
let s = self.state_ptr();
|
||||
|
||||
unsafe {
|
||||
let ptr = lua::lua_newuserdata(s, mem::size_of::<ClosureData>());
|
||||
let ptr = ptr.cast();
|
||||
ptr::write(ptr, data);
|
||||
|
||||
lua::lua_pushcclosure(s, rust_closure, 1);
|
||||
let lref = LuaRef::from_stack(self)?;
|
||||
|
||||
Ok(Function::from_ref(self, lref))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
125
src/table.rs
125
src/table.rs
|
@ -1,12 +1,20 @@
|
|||
|
||||
use std::{ffi::CStr, ops::Deref, sync::Arc};
|
||||
|
||||
use mlua_sys as lua;
|
||||
|
||||
use crate::{State, Result, PushToLuaStack, LuaRef, FromLuaStack};
|
||||
use crate::{FromLuaStack, LuaRef, PushToLuaStack, Result, StackGuard, State};
|
||||
|
||||
pub(crate) struct MetaTableInfo {
|
||||
name: Option<String>,
|
||||
lref: LuaRef,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Table<'a> {
|
||||
state: &'a State,
|
||||
lref: LuaRef,
|
||||
meta: Option<LuaRef>, // Some if this table is a metatable
|
||||
}
|
||||
|
||||
impl<'a> Table<'a> {
|
||||
|
@ -23,24 +31,62 @@ impl<'a> Table<'a> {
|
|||
Ok(Self {
|
||||
state,
|
||||
lref,
|
||||
meta: None,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn new_meta_table(state: &'a State, name: &str) -> Result<Self> {
|
||||
let name_term = format!("{}\0", name);
|
||||
let name_term_c = name_term.as_str().as_ptr() as *const i8;
|
||||
let name_term_arc = Arc::new(name_term_c);
|
||||
|
||||
let (lref, meta_ref) = unsafe {
|
||||
let _g = StackGuard::new(state);
|
||||
state.ensure_stack(2)?;
|
||||
|
||||
let s = state.state_ptr();
|
||||
if lua::luaL_newmetatable(s, name_term_c) == 0 {
|
||||
// lua::luaL_getmetatable does not return the type that was
|
||||
// retrieved from the registry
|
||||
lua::lua_pushstring(s, name_term_c);
|
||||
let ty = lua::lua_rawget(s, lua::LUA_REGISTRYINDEX);
|
||||
|
||||
if ty != lua::LUA_TTABLE {
|
||||
return Err(crate::Error::RegistryConflict(name.to_string()));
|
||||
}
|
||||
}
|
||||
let meta = LuaRef::from_stack(state)?;
|
||||
|
||||
|
||||
let s = state.state_ptr();
|
||||
lua::lua_newtable(s);
|
||||
(LuaRef::from_stack(state)?, meta)
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
state,
|
||||
lref,
|
||||
meta: Some(meta_ref)
|
||||
})
|
||||
}
|
||||
|
||||
/// Construct a table with a lua reference to one.
|
||||
pub fn with_ref(state: &'a State, lua_ref: LuaRef) -> Result<Self> {
|
||||
pub fn with_ref(state: &'a State, lua_ref: LuaRef, name: Option<String>) -> Result<Self> {
|
||||
let s = state.state_ptr();
|
||||
|
||||
unsafe {
|
||||
let _g = StackGuard::new(state);
|
||||
|
||||
lua_ref.push_to_lua_stack(state)?;
|
||||
if lua::lua_istable(s, -1) == 0 {
|
||||
panic!("Index is not a table")
|
||||
}
|
||||
lua::lua_pop(s, -1);
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
state,
|
||||
lref: lua_ref,
|
||||
meta: None,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -54,14 +100,24 @@ impl<'a> Table<'a> {
|
|||
{
|
||||
let s = self.state.state_ptr();
|
||||
unsafe {
|
||||
let _g = StackGuard::new(self.state);
|
||||
|
||||
if let Some(_) = &self.meta {
|
||||
self.state.ensure_stack(4)?;
|
||||
} else {
|
||||
self.state.ensure_stack(3)?;
|
||||
}
|
||||
|
||||
self.lref.push_to_lua_stack(self.state)?;
|
||||
key.push_to_lua_stack(self.state)?;
|
||||
val.push_to_lua_stack(self.state)?;
|
||||
|
||||
lua::lua_settable(s, -3);
|
||||
lua::lua_pop(self.state.state_ptr(), -1);
|
||||
|
||||
if let Some(mt) = &self.meta {
|
||||
mt.push_to_lua_stack(self.state)?;
|
||||
lua::lua_setmetatable(s, -2);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -77,14 +133,14 @@ impl<'a> Table<'a> {
|
|||
{
|
||||
let s = self.state.state_ptr();
|
||||
unsafe {
|
||||
self.state.ensure_stack(2)?;
|
||||
let _g = StackGuard::new(self.state);
|
||||
|
||||
self.lref.push_to_lua_stack(self.state)?;
|
||||
key.push_to_lua_stack(self.state)?;
|
||||
lua::lua_gettable(s, -2); // table[key] is at top of stack
|
||||
|
||||
let top = lua::lua_gettop(s);
|
||||
let val = V::from_lua_stack(self.state)?;
|
||||
let new_top = lua::lua_gettop(s);
|
||||
debug_assert!(new_top < top, "V::from_lua_stack did not remove anything from the stack!");
|
||||
|
||||
Ok(val)
|
||||
}
|
||||
|
@ -96,11 +152,13 @@ impl<'a> Table<'a> {
|
|||
pub fn len(&self) -> Result<u64> {
|
||||
let s = self.state.state_ptr();
|
||||
unsafe {
|
||||
self.state.ensure_stack(1)?;
|
||||
let _g = StackGuard::new(self.state);
|
||||
|
||||
self.lref.push_to_lua_stack(self.state)?;
|
||||
lua::lua_len(s, -1);
|
||||
|
||||
let len = lua::lua_tonumber(s, -1);
|
||||
lua::lua_pop(self.state.state_ptr(), -1);
|
||||
|
||||
Ok(len as u64)
|
||||
}
|
||||
|
@ -115,6 +173,7 @@ impl<'a> Table<'a> {
|
|||
let s = self.state.state_ptr();
|
||||
unsafe {
|
||||
self.state.ensure_stack(3)?;
|
||||
let _g = StackGuard::new(self.state);
|
||||
|
||||
self.lref.push_to_lua_stack(self.state)?;
|
||||
key.push_to_lua_stack(self.state)?;
|
||||
|
@ -135,6 +194,9 @@ impl<'a> Table<'a> {
|
|||
{
|
||||
let s = self.state.state_ptr();
|
||||
unsafe {
|
||||
self.state.ensure_stack(2)?;
|
||||
let _g = StackGuard::new(self.state);
|
||||
|
||||
self.lref.push_to_lua_stack(self.state)?;
|
||||
key.push_to_lua_stack(self.state)?;
|
||||
lua::lua_rawget(s, -2); // table[key] is at top of stack
|
||||
|
@ -146,13 +208,39 @@ impl<'a> Table<'a> {
|
|||
pub fn raw_len(&self) -> Result<u64> {
|
||||
let s = self.state.state_ptr();
|
||||
unsafe {
|
||||
self.state.ensure_stack(1)?;
|
||||
let _g = StackGuard::new(self.state);
|
||||
|
||||
self.lref.push_to_lua_stack(self.state)?;
|
||||
let len = lua::lua_rawlen(s, -1);
|
||||
lua::lua_pop(s, -1); // pop table
|
||||
lua::lua_pop(s, 1); // pop table
|
||||
|
||||
Ok(len as u64)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_meta<K, V>(&self, key: K, val: V) -> Result<()>
|
||||
where
|
||||
K: PushToLuaStack,
|
||||
V: PushToLuaStack
|
||||
{
|
||||
let mt = self.meta.as_ref()
|
||||
.expect("this table is not a meta table!");
|
||||
|
||||
unsafe {
|
||||
let s = self.state.state_ptr();
|
||||
self.state.ensure_stack(3)?;
|
||||
let _g = StackGuard::new(self.state);
|
||||
|
||||
//lua::luaL_getmetatable(s, **cname);
|
||||
mt.push_to_lua_stack(self.state)?;
|
||||
key.push_to_lua_stack(self.state)?;
|
||||
val.push_to_lua_stack(self.state)?;
|
||||
lua::lua_settable(s, -3);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> PushToLuaStack for Table<'a> {
|
||||
|
@ -163,3 +251,22 @@ impl<'a> PushToLuaStack for Table<'a> {
|
|||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FromLuaStack<'a> for Table<'a> {
|
||||
unsafe fn from_lua_stack(state: &'a State) -> Result<Self> {
|
||||
let s = state.state_ptr();
|
||||
|
||||
let ty = lua::lua_type(s, -1);
|
||||
if ty == lua::LUA_TTABLE {
|
||||
let t = Table::with_ref(state, LuaRef::from_stack(state)?, None);
|
||||
|
||||
t
|
||||
} else {
|
||||
let tyname = lua::lua_typename(s, ty);
|
||||
let cstr = CStr::from_ptr(tyname);
|
||||
let s = cstr.to_str()
|
||||
.expect("Lua type has invalid bytes!");
|
||||
Err(crate::Error::UnexpectedType("Table".to_string(), s.to_string()))
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue