low-luajit 0.2.0

Low level but safe bindings to the LuaJIT C API with no dependencies and no-std support to create Lua modules using Rust.
Documentation
use core::ffi::CStr;
use crate::{LuaState, ffi};

/// Table related functions.
impl LuaState {
	/// Returns `true` if the value at the given acceptable `index` is a table, `false` otherwise.
	#[inline]
	#[must_use]
	pub fn is_table(&self, index: i32) -> bool {
		// SAFETY: self.0 is always valid.
		unsafe { ffi::lua_istable(self.0, index) != 0 }
	}

	/// Creates a new empty table and pushes it onto the stack.
	///
	/// To pre-allocate elements when creating a new table, see [`LuaState::create_table`].
	#[inline]
	pub fn new_table(&mut self) {
		// SAFETY: self.0 is always valid.
		unsafe { ffi::lua_newtable(self.0); }
	}

	/// Creates a new empty table and pushes it onto the stack.
	///
	/// The table will have `n_arr` array elements and `n_rec` non-array elements pre-allocated.
	///
	/// To create a new table without pre-allocating, see [`LuaState::new_table`].
	///
	/// # Lua Errors
	///
	/// Calling this function way too many times may trigger a stack overflow.
	///
	/// Incredibly high values for `n_arr` or `n_rec` may instantly trigger a table overflow.
	#[inline]
	pub fn create_table(&mut self, n_arr: i32, n_rec: i32) {
		// SAFETY: self.0 is always valid.
		unsafe { ffi::lua_createtable(self.0, n_arr, n_rec); }
	}

	/// Does the equivalent of the Lua code `t[key]`, where `t` is the value located at the given valid `index`.
	///
	/// The result is pushed onto the stack (even if `nil`).
	///
	/// For a function that does the same but allows any value instead of `key`, see [`LuaState::get_table`].
	///
	/// # Lua Errors
	///
	/// Not every value `t` can be indexed: for example, if `t` is `nil` this function will error.
	///
	/// Indexing `t` may also trigger the `__index` metamethod which could error.
	#[inline]
	pub fn get_field(&mut self, index: i32, key: &CStr) {
		// SAFETY: self.0 is always valid and key's pointer is valid for the duration of this function.
		unsafe { ffi::lua_getfield(self.0, index, key.as_ptr()); }
	}

	/// Does the equivalent of the Lua code `t[key] = v`, where `t` is the value located at the given valid `index`
	/// and `v` is the value at the top of the stack (which is popped).
	///
	/// For a function that does the same but allows any value instead of `key`, see [`LuaState::set_table`].
	///
	/// # Panics
	///
	/// Panics if the stack is empty.
	///
	/// # Lua Errors
	///
	/// Not every value `t` can be indexed: for example, if `t` is `nil` this function will error.
	///
	/// Indexing `t` may also trigger the `__newindex` metamethod which could error.
	#[inline]
	pub fn set_field(&mut self, index: i32, key: &CStr) {
		assert!(self.get_top() > 0, "tried to set field {key:?} for the table at index {index} despite having an empty stack");

		// SAFETY: self.0 is always valid, key's pointer is valid for the duration of this function, and the stack is not empty.
		unsafe { ffi::lua_setfield(self.0, index, key.as_ptr()); }
	}

	/// Gets the global named `name` and pushes it onto the stack (even if `nil`).
	///
	/// # Lua Errors
	///
	/// Indexing the global environment may trigger the `__index` metamethod which could error.
	#[inline]
	pub fn get_global(&mut self, name: &CStr) {
		// SAFETY: self.0 is always valid and name's pointer is valid for the duration of this function.
		unsafe { ffi::lua_getglobal(self.0, name.as_ptr()); }
	}

	/// Pops the top element and sets it as the value of the global named `name`.
	///
	/// # Panics
	///
	/// Panics if the stack is empty.
	///
	/// # Lua Errors
	///
	/// Indexing the global environment may trigger the `__newindex` metamethod which could error.
	#[inline]
	pub fn set_global(&mut self, name: &CStr) {
		assert!(self.get_top() > 0, "tried to set global {name:?} despite having an empty stack");

		// SAFETY: self.0 and name's pointer are always valid and the stack is not empty.
		unsafe { ffi::lua_setglobal(self.0, name.as_ptr()); }
	}

