Add more bindings, add push to Lua trait

This commit is contained in:
William Venner 2022-01-22 17:30:04 +00:00
parent 0a3141d6b8
commit 47492748f2
8 changed files with 232 additions and 15 deletions

2
Cargo.lock generated
View File

@ -135,7 +135,7 @@ dependencies = [
[[package]]
name = "gmod"
version = "11.0.2"
version = "11.1.1"
dependencies = [
"cfg_table 1.0.0",
"cstr",

View File

@ -8,4 +8,4 @@ publish = false
crate-type = ["cdylib"]
[dependencies]
gmod = {version = "10.2.1", features = ["gmcl"], default-features = false}
gmod = {version = "*", features = ["gmcl"], default-features = false}

View File

@ -2,7 +2,7 @@
name = "gmod-macros"
version = "1.0.4"
authors = ["William Venner <william@venner.io>"]
edition = "2018"
edition = "2021"
license = "MIT"
description = "Proc macros for gmod-rs"
repository = "https://github.com/WilliamVenner/gmod-rs"

View File

@ -2,7 +2,7 @@
name = "gmod"
version = "11.1.1"
authors = ["William Venner <william@venner.io>"]
edition = "2018"
edition = "2021"
license = "MIT"
description = "A swiss army knife for creating binary modules for Garry's Mod in Rust"
repository = "https://github.com/WilliamVenner/gmod-rs"

View File

@ -164,6 +164,11 @@ pub struct LuaShared {
pub lua_topointer: Symbol<'static, unsafe extern "C-unwind" fn(state: LuaState, index: i32) -> *const c_void>,
pub lua_newuserdata: Symbol<'static, unsafe extern "C-unwind" fn(state: LuaState, size: usize) -> *mut c_void>,
pub lual_newmetatable: Symbol<'static, unsafe extern "C-unwind" fn(state: LuaState, name: LuaString) -> i32>,
pub lua_resume: Symbol<'static, unsafe extern "C-unwind" fn(state: LuaState, narg: i32) -> i32>,
pub lua_newthread: Symbol<'static, unsafe extern "C-unwind" fn(state: LuaState) -> LuaState>,
pub lua_yield: Symbol<'static, unsafe extern "C-unwind" fn(state: LuaState, nresults: i32) -> i32>,
pub lua_pushthread: Symbol<'static, unsafe extern "C-unwind" fn(state: LuaState) -> i32>,
pub lua_tothread: Symbol<'static, unsafe extern "C-unwind" fn(state: LuaState, index: i32) -> LuaState>,
}
unsafe impl Sync for LuaShared {}
impl LuaShared {
@ -231,6 +236,11 @@ impl LuaShared {
lua_topointer: find_symbol!("lua_topointer"),
lua_newuserdata: find_symbol!("lua_newuserdata"),
lual_newmetatable: find_symbol!("luaL_newmetatable"),
lua_resume: find_symbol!("lua_resume_real"),
lua_newthread: find_symbol!("lua_newthread"),
lua_yield: find_symbol!("lua_yield"),
lua_pushthread: find_symbol!("lua_pushthread"),
lua_tothread: find_symbol!("lua_tothread"),
}
}
}

View File

@ -166,6 +166,16 @@ impl LuaState {
(LUA_SHARED.lua_pushnil)(*self)
}
#[inline(always)]
pub unsafe fn push_thread(&self) -> i32 {
(LUA_SHARED.lua_pushthread)(*self)
}
#[inline(always)]
pub unsafe fn to_thread(&self, index: i32) -> State {
(LUA_SHARED.lua_tothread)(*self, index)
}
#[inline(always)]
pub unsafe fn pcall(&self, nargs: i32, nresults: i32, errfunc: i32) -> i32 {
(LUA_SHARED.lua_pcall)(*self, nargs, nresults, errfunc)
@ -177,18 +187,27 @@ impl LuaState {
pub unsafe fn pcall_ignore(&self, nargs: i32, nresults: i32) -> bool {
let res = self.pcall(nargs, nresults, 0);
if res == LUA_ERRRUN {
self.get_global(crate::lua_string!("ErrorNoHaltWithStack"));
if self.is_nil(-1) {
eprintln!("[ERROR] {:?}", self.get_string(-2));
self.pop_n(2);
return false;
}
self.push_value(-2);
self.call(1, 0);
crate::lua_stack_guard!(self => {
self.get_global(crate::lua_string!("ErrorNoHaltWithStack"));
if self.is_nil(-1) {
eprintln!("[ERROR] {:?}", self.get_string(-2));
self.pop();
} else {
#[cfg(debug_assertions)] {
self.push_string(&format!("[pcall_ignore] {}", self.get_string(-2).expect("Expected a string here")));
}
#[cfg(not(debug_assertions))] {
self.push_value(-2);
}
self.call(1, 0);
}
});
self.pop();
return false;
false
} else {
true
}
true
}
pub unsafe fn load_string(&self, src: LuaString) -> Result<(), LuaError> {
@ -450,9 +469,25 @@ impl LuaState {
(LUA_SHARED.lua_touserdata)(*self, index)
}
#[inline(always)]
pub unsafe fn coroutine_new(&self) -> State {
(LUA_SHARED.lua_newthread)(*self)
}
#[inline(always)]
pub unsafe fn coroutine_yield(&self, nresults: i32) -> i32 {
(LUA_SHARED.lua_yield)(*self, nresults)
}
#[inline(always)]
pub unsafe fn coroutine_resume(&self, narg: i32) -> i32 {
(LUA_SHARED.lua_resume)(*self, narg)
}
/// Creates a new table in the registry with the given `name` as the key if it doesn't already exist, and pushes it onto the stack.
///
/// Returns if the metatable was already present in the registry.
#[inline(always)]
pub unsafe fn new_metatable(&self, name: LuaString) -> bool {
(LUA_SHARED.lual_newmetatable)(*self, name) == 0
}
@ -480,6 +515,7 @@ impl LuaState {
self.push_value(-2);
self.set_metatable(-2);
self.remove(self.get_top() - 1);
self.remove(self.get_top() - 1);
}
ptr.write(data);

View File

@ -6,6 +6,9 @@ pub use import::*;
mod lua_state;
pub use lua_state::LuaState as State;
mod push;
pub use push::*;
#[derive(Debug, Clone)]
pub enum LuaError {
/// Out of memory
@ -73,7 +76,21 @@ macro_rules! lua_stack_guard {
let ret = (|| $code)();
if top != $lua.get_top() {
$lua.dump_stack();
panic!("Stack is dirty!");
panic!("Stack is dirty! Expected the stack to have {} elements, but it has {}!", top, $lua.get_top());
}
ret
}
#[cfg(not(debug_assertions))]
$code
}};
( $lua:ident => $elem:literal => $code:block ) => {{
#[cfg(debug_assertions)] {
let ret = (|| $code)();
if $lua.get_top() != $elem {
$lua.dump_stack();
panic!("Stack is dirty! Expected the stack to have ", $elem, " (fixed size) elements, but it has {}!", $lua.get_top());
}
ret
}

154
gmod/src/lua/push.rs Normal file
View File

@ -0,0 +1,154 @@
use std::time::{Duration, SystemTime};
pub trait PushToLua: Sized {
/// Pushes this value to the Lua stack.
unsafe fn push_to_lua(self, lua: crate::lua::State);
}
pub trait TryPushToLua: Sized {
/// Checked `push_to_lua` for types that may not fit in an `i32`
unsafe fn try_push_to_lua(self, lua: crate::lua::State) -> Result<(), Self>;
}
pub trait ForcePushToLua: Sized {
/// `push_to_lua` but may result in loss of data
unsafe fn force_push_to_lua(self, lua: crate::lua::State);
}
pub trait PushCollectionToLua: Sized {
/// Pushes this collection to a table at the top of the Lua stack.
///
/// **You must create the table yourself**
unsafe fn push_to_lua_table(self, lua: crate::lua::State);
}
impl<P: PushToLua> TryPushToLua for P {
#[inline]
unsafe fn try_push_to_lua(self, lua: crate::lua::State) -> Result<(), Self> {
self.push_to_lua(lua);
Ok(())
}
}
impl<P: PushToLua>ForcePushToLua for P {
#[inline]
unsafe fn force_push_to_lua(self, lua: crate::lua::State) {
self.push_to_lua(lua);
}
}
macro_rules! push_primitives {
{$($ty:ty => $fn:ident),*} => {$(
impl PushToLua for $ty {
#[inline]
unsafe fn push_to_lua(self, lua: crate::lua::State) {
lua.$fn(self as _);
}
}
)*};
}
macro_rules! try_push_primitives {
{$($ty:ty => $fn:ident / $forcefn:ident),*} => {$(
impl TryPushToLua for $ty {
#[inline]
unsafe fn try_push_to_lua(self, lua: crate::lua::State) -> Result<(), Self> {
lua.$fn(match self.try_into() {
Ok(v) => v,
Err(e) => return Err(self)
});
Ok(())
}
}
impl ForcePushToLua for $ty {
#[inline]
unsafe fn force_push_to_lua(self, lua: crate::lua::State) {
lua.$forcefn(self as _);
}
}
)*};
}
push_primitives! {
&str => push_string,
bool => push_boolean,
f64 => push_number,
f32 => push_number,
u8 => push_integer,
i8 => push_integer,
u16 => push_integer,
i16 => push_integer,
i32 => push_integer
}
try_push_primitives! {
u32 => push_integer / push_number,
i64 => push_integer / push_number,
u64 => push_integer / push_number,
u128 => push_integer / push_number,
i128 => push_integer / push_number
}
impl PushToLua for String {
#[inline]
unsafe fn push_to_lua(self, lua: crate::lua::State) {
lua.push_string(&self);
}
}
impl PushToLua for Vec<u8> {
#[inline]
unsafe fn push_to_lua(self, lua: crate::lua::State) {
lua.push_binary_string(&self);
}
}
impl PushToLua for &[u8] {
#[inline]
unsafe fn push_to_lua(self, lua: crate::lua::State) {
lua.push_binary_string(&self);
}
}
impl PushToLua for Duration {
#[inline]
unsafe fn push_to_lua(self, lua: crate::lua::State) {
lua.push_number(self.as_secs_f64());
}
}
impl<T: PushToLua> PushToLua for Option<T> {
#[inline]
unsafe fn push_to_lua(self, lua: crate::lua::State) {
match self {
Some(val) => val.push_to_lua(lua),
None => lua.push_nil()
}
}
}
impl<K: PushToLua, V: PushToLua> PushCollectionToLua for std::collections::BTreeMap<K, V> {
#[inline]
unsafe fn push_to_lua_table(self, lua: crate::lua::State) {
for (k, v) in self {
k.push_to_lua(lua);
v.push_to_lua(lua);
lua.set_table(-3);
}
}
}
impl<T: PushToLua> PushCollectionToLua for Vec<T> {
#[inline]
unsafe fn push_to_lua_table(self, lua: crate::lua::State) {
iterator(lua, &mut self.into_iter())
}
}
impl TryPushToLua for SystemTime {
#[inline]
unsafe fn try_push_to_lua(self, lua: crate::lua::State) -> Result<(), Self> {
lua.push_number(self.duration_since(SystemTime::UNIX_EPOCH).map_err(|_| self)?.as_secs_f64());
Ok(())
}
}
/// Pushes all elements in an iterator to a Lua table at the top of the stack.
///
/// **You must create the table yourself**
#[inline]
pub unsafe fn iterator<T: PushToLua, I: Iterator<Item = T>>(lua: crate::lua::State, iter: &mut I) {
for (i, val) in iter.enumerate() {
lua.push_integer((i + 1) as _);
val.push_to_lua(lua);
lua.set_table(-3);
}
}