elua/src/main.rs

366 lines
9.3 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::*;
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("Vec2", Vec2 { x: 0.0, y: 0.0})?;
let ud = lua.create_userdata("Vec2", UserdataProxy::<Vec2>::new())?;
globals.set("Vec2", ud)?;
let tbl = lua.create_table()?;
tbl.set("x", 10)?;
//let globals = lua.globals()?;
globals.set("X", tbl)?;
lua.execute(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
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 .. ")")
"#).unwrap();
unsafe {
assert_eq!(lua::lua_gettop(lua.state_ptr()), 0); // ensure that nothing is left on the stack
}
Ok(())
}
#[derive(Clone)]
pub struct LuaRef(Arc<i32>);
impl From<i32> for LuaRef {
fn from(value: i32) -> Self {
Self(Arc::new(value))
}
}
impl LuaRef {
/// Creates a reference to what is at the top of the stack.
pub unsafe fn from_stack(state: &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::from(r))
}
}
}
impl<'a> PushToLuaStack<'a> for LuaRef {
unsafe fn push_to_lua_stack(&self, state: &State) -> Result<()> {
let s = state.state_ptr();
unsafe {
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 {
/// An error returned from lua
#[error("Lua runtime error: {0}")]
Runtime(String),
/// Ran into a not enough memory error when trying to grow the lua stack.
#[error("Ran out of memory when attempting to use `lua_checkstack`")]
Oom,
#[error("Ran into a nill value on the stack")]
Nil,
#[error("Unexpected type, expected {0} but got {1}")]
UnexpectedType(String, String),
#[error("Bad argument provided to {func:?}! Argument #{arg_index} (name: {arg_name:?}), cause: {error}")]
BadArgument {
func: Option<String>,
arg_index: i32,
arg_name: Option<String>,
/// the error that describes what was wrong for this argument
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)
}
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(())
}
}
/// 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(())
}
}
pub struct Vec2 {
x: f32,
y: f32,
}
impl Userdata for Vec2 {
fn build<'a>(builder: &mut UserdataBuilder<'a, Vec2>) -> crate::Result<()> {
builder.name("Vec2")
.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", Vec2 { x, y, })
})
// method test
.method("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", 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", Vec2 { x: lx + rx, y: ly + ry, })
});
Ok(())
}
}
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)?;
let name = format!("{}Proxy", other.name.unwrap());
builder.name = Some(name);
// only the functions need to be added since they're the only thing usable from a proxy
builder.functions = other.functions;
Ok(())
}
}