	/// Does the equivalent of the Lua code `t[k]`, where `t` is the value located at the given valid `index`
	/// and `k` is the value at the top of the stack (which is popped).
	///
	/// The result is pushed onto the stack, replacing `k` (even if `nil`).
	///
	/// # Panics
	///
	/// Panics if the stack is empty.
	///
	/// # Lua Errors
	///
	/// Not every value `t` can be indexed: for example, if `t` is `nil` this function will error.
	///
	/// Indexing `t` may also trigger the `__index` metamethod which could error.
	#[inline]
	pub fn get_table(&mut self, index: i32) {
		assert!(self.get_top() > 0, "tried to get a value from the table at index {index} despite having an empty stack");

		// SAFETY: self.0 is always valid and the stack is not empty.
		unsafe { ffi::lua_gettable(self.0, index); }
	}

	/// Does the equivalent of the Lua code `t[k] = v`, where `t` is the value located at the given valid `index`
	/// and `v` and `k` are the values at the top of the stack (which are popped from the stack in said order).
	///
	/// # Panics
	///
	/// Panics if the stack does not contain both `v` and `k` (thus contains less than 2 elements).
	///
	/// # Lua Errors
	///
	/// Not every value `t` or `k` can be used: if `t` or `k` are `nil` this function will error.
	///
	/// Indexing `t` may also trigger the `__newindex` metamethod which could error.
	#[inline]
	pub fn set_table(&mut self, index: i32) {
		assert!(2 <= self.get_top(), "tried to set a value for the table at index {index} despite not having `v` and `k` in the stack");

		// SAFETY: self.0 is always valid and v and k are in the stack.
		unsafe { ffi::lua_settable(self.0, index); }
	}

	/// Accesses a table similarly to [`LuaState::get_table`], but without triggering any metamethod (see [`LuaState::get_table`]).
	///
	/// # Panics
	///
	/// Panics if the element at the given valid `index` is not a table or if the stack is empty.
	#[inline]
	pub fn raw_get(&mut self, index: i32) {
		assert!(self.is_table(index), "cannot raw get a value from the element at index {index} as it is not a table");
		assert!(self.get_top() > 0, "tried to raw get a value from the table at index {index} despite having an empty stack");

		// SAFETY: self.0 is always valid, the element specified is a table and the stack is not empty.
		unsafe { ffi::lua_rawget(self.0, index); }
	}

	/// Accesses a table similarly to [`LuaState::set_table`], but without triggering any metamethod (see [`LuaState::set_table`]).
	///
	/// # Panics
	///
	/// Panics if the element at the given valid `index` is not a table or if the stack is empty.
	///
	/// # Lua errors
	///
	/// Not every value `k` can be used: if `k` is `nil` this function will error.
	#[inline]
	pub fn raw_set(&mut self, index: i32) {
		assert!(self.is_table(index), "cannot raw set a value for the element at index {index} as it is not a table");
		assert!(2 <= self.get_top(), "tried to raw set a value for the table at index {index} despite not having `v` and `k` in the stack");

		// SAFETY: self.0 is always valid, the element specified is a table and v and k are in the stack.
		unsafe { ffi::lua_rawset(self.0, index); }
	}

	/// Does the equivalent of the Lua code `t[i]`, where `t` is the value located at the given valid `index`.
	///
	/// The result is pushed onto the stack.
	///
	/// # Panics
	///
	/// Panics if the element at the given valid `index` is not a table.
	#[inline]
	pub fn raw_geti(&mut self, index: i32, i: i32) {
		assert!(self.is_table(index), "cannot raw get an element from the element at index {index} as it is not a table");

		// SAFETY: self.0 is always valid and the element specified is a table.
		unsafe { ffi::lua_rawgeti(self.0, index, i); }
	}

	/// Does the equivalent of the Lua code `t[i] = v`, where `t` is the value located at the given valid `index`
	/// and `v` is the value at the top of the stack (which is popped).
	///
	/// # Panics
	///
	/// Panics if the element at the given valid `index` is not a table or if the stack is empty.
	#[inline]
	pub fn raw_seti(&mut self, index: i32, i: i32) {
		assert!(self.is_table(index), "cannot raw set a value for the element at index {index} as it is not a table");
		assert!(self.get_top() > 0, "tried to raw set a value for the table at index {index} despite having an empty stack");

		// SAFETY: self.0 is always valid, the element specified is a table and the stack is not empty.
		unsafe { ffi::lua_rawseti(self.0, index, i); }
	}

