304 lines
8.6 KiB
Rust
Executable File
304 lines
8.6 KiB
Rust
Executable File
|
|
use mlua_sys as lua;
|
|
|
|
use crate::{ensure_type, AsLua, FromLua, FromLuaStack, LuaRef, PushToLuaStack, Result, StackGuard, State, Value};
|
|
|
|
#[derive(Clone)]
|
|
pub struct Table<'a> {
|
|
state: &'a State,
|
|
pub(crate) lref: LuaRef<'a>,
|
|
/// a boolean indicating if this Table is a Metatable
|
|
mt_marker: bool,
|
|
}
|
|
|
|
impl<'a> Table<'a> {
|
|
/// Create a new table
|
|
pub fn new(state: &'a State) -> Result<Self> {
|
|
let lref = unsafe {
|
|
state.ensure_stack(1)?;
|
|
|
|
let s = state.state_ptr();
|
|
lua::lua_newtable(s);
|
|
LuaRef::from_stack(state)?
|
|
};
|
|
|
|
Ok(Self {
|
|
state,
|
|
lref,
|
|
mt_marker: false,
|
|
})
|
|
}
|
|
|
|
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 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()));
|
|
}
|
|
}
|
|
LuaRef::from_stack(state)?
|
|
};
|
|
|
|
Ok(Self {
|
|
state,
|
|
lref: meta_ref,
|
|
mt_marker: true
|
|
})
|
|
}
|
|
|
|
/// Construct a table with a lua reference to one.
|
|
pub fn with_ref(state: &'a State, lua_ref: LuaRef<'a>, is_metatable: bool) -> 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!("Provided reference is not a table")
|
|
}
|
|
}
|
|
|
|
Ok(Self {
|
|
state,
|
|
lref: lua_ref,
|
|
mt_marker: is_metatable,
|
|
})
|
|
}
|
|
|
|
/// Set a key to a value in the table.
|
|
///
|
|
/// This may trigger the `__newindex` metamethod, see [`Table::raw_set`]
|
|
pub fn set<K, V>(&self, key: K, val: V) -> Result<()>
|
|
where
|
|
K: AsLua<'a>,
|
|
V: AsLua<'a>
|
|
{
|
|
let s = self.state.state_ptr();
|
|
unsafe {
|
|
let _g = StackGuard::new(self.state);
|
|
|
|
if self.mt_marker {
|
|
self.state.ensure_stack(4)?;
|
|
} else {
|
|
self.state.ensure_stack(3)?;
|
|
}
|
|
|
|
self.lref.push_to_lua_stack(self.state)?;
|
|
key.as_lua(self.state)?
|
|
.push_to_lua_stack(self.state)?;
|
|
val.as_lua(self.state)?
|
|
.push_to_lua_stack(self.state)?;
|
|
|
|
lua::lua_settable(s, -3);
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Get a value from the table.
|
|
///
|
|
/// This may trigger the `__index` metamethod, see [`Table::raw_get`]
|
|
pub fn get<K, V>(&self, key: K) -> Result<V>
|
|
where
|
|
K: AsLua<'a>,
|
|
V: FromLua<'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)?;
|
|
let val = key.as_lua(self.state)?;
|
|
val.push_to_lua_stack(self.state)?;
|
|
|
|
lua::lua_gettable(s, -2); // table[key] is at top of stack
|
|
|
|
let val = Value::from_lua_stack(self.state)?;
|
|
let val = V::from_lua(self.state, val)?;
|
|
|
|
Ok(val)
|
|
}
|
|
}
|
|
|
|
/// Returns the length of the table.
|
|
///
|
|
/// This may trigger the `__len` metamethod, see [`Table::raw_len`]
|
|
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);
|
|
|
|
Ok(len as u64)
|
|
}
|
|
}
|
|
|
|
/// Returns a boolean indicating if this table has a key
|
|
pub fn has_key<K>(&self, key: K) -> Result<bool>
|
|
where
|
|
K: AsLua<'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)?;
|
|
let key_val = key.as_lua(self.state)?;
|
|
key_val.push_to_lua_stack(self.state)?;
|
|
|
|
// table[key] is at top of stack
|
|
if lua::lua_gettable(s, -2) == lua::LUA_TNIL {
|
|
Ok(true)
|
|
} else {
|
|
Ok(false)
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Set a key to a value in the table without calling any meta methods.
|
|
pub fn raw_set<K, V>(&self, key: K, val: V) -> Result<()>
|
|
where
|
|
K: AsLua<'a>,
|
|
V: AsLua<'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.as_lua(self.state)?
|
|
.push_to_lua_stack(self.state)?;
|
|
val.as_lua(self.state)?
|
|
.push_to_lua_stack(self.state)?;
|
|
|
|
lua::lua_rawset(s, -3);
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Get a value from the table without calling any meta methods.
|
|
pub fn raw_get<K, V>(&self, key: K) -> Result<V>
|
|
where
|
|
K: AsLua<'a>,
|
|
V: FromLua<'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.as_lua(self.state)?
|
|
.push_to_lua_stack(self.state)?;
|
|
|
|
lua::lua_rawget(s, -2); // table[key] is at top of stack
|
|
|
|
let val = Value::from_lua_stack(self.state)?;
|
|
V::from_lua(self.state, val)
|
|
}
|
|
}
|
|
|
|
/// Get the length of the table without calling any meta methods.
|
|
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
|
|
|
|
Ok(len as u64)
|
|
}
|
|
}
|
|
|
|
/// Returns a boolean indicating if this table has a key without calling any meta methods.
|
|
pub fn raw_has_key<K>(&self, key: K) -> Result<bool>
|
|
where
|
|
K: AsLua<'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.as_lua(self.state)?
|
|
.push_to_lua_stack(self.state)?;
|
|
|
|
// table[key] is at top of stack
|
|
Ok(lua::lua_rawget(s, -2) != lua::LUA_TNIL)
|
|
}
|
|
}
|
|
|
|
/// Set something in the metatable
|
|
///
|
|
/// Does nothing if this table is not a metatable
|
|
pub fn set_meta<K, V>(&self, key: K, val: V) -> Result<()>
|
|
where
|
|
K: AsLua<'a>,
|
|
V: AsLua<'a>
|
|
{
|
|
if self.mt_marker {
|
|
unsafe {
|
|
let s = self.state.state_ptr();
|
|
self.state.ensure_stack(3)?;
|
|
let _g = StackGuard::new(self.state);
|
|
|
|
self.lref.push_to_lua_stack(self.state)?;
|
|
key.as_lua(self.state)?
|
|
.push_to_lua_stack(self.state)?;
|
|
val.as_lua(self.state)?
|
|
.push_to_lua_stack(self.state)?;
|
|
lua::lua_settable(s, -3);
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl<'a> PushToLuaStack<'a> for Table<'a> {
|
|
unsafe fn push_to_lua_stack(&self, state: &State) -> Result<()> {
|
|
// no need to ensure stack, the LuaRef does it for us
|
|
self.lref.push_to_lua_stack(state)?;
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl<'a> FromLuaStack<'a> for Table<'a> {
|
|
unsafe fn from_lua_stack(state: &'a State) -> Result<Self> {
|
|
ensure_type(state, lua::LUA_TTABLE, -1)?;
|
|
Table::with_ref(state, LuaRef::from_stack(state)?, false)
|
|
}
|
|
}
|
|
|
|
impl<'a> FromLua<'a> for Table<'a> {
|
|
fn from_lua(_lua: &'a State, val: Value<'a>) -> crate::Result<Self> {
|
|
val.into_table()
|
|
}
|
|
} |