Handle arguments for lua functions in a better way

This commit is contained in:
IQuant 2024-11-25 18:34:30 +03:00
parent 66a5a009e2
commit d62fb39a93
3 changed files with 115 additions and 14 deletions

View file

@ -14,6 +14,7 @@ noita_api_macro::generate_components!();
pub mod raw {
use super::{Color, ComponentID, EntityID, Obj};
use crate::lua::LuaPutValue;
use std::borrow::Cow;
use crate::lua::LuaState;

View file

@ -1,6 +1,7 @@
pub mod lua_bindings;
use std::{
borrow::Cow,
cell::Cell,
ffi::{c_char, c_int, CStr},
mem, slice,
@ -10,6 +11,8 @@ use std::{
use eyre::{bail, Context, OptionExt};
use lua_bindings::{lua_CFunction, lua_State, Lua51, LUA_GLOBALSINDEX};
use crate::{Color, ComponentID, EntityID, Obj};
thread_local! {
static CURRENT_LUA_STATE: Cell<Option<LuaState>> = Cell::default();
}
@ -121,6 +124,7 @@ impl LuaState {
}
}
/// Used for types that can be returned from functions that were defined in rust to lua.
pub trait LuaFnRet {
fn do_return(self, lua: LuaState) -> c_int;
}
@ -150,3 +154,93 @@ impl<R: LuaFnRet> LuaFnRet for eyre::Result<R> {
}
}
}
/// Trait for arguments that can be put on lua stack.
pub(crate) trait LuaPutValue {
fn put(&self, lua: LuaState);
fn is_non_empty(&self) -> bool {
true
}
}
impl LuaPutValue for i32 {
fn put(&self, lua: LuaState) {
lua.push_integer(*self as isize);
}
}
impl LuaPutValue for isize {
fn put(&self, lua: LuaState) {
lua.push_integer(*self);
}
}
impl LuaPutValue for u32 {
fn put(&self, lua: LuaState) {
lua.push_integer(unsafe { mem::transmute::<_, i32>(*self) as isize });
}
}
impl LuaPutValue for f64 {
fn put(&self, lua: LuaState) {
lua.push_number(*self);
}
}
impl LuaPutValue for bool {
fn put(&self, lua: LuaState) {
lua.push_bool(*self);
}
}
impl LuaPutValue for Cow<'_, str> {
fn put(&self, lua: LuaState) {
lua.push_string(self.as_ref());
}
}
impl LuaPutValue for str {
fn put(&self, lua: LuaState) {
lua.push_string(self);
}
}
impl LuaPutValue for EntityID {
fn put(&self, lua: LuaState) {
self.0.put(lua);
}
}
impl LuaPutValue for ComponentID {
fn put(&self, lua: LuaState) {
self.0.put(lua);
}
}
impl LuaPutValue for Color {
fn put(&self, _lua: LuaState) {
todo!()
}
}
impl LuaPutValue for Obj {
fn put(&self, _lua: LuaState) {
todo!()
}
}
impl<T: LuaPutValue> LuaPutValue for Option<T> {
fn put(&self, lua: LuaState) {
match self {
Some(val) => val.put(lua),
None => lua.push_nil(),
}
}
fn is_non_empty(&self) -> bool {
match self {
Some(val) => val.is_non_empty(),
None => false,
}
}
}

View file

@ -228,19 +228,23 @@ fn generate_code_for_api_fn(api_fn: ApiFn) -> proc_macro2::TokenStream {
}
});
let put_args = api_fn.args.iter().map(|arg| {
let optional = arg.default.is_some();
let put_args_pre = api_fn.args.iter().enumerate().map(|(i, arg)| {
let arg_name = format_ident!("{}", arg.name);
let arg_push = arg.typ.generate_lua_push(arg_name.clone());
if optional {
let i = i as i32;
quote! {
match #arg_name {
Some(#arg_name) => #arg_push,
None => lua.push_nil(),
if LuaPutValue::is_non_empty(&#arg_name) {
last_non_empty = #i;
}
}
} else {
arg_push
});
let put_args = api_fn.args.iter().enumerate().map(|(i, arg)| {
let arg_name = format_ident!("{}", arg.name);
let i = i as i32;
quote! {
if #i <= last_non_empty {
LuaPutValue::put(&#arg_name, lua);
}
}
});
@ -266,7 +270,6 @@ fn generate_code_for_api_fn(api_fn: ApiFn) -> proc_macro2::TokenStream {
let fn_name_c = name_to_c_literal(api_fn.fn_name);
let arg_count = api_fn.args.len() as i32;
let ret_count = api_fn.rets.len() as i32;
quote! {
@ -275,9 +278,12 @@ fn generate_code_for_api_fn(api_fn: ApiFn) -> proc_macro2::TokenStream {
let lua = LuaState::current()?;
lua.get_global(#fn_name_c);
#(#put_args;)*
lua.call(#arg_count, #ret_count);
let mut last_non_empty: i32 = -1;
#(#put_args_pre)*
#(#put_args)*
lua.call(last_non_empty+1, #ret_count);
let ret = Ok(#ret_expr);
lua.pop_last_n(#ret_count);