	/// Pops the last element of the stack, treating it as the key,
	/// and pushes onto the stack the *next* key-value pair from the table at the given `index`, returning `true`.
	///
	/// If there is no next key-value pair, then nothing is pushed and `false` is returned instead.
	///
	/// This function can be used to traverse all elements of a table, see the example below.
	///
	/// # Examples
	///
	/// ```
	/// # use low_luajit::{LuaState, AmountReturned};
	/// # extern "C-unwind" fn example(mut lua: LuaState) -> AmountReturned {
	/// # let t = 1;
	/// // Assume the table is located in the stack at index t.
	/// lua.push_nil(); // Push the first (dummy) key.
	/// while lua.next(t) { // Get the next key-value pair.
	///     // (The key is located at index -2, while the value is located at index -1.)
	///     // Use the key-value pair in some way...
	///     lua.pop(1); // Pop the value, so that the key can be used to get the next key-value pair.
	/// }
	/// # AmountReturned::NONE }
	/// ```
	pub fn next(&mut self, index: i32) -> bool { // TODO: check for any error/panic
		unsafe { ffi::lua_next(self.0, index) != 0 }
	}

	/// Gets the metatable of the value at the given acceptable `index` and pushes it onto the stack, returning `true`.
	///
	/// If `index` is invalid or the value has no metatable, nothing is pushed and the function returns `false`.
	#[inline]
	pub fn get_metatable(&mut self, index: i32) -> bool {
		unsafe { ffi::lua_getmetatable(self.0, index) != 0 }
	}

	/// Pops the top elemenet and sets it as the metatable of the value at the given acceptable `index`.
	///
	/// This function always returns `true`.
	///
	/// # Panics
	///
	/// Panics if the top element is not a table or if the stack is empty.
	#[inline]
	pub fn set_metatable(&mut self, index: i32) -> bool {
		assert!(self.get_top() > 0, "tried to set the metatable for the element at index {index} despite having an empty stack");
		assert!(self.is_table(-1), "cannot set the metatable for the element at index {index} as the given metatable is not a table");

		// SAFETY: self.0 is always valid, the stack is not empty and the top element is a table.
		unsafe { ffi::lua_setmetatable(self.0, index) != 0 }
	}

	/// Gets the environment table of the value at the given `index` and pushes it onto the stack.
	///
	/// If `index` is invalid or the value has no environment table, `nil` is pushed onto the stack.
	#[inline]
	pub fn get_fenv(&mut self, index: i32) {
		unsafe { ffi::lua_getfenv(self.0, index); }
	}

	/// Pops a table from the stack and sets it as the environment table of the value at the given `index`, returning `true`.
	///
	/// Only functions, userdata and threads may have an environment table.
	///
	/// If `index` is invalid or the value's type is not one listed above, the stack is still popped but the function returns `false`.
	///
	/// # Panics
	///
	/// Panics if the top element is not a table or if the stack is empty.
	#[inline]
	pub fn set_fenv(&mut self, index: i32) -> bool {
		assert!(self.get_top() > 0, "tried to set the environment table for the element at index {index} despite having an empty stack");
		assert!(self.is_table(-1), "cannot set the environment table for the element at index {index} as the given environment table is not a table");

		unsafe { ffi::lua_setfenv(self.0, index) != 0 }
	}

	/// Calls the metamethod `method` from the metatable of the element at the given `index`.
	///
	/// The metamethod will receive the element itself as its only argument and its result will be pushed onto the stack, returning `true`.
	///
	/// If there is no metatable or no metamethod, nothing is pushed and this function returns `false` instead.
	///
	/// # Lua Errors
	///
	/// The called metamethod may error.
	#[inline]
	pub fn call_meta(&mut self, index: i32, method: &CStr) -> bool {
		// SAFETY: method's pointer is valid for the duration of this function.
		unsafe { ffi::luaL_callmeta(self.0, index, method.as_ptr()) != 0 }
	}
}