467 lines
12 KiB
Rust
Executable File
467 lines
12 KiB
Rust
Executable File
use std::{marker::PhantomData, sync::Arc};
|
|
|
|
use mlua_sys as lua;
|
|
|
|
pub mod state;
|
|
use state::*;
|
|
|
|
pub mod table;
|
|
use table::*;
|
|
|
|
pub mod function;
|
|
use function::*;
|
|
|
|
pub mod value;
|
|
use value::*;
|
|
|
|
pub mod guard;
|
|
use guard::*;
|
|
|
|
pub mod userdata;
|
|
use userdata::*;
|
|
|
|
pub mod util;
|
|
use util::*;
|
|
|
|
pub mod chunk;
|
|
use chunk::*;
|
|
|
|
fn main() -> Result<()> {
|
|
let lua = State::new();
|
|
lua.expose_libraries(&[StdLibrary::Debug, StdLibrary::Package]);
|
|
|
|
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 ud = lua.create_userdata(UserdataProxy::<Vec2>::new())?;
|
|
globals.set("Vec2", ud)?;
|
|
|
|
let tbl = lua.create_table()?;
|
|
tbl.set("x", 10)?;
|
|
|
|
//let globals = lua.globals()?;
|
|
globals.set("X", tbl)?;
|
|
|
|
let chunk = lua.load("text.lua", r#"
|
|
require "util"
|
|
|
|
--[[function dump_table(tbl)
|
|
for k, v in pairs(tbl) do
|
|
if type(v) == "table" then
|
|
dump_table(v)
|
|
elseif type(v) == "function" then
|
|
|
|
else
|
|
print(k .. "=" .. tostring(v))
|
|
end
|
|
|
|
end
|
|
end
|
|
|
|
for k, v in pairs(_G) do
|
|
--print("Found global named " .. k)
|
|
|
|
if k == "X" then
|
|
--dump_table(v)
|
|
end
|
|
end]]--
|
|
|
|
function multiply_print(a, b)
|
|
print(a .. " * " .. b .. " = " .. a*b)
|
|
end
|
|
|
|
function multiply_ret(a, b)
|
|
return a * b
|
|
end
|
|
|
|
function say_number(a)
|
|
print("Lua says " .. a)
|
|
end
|
|
|
|
cool_num = 50
|
|
|
|
print("Lua is about to exec native_test")
|
|
local res = native_test(50)
|
|
print("Lua got " .. res .. " back from rust!")
|
|
|
|
print("Vec2: " .. dump_table(Vec2))
|
|
print("Meta Vec2: " .. dump_table(getmetatable(Vec2)))
|
|
|
|
--print("Vec2 is (" .. Vec2.x .. ", " .. Vec2.y .. ")")
|
|
|
|
local v1 = Vec2.new(50, 50)
|
|
print("v1 is (" .. v1.x .. ", " .. v1.y .. ")")
|
|
|
|
local v2 = Vec2.new(500, 500)
|
|
print("v2 is (" .. v2.x .. ", " .. v2.y .. ")")
|
|
|
|
local v_add = v1 + v2
|
|
v_add.x = 90
|
|
print("v_add is (" .. v_add.x .. ", " .. v_add.y .. ")")
|
|
|
|
function f1(v1, v2)
|
|
--print("f1 tb: " .. debug.traceback(1))
|
|
local v_add = v1:add(v2)
|
|
end
|
|
|
|
function f2(v1, v2)
|
|
--print("f2 tb: " .. debug.traceback(1))
|
|
f1(v1, v2)
|
|
end
|
|
|
|
function f3(v1, v2)
|
|
--print("f3 tb: " .. debug.traceback(1))
|
|
f2(v1, v2)
|
|
end
|
|
|
|
--f3(v1, v2)
|
|
"#)?;
|
|
|
|
// I don't care about the result of this execution, so I set the result as a
|
|
// Value which can be anything
|
|
//
|
|
// Chunks can also be executed with: `chunk.execute(())?;`
|
|
let res = lua.execute_chunk::<_, Value>(&chunk, ());
|
|
|
|
// if unwrapped, the new lines in the message would be escaped making
|
|
// the traceback in the error difficult to read.
|
|
if let Err(e) = res {
|
|
panic!("{}", e);
|
|
}
|
|
|
|
unsafe {
|
|
assert_eq!(lua::lua_gettop(lua.state_ptr()), 0); // ensure that nothing is left on the stack
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// A LuaRef is a a handle to something in Lua.
|
|
///
|
|
/// These are created with `luaL_ref`, they can be created from anything on the top of the stack
|
|
/// with [`LuaRef::from_stack`]. The references are automatically freed with `luaL_unref` when
|
|
/// the inner Arc detects a single strong count.
|
|
#[derive(Clone)]
|
|
pub struct LuaRef<'a>(Arc<i32>, &'a State);
|
|
|
|
impl<'a> Drop for LuaRef<'a> {
|
|
fn drop(&mut self) {
|
|
unsafe {
|
|
let s = self.1.state_ptr();
|
|
|
|
if Arc::strong_count(&self.0) == 1 {
|
|
lua::luaL_unref(s, lua::LUA_REGISTRYINDEX, *self.0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<'a> LuaRef<'a> {
|
|
pub fn new(lua_ref: i32, state: &'a State) -> Self {
|
|
Self(Arc::new(lua_ref), state)
|
|
}
|
|
|
|
/// Creates a reference to what is at the top of the stack.
|
|
pub unsafe fn from_stack(state: &'a State) -> Result<Self> {
|
|
let s = state.state_ptr();
|
|
|
|
let r = lua::luaL_ref(s, lua::LUA_REGISTRYINDEX);
|
|
if r == lua::LUA_REFNIL {
|
|
Err(Error::Nil)
|
|
} else {
|
|
Ok(LuaRef::new(r, state))
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<'a> PushToLuaStack<'a> for LuaRef<'a> {
|
|
unsafe fn push_to_lua_stack(&self, state: &State) -> Result<()> {
|
|
let s = state.state_ptr();
|
|
|
|
state.ensure_stack(1)?;
|
|
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(())
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, thiserror::Error)]
|
|
pub enum Error {
|
|
#[error("Syntax error: {0}")]
|
|
Syntax(String),
|
|
/// An error returned from lua
|
|
#[error("Lua runtime error: {0}")]
|
|
Runtime(String),
|
|
#[error("Error when running a __gc metamethod")]
|
|
GcFailure(String),
|
|
/// Ran into a not enough memory error when trying to grow the lua stack.
|
|
#[error("Failed to allocate memory")]
|
|
MemoryAlloc,
|
|
#[error("Ran into a nill value on the stack")]
|
|
Nil,
|
|
#[error("Unexpected type, expected {0} but got {1}")]
|
|
UnexpectedType(String, String),
|
|
#[error("bad argument #{arg_index}{} to `{}` ({error})",
|
|
.arg_name.clone().map(|a| format!(" (name: {})", a)).unwrap_or("".to_string()),
|
|
.func.clone().unwrap_or("Unknown".to_string())
|
|
)]
|
|
BadArgument {
|
|
func: Option<String>,
|
|
arg_index: i32,
|
|
arg_name: Option<String>,
|
|
/// the error that describes what was wrong for this argument
|
|
#[source]
|
|
error: Arc<Error>
|
|
},
|
|
#[error("Incorrect number of arguments, expected {arg_expected}, got {arg_count}")]
|
|
IncorrectArgCount {
|
|
arg_expected: i32,
|
|
arg_count: i32,
|
|
},
|
|
#[error("There is already a registry entry with the key {0}")]
|
|
RegistryConflict(String),
|
|
#[error("Userdata types did not match")]
|
|
UserdataMismatch,
|
|
#[error("Missing meta table for userdata")]
|
|
MissingMetatable,
|
|
#[error("An error occurred when attempting to convert from a ValueVec at value index {value_idx}, cause: {error}")]
|
|
ValueVecError {
|
|
value_idx: i32,
|
|
#[source]
|
|
error: Arc<Error>,
|
|
},
|
|
}
|
|
|
|
impl Error {
|
|
pub fn runtime(msg: &str) -> Self {
|
|
Self::Runtime(msg.to_string())
|
|
}
|
|
|
|
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!("{}\0", self);
|
|
let msg_c = msg.as_ptr() as *const i8;
|
|
lua::luaL_error(lua, msg_c);
|
|
unreachable!();
|
|
}
|
|
}
|
|
|
|
/// A result for use with lua functions
|
|
type Result<T> = core::result::Result<T, Error>;
|
|
|
|
pub trait PushToLuaStack<'a> {
|
|
unsafe fn push_to_lua_stack(&self, state: &'a State) -> Result<()>;
|
|
}
|
|
|
|
pub trait FromLuaStack<'a>: Sized {
|
|
unsafe fn from_lua_stack(state: &'a State) -> Result<Self>;
|
|
}
|
|
|
|
impl<'a> PushToLuaStack<'a> for () {
|
|
unsafe fn push_to_lua_stack(&self, _state: &'a State) -> Result<()> {
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl<'a, T: PushToLuaStack<'a>> PushToLuaStack<'a> for Option<T> {
|
|
unsafe fn push_to_lua_stack(&self, state: &'a State) -> Result<()> {
|
|
if let Some(v) = self {
|
|
v.push_to_lua_stack(state)?;
|
|
} else {
|
|
unsafe {
|
|
lua::lua_pushnil(state.state_ptr());
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
/// Implements PushToLuaStack for a number
|
|
macro_rules! impl_as_lua_number {
|
|
($ty: ident) => {
|
|
impl<'a> AsLua<'a> for $ty {
|
|
fn as_lua(&self, _lua: &'a State) -> crate::Result<Value<'a>> {
|
|
Ok(Value::Number(*self as f64))
|
|
}
|
|
}
|
|
|
|
impl<'a> FromLua<'a> for $ty {
|
|
fn from_lua(_lua: &State, val: Value) -> crate::Result<Self> {
|
|
match val {
|
|
Value::Number(v) => Ok(v as $ty),
|
|
_ => {
|
|
Err(Error::UnexpectedType("Number".to_string(), val.type_name().to_string()))
|
|
},
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<'a> PushToLuaStack<'a> for $ty {
|
|
unsafe fn push_to_lua_stack(&self, state: &'a State) -> crate::Result<()> {
|
|
let v = self.as_lua(state)?;
|
|
v.push_to_lua_stack(state)
|
|
}
|
|
}
|
|
|
|
};
|
|
}
|
|
|
|
impl_as_lua_number!(i8);
|
|
impl_as_lua_number!(i16);
|
|
impl_as_lua_number!(i32);
|
|
impl_as_lua_number!(i64);
|
|
|
|
impl_as_lua_number!(u8);
|
|
impl_as_lua_number!(u16);
|
|
impl_as_lua_number!(u32);
|
|
impl_as_lua_number!(u64);
|
|
|
|
impl_as_lua_number!(f32);
|
|
impl_as_lua_number!(f64);
|
|
|
|
impl<'a> PushToLuaStack<'a> for String {
|
|
unsafe fn push_to_lua_stack(&self, state: &State) -> Result<()> {
|
|
state.ensure_stack(1)?;
|
|
|
|
let s = format!("{}\0", self);
|
|
let cstr = s.as_ptr() as *const i8;
|
|
lua::lua_pushstring(state.state_ptr(), cstr);
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl<'a> FromLua<'a> for String {
|
|
fn from_lua(_lua: &State, val: Value) -> crate::Result<Self> {
|
|
val.as_string().cloned()
|
|
}
|
|
}
|
|
|
|
impl<'a> PushToLuaStack<'a> for &str {
|
|
unsafe fn push_to_lua_stack(&self, state: &State) -> Result<()> {
|
|
state.ensure_stack(1)?;
|
|
|
|
let s = format!("{}\0", self);
|
|
let cstr = s.as_ptr() as *const i8;
|
|
lua::lua_pushstring(state.state_ptr(), cstr);
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
#[allow(dead_code)]
|
|
pub struct Vec3 {
|
|
x: f32,
|
|
y: f32,
|
|
z: f32,
|
|
}
|
|
|
|
impl Userdata for Vec3 {
|
|
fn build<'a>(_builder: &mut UserdataBuilder<'a, Self>) -> crate::Result<()> {
|
|
todo!()
|
|
}
|
|
|
|
fn name() -> String {
|
|
todo!()
|
|
}
|
|
}
|
|
|
|
pub struct Vec2 {
|
|
x: f32,
|
|
y: f32,
|
|
}
|
|
|
|
impl Userdata for Vec2 {
|
|
|
|
fn build<'a>(builder: &mut UserdataBuilder<'a, Vec2>) -> crate::Result<()> {
|
|
builder
|
|
.field_getter("x", |_, this| Ok(this.x))
|
|
.field_getter("y", |_, this| Ok(this.y))
|
|
|
|
.field_setter("x", |_, this, x: f32| this.x = x)
|
|
.field_setter("y", |_, this, y: f32| this.y = y)
|
|
|
|
.function("new", |lua, (x, y)| {
|
|
lua.create_userdata(Vec2 { x, y, })
|
|
})
|
|
|
|
// method test
|
|
.method("add", |lua, lhs: &Vec2, (rhs,): (&Vec3,)| {
|
|
let lx = lhs.x;
|
|
let ly = lhs.y;
|
|
|
|
let rx = rhs.x;
|
|
let ry = rhs.y;
|
|
|
|
lua.create_userdata(Vec2 { x: lx + rx, y: ly + ry, })
|
|
})
|
|
|
|
.meta_method(MetaMethod::Add, |lua, lhs: &Vec2, (rhs,): (&Vec2,)| {
|
|
let lx = lhs.x;
|
|
let ly = lhs.y;
|
|
|
|
let rx = rhs.x;
|
|
let ry = rhs.y;
|
|
|
|
lua.create_userdata(Vec2 { x: lx + rx, y: ly + ry, })
|
|
});
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn name() -> String {
|
|
"Vec2".to_string()
|
|
}
|
|
}
|
|
|
|
pub struct UserdataProxy<T: Userdata>(PhantomData<T>);
|
|
|
|
impl<T: Userdata> UserdataProxy<T> {
|
|
pub fn new() -> Self {
|
|
Self(PhantomData)
|
|
}
|
|
}
|
|
|
|
impl<'a, T: Userdata> AsLua<'a> for UserdataProxy<T> {
|
|
fn as_lua(&self, _lua: &'a State) -> crate::Result<Value<'a>> {
|
|
todo!()
|
|
}
|
|
}
|
|
|
|
impl<T: Userdata> Userdata for UserdataProxy<T> {
|
|
fn build<'a>(builder: &mut UserdataBuilder<'a, Self>) -> crate::Result<()> {
|
|
|
|
let mut other = UserdataBuilder::<T>::new();
|
|
T::build(&mut other)?;
|
|
|
|
// only the functions need to be added since they're the only thing usable from a proxy
|
|
builder.functions = other.functions;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn name() -> String {
|
|
let name = format!("{}Proxy", T::name());
|
|
name
|
|
}
|
|
} |