Add meta methods, methods, functions, and field setters to UserData
This commit is contained in:
parent
9ef4203619
commit
a8db62fe08
|
@ -61,7 +61,7 @@ impl<'a> Function<'a> {
|
|||
}
|
||||
|
||||
impl<'a> AsLua<'a> for Function<'a> {
|
||||
fn as_lua(&self, lua: &'a State) -> crate::Result<crate::Value<'a>> {
|
||||
fn as_lua(&self, _lua: &'a State) -> crate::Result<crate::Value<'a>> {
|
||||
Ok(crate::Value::Function(self.clone()))
|
||||
}
|
||||
}
|
||||
|
@ -135,6 +135,7 @@ macro_rules! impl_function_arg_tuple {
|
|||
}
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
impl<'a, $first: FromLuaStack<'a>, $($name: FromLuaStack<'a>,)+> FromLuaStackMulti<'a> for ($first, $($name,)+) {
|
||||
fn len() -> usize {
|
||||
$count
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
use std::{any::type_name, ffi::CStr, marker::PhantomData, mem, ptr, str::Utf8Error, sync::Arc};
|
||||
use std::{marker::PhantomData, sync::Arc};
|
||||
|
||||
use lua::{lua_typename, lua_type};
|
||||
use mlua_sys as lua;
|
||||
|
||||
pub mod state;
|
||||
|
@ -90,8 +89,18 @@ fn main() -> Result<()> {
|
|||
|
||||
print("Vec2: " .. dump_table(Vec2))
|
||||
print("Meta Vec2: " .. dump_table(getmetatable(Vec2)))
|
||||
--local vec2 = Vec2.new(50, 50)
|
||||
|
||||
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 {
|
||||
|
@ -198,6 +207,12 @@ 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) => {
|
||||
|
@ -207,7 +222,7 @@ macro_rules! impl_as_lua_number {
|
|||
}
|
||||
}
|
||||
|
||||
impl FromLua for $ty {
|
||||
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),
|
||||
|
@ -253,7 +268,7 @@ impl<'a> PushToLuaStack<'a> for String {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> FromLuaStack<'a> for String {
|
||||
/* impl<'a> FromLuaStack<'a> for String {
|
||||
unsafe fn from_lua_stack(state: &'a State) -> Result<Self> {
|
||||
let s = state.state_ptr();
|
||||
|
||||
|
@ -263,6 +278,12 @@ impl<'a> FromLuaStack<'a> for String {
|
|||
let cstr = CStr::from_ptr(cstr);
|
||||
Ok(cstr.to_str().unwrap().to_string())
|
||||
}
|
||||
} */
|
||||
|
||||
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 {
|
||||
|
@ -286,7 +307,23 @@ 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_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, })
|
||||
})
|
||||
.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(())
|
||||
}
|
||||
|
@ -301,7 +338,7 @@ impl<T: Userdata> UserdataProxy<T> {
|
|||
}
|
||||
|
||||
impl<'a, T: Userdata> AsLua<'a> for UserdataProxy<T> {
|
||||
fn as_lua(&self, lua: &'a State) -> crate::Result<Value<'a>> {
|
||||
fn as_lua(&self, _lua: &'a State) -> crate::Result<Value<'a>> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
@ -314,12 +351,12 @@ impl<T: Userdata> Userdata for UserdataProxy<T> {
|
|||
|
||||
builder.name(&other.name.unwrap());
|
||||
|
||||
for (lbl, getters) in other.field_getters.into_iter() {
|
||||
/* for (lbl, getters) in other.field_getters.into_iter() {
|
||||
let wrap = |state: &State, data: &Self| {
|
||||
Ok(())
|
||||
};
|
||||
builder.field_getter(&lbl, wrap);
|
||||
}
|
||||
} */
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
use core::ffi;
|
||||
use std::{ffi::{CString, CStr}, mem, ptr::{self, NonNull}, str::Utf8Error};
|
||||
use std::{collections::HashMap, ffi::{CString, CStr}, mem, ptr::{self, NonNull}, str::Utf8Error};
|
||||
|
||||
use mlua_sys as lua;
|
||||
|
||||
use crate::{ensure_type, lua_error_guard, AnyUserdata, Error, FromLuaStack, FromLuaStackMulti, Function, LuaRef, PushToLuaStack, PushToLuaStackMulti, Result, StackGuard, Table, Userdata, UserdataBuilder, Value};
|
||||
use crate::{lua_error_guard, AnyUserdata, AsLua, Error, FromLuaStack, FromLuaVec, Function, LuaRef, MetaMethod, PushToLuaStack, PushToLuaStackMulti, Result, StackGuard, Table, Userdata, UserdataBuilder, Value, ValueVec};
|
||||
|
||||
pub fn ptr_to_string(ptr: *const i8) -> std::result::Result<String, Utf8Error> {
|
||||
let c = unsafe { CStr::from_ptr(ptr) };
|
||||
|
@ -117,7 +117,12 @@ impl State {
|
|||
if lua::luaL_dostring(lua, text_c) != 0 {
|
||||
let error_c = CStr::from_ptr(lua::lua_tostring(lua, -1));
|
||||
let error_str = error_c.to_str()
|
||||
.expect("Error bytes are invalid!");
|
||||
.unwrap_or_else(|_| {
|
||||
let b = error_c.to_bytes();
|
||||
std::str::from_utf8_unchecked(b)
|
||||
});
|
||||
//.expect("Error bytes are invalid!");
|
||||
//std::str::from
|
||||
return Err(Error::runtime(error_str));
|
||||
}
|
||||
}
|
||||
|
@ -177,6 +182,7 @@ impl State {
|
|||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub(crate) unsafe fn print_stack(state: &State) {
|
||||
let s = state.state_ptr();
|
||||
let t = lua::lua_gettop(s);
|
||||
|
@ -192,7 +198,7 @@ impl State {
|
|||
|
||||
pub fn create_function<'a, A, R, F>(&self, f: F) -> Result<Function>
|
||||
where
|
||||
A: FromLuaStackMulti<'a>,
|
||||
A: FromLuaVec<'a>,
|
||||
R: PushToLuaStackMulti<'a>,
|
||||
F: Fn(&'a State, A) -> Result<R> + 'static,
|
||||
{
|
||||
|
@ -236,17 +242,13 @@ impl State {
|
|||
state: &'a State,
|
||||
}
|
||||
|
||||
let wrapper_fn = move |lua: &State, narg: i32| -> i32 {
|
||||
let wrapper_fn = move |lua: &State, _narg: i32| -> i32 {
|
||||
unsafe {
|
||||
let lua: &State = mem::transmute(lua); // transmute lifetimes
|
||||
|
||||
if narg != A::len() as i32 {
|
||||
Error::Runtime(format!("incorrect number of arguments provided to lua function, expected {}", A::len()))
|
||||
.throw_lua(lua.state_ptr());
|
||||
}
|
||||
|
||||
lua_error_guard(lua, || {
|
||||
let args = A::results_from_lua_stack(lua)?;
|
||||
let vec = ValueVec::from_lua_stack(lua)?;
|
||||
let args = A::from_lua_value_vec(lua, vec)?;
|
||||
|
||||
let r = f(lua, args)?;
|
||||
r.push_args_to_lua_stack(lua)?;
|
||||
|
@ -291,13 +293,12 @@ impl State {
|
|||
// attempt to get the metatable
|
||||
lua::lua_pushstring(s, name_cstr);
|
||||
let ty = lua::lua_rawget(s, lua::LUA_REGISTRYINDEX);
|
||||
|
||||
|
||||
if ty == lua::LUA_TNIL {
|
||||
lua::lua_pop(s, 1); // remove nil
|
||||
|
||||
// if the metatable is not made yet, make it
|
||||
let mt = self.create_userdata_metatable::<T>()?;
|
||||
//let mt = mt.get_metatable().unwrap(); // the table 100% has a metatable
|
||||
mt.push_to_lua_stack(self)?;
|
||||
} else if ty != lua::LUA_TTABLE {
|
||||
return Err(Error::RegistryConflict(name.to_string()));
|
||||
|
@ -314,21 +315,63 @@ impl State {
|
|||
T::build(&mut builder)?;
|
||||
|
||||
let getters = builder.field_getters;
|
||||
let setters = builder.field_setters;
|
||||
let name = builder.name.unwrap();
|
||||
|
||||
let mut fns_table = HashMap::new();
|
||||
for (func_name, func) in builder.functions.into_iter() {
|
||||
let lua_func = self.create_function(move |lua: &State, vals: ValueVec| {
|
||||
func(lua, vals)
|
||||
})?;
|
||||
// Safety: This will be alive for as long as the lua state is alive
|
||||
let lua_func: Function<'_> = unsafe { mem::transmute(lua_func) };
|
||||
fns_table.insert(func_name, lua_func);
|
||||
}
|
||||
|
||||
let mt = self.create_meta_table(&name)?;
|
||||
|
||||
if !getters.is_empty() {
|
||||
// dont create an index function if there are no getters,
|
||||
// or if an index metamethod was defined
|
||||
if !getters.is_empty() || !builder.meta_methods.contains_key("__index") {
|
||||
let index_fn = self.create_function(move |lua: &State, (ud, key): (AnyUserdata, String)| {
|
||||
if let Some(getter) = getters.get(&key) {
|
||||
let r = getter(lua, Value::Userdata(ud))?;
|
||||
let r = getter(lua, ValueVec::from(Value::Userdata(ud)))?;
|
||||
Ok(r)
|
||||
} else if let Some(function) = fns_table.get(&key) {
|
||||
Ok(Value::Function(function.clone()))
|
||||
} else {
|
||||
Ok(Value::Nil)
|
||||
}
|
||||
})?;
|
||||
|
||||
mt.set_meta("__index", index_fn)?;
|
||||
mt.set_meta(MetaMethod::Index, index_fn)?;
|
||||
}
|
||||
|
||||
// dont create an index function if there are no setters,
|
||||
// or if a new index metamethod was defined
|
||||
if !setters.is_empty() || !builder.meta_methods.contains_key("__newindex") {
|
||||
let index_fn = self.create_function(move |lua: &State, (ud, key, val): (AnyUserdata, String, Value)| {
|
||||
if let Some(setter) = setters.get(&key) {
|
||||
let mut vec = ValueVec::from(ud.as_lua(lua)?);
|
||||
vec.push_back(val);
|
||||
|
||||
// ignore value result, the wrapper function doesn't return anything
|
||||
setter(lua, vec)?;
|
||||
|
||||
Ok(Value::None)
|
||||
} else {
|
||||
Ok(Value::Nil)
|
||||
}
|
||||
})?;
|
||||
|
||||
mt.set_meta(MetaMethod::NewIndex, index_fn)?;
|
||||
}
|
||||
|
||||
for (mm_name, mm) in builder.meta_methods.into_iter() {
|
||||
let mm_func = self.create_function(move |lua: &State, vals: ValueVec| {
|
||||
mm(lua, vals)
|
||||
})?;
|
||||
mt.set_meta(mm_name, mm_func)?;
|
||||
}
|
||||
|
||||
Ok(mt)
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
|
||||
use std::{ffi::CStr, ops::Deref, sync::Arc};
|
||||
|
||||
use mlua_sys as lua;
|
||||
|
||||
use crate::{ensure_type, FromLuaStack, LuaRef, PushToLuaStack, Result, StackGuard, State};
|
||||
|
@ -165,7 +163,6 @@ impl<'a> Table<'a> {
|
|||
val.push_to_lua_stack(self.state)?;
|
||||
|
||||
lua::lua_rawset(s, -3);
|
||||
lua::lua_pop(self.state.state_ptr(), -1);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
|
|
@ -1,12 +1,89 @@
|
|||
use std::{collections::HashMap, marker::PhantomData, sync::Arc};
|
||||
use std::{collections::HashMap, marker::PhantomData};
|
||||
|
||||
use crate::{ensure_type, AsLua, FromLua, FromLuaStack, LuaRef, PushToLuaStack, State, Table, Value};
|
||||
use crate::{ensure_type, AsLua, FromLua, FromLuaStack, FromLuaVec, LuaRef, PushToLuaStack, State, Value, ValueVec};
|
||||
|
||||
use mlua_sys as lua;
|
||||
|
||||
//pub type FieldSetter<T> = fn(lua: &State, this: &T);
|
||||
//pub type FieldGetter<T, U> = fn(lua: &State, this: &T, val: &U);
|
||||
|
||||
/// An enum representing all Lua MetaMethods
|
||||
/// https://gist.github.com/oatmealine/655c9e64599d0f0dd47687c1186de99f
|
||||
pub enum MetaMethod {
|
||||
Add,
|
||||
Sub,
|
||||
Mul,
|
||||
Div,
|
||||
Unm,
|
||||
Mod,
|
||||
Pow,
|
||||
IDiv,
|
||||
BAnd,
|
||||
BOr,
|
||||
BXOr,
|
||||
BNot,
|
||||
Shl,
|
||||
Shr,
|
||||
Eq,
|
||||
Lt,
|
||||
Le,
|
||||
Concat,
|
||||
Len,
|
||||
Index,
|
||||
NewIndex,
|
||||
Call,
|
||||
Mode,
|
||||
Close,
|
||||
Gc,
|
||||
ToString,
|
||||
Metatable,
|
||||
Name,
|
||||
Pairs,
|
||||
}
|
||||
|
||||
impl AsRef<str> for MetaMethod {
|
||||
fn as_ref(&self) -> &str {
|
||||
match self {
|
||||
MetaMethod::Add => "__add",
|
||||
MetaMethod::Sub => "__sub",
|
||||
MetaMethod::Mul => "__mul",
|
||||
MetaMethod::Div => "__div",
|
||||
MetaMethod::Unm => "__unm",
|
||||
MetaMethod::Mod => "__mod",
|
||||
MetaMethod::Pow => "__pow",
|
||||
MetaMethod::IDiv => "__idiv",
|
||||
MetaMethod::BAnd => "__band",
|
||||
MetaMethod::BOr => "__bor",
|
||||
MetaMethod::BXOr => "__bxor",
|
||||
MetaMethod::BNot => "__bnot",
|
||||
MetaMethod::Shl => "__shl",
|
||||
MetaMethod::Shr => "__shr",
|
||||
MetaMethod::Eq => "__eq",
|
||||
MetaMethod::Lt => "__lt",
|
||||
MetaMethod::Le => "__le",
|
||||
MetaMethod::Concat => "__concat",
|
||||
MetaMethod::Len => "__len",
|
||||
MetaMethod::Index => "__index",
|
||||
MetaMethod::NewIndex => "__newindex",
|
||||
MetaMethod::Call => "__call",
|
||||
MetaMethod::Mode => "__mode",
|
||||
MetaMethod::Close => "__close",
|
||||
MetaMethod::Gc => "__gc",
|
||||
MetaMethod::ToString => "__tostring",
|
||||
MetaMethod::Metatable => "__metatable",
|
||||
MetaMethod::Name => "__name",
|
||||
MetaMethod::Pairs => "__pairs",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> PushToLuaStack<'a> for MetaMethod {
|
||||
unsafe fn push_to_lua_stack(&self, state: &'a State) -> crate::Result<()> {
|
||||
let s = self.as_ref().to_string();
|
||||
s.push_to_lua_stack(state)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait FieldSetter {
|
||||
fn set_field(&self, val: Value);
|
||||
}
|
||||
|
@ -15,11 +92,14 @@ pub trait FieldGetter {
|
|||
fn get_field(&self) -> Value;
|
||||
}
|
||||
|
||||
type UserdataFn<'a> = Box<dyn Fn(&'a State, Value<'a>) -> crate::Result<Value<'a>>>;
|
||||
type UserdataFn<'a> = Box<dyn Fn(&'a State, ValueVec<'a>) -> crate::Result<Value<'a>>>;
|
||||
|
||||
pub struct UserdataBuilder<'a, T> {
|
||||
pub(crate) name: Option<String>,
|
||||
pub(crate) field_getters: HashMap<String, UserdataFn<'a>>,
|
||||
pub(crate) field_setters: HashMap<String, UserdataFn<'a>>,
|
||||
pub(crate) functions: HashMap<String, UserdataFn<'a>>,
|
||||
pub(crate) meta_methods: HashMap<String, UserdataFn<'a>>,
|
||||
_marker: PhantomData<T>,
|
||||
}
|
||||
|
||||
|
@ -28,6 +108,9 @@ impl<'a, T> UserdataBuilder<'a, T> {
|
|||
Self {
|
||||
name: None,
|
||||
field_getters: HashMap::new(),
|
||||
field_setters: HashMap::new(),
|
||||
functions: HashMap::new(),
|
||||
meta_methods: HashMap::new(),
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
|
@ -42,7 +125,8 @@ impl<'a, T> UserdataBuilder<'a, T> {
|
|||
F: Fn(&'a State, &T) -> crate::Result<R> + 'static,
|
||||
R: AsLua<'a>,
|
||||
{
|
||||
let wrap = move |lua: &'a State, val: Value<'a>| {
|
||||
let wrap = move |lua: &'a State, mut val: ValueVec<'a>| {
|
||||
let val = val.pop_front().unwrap();
|
||||
let this = val.as_userdata().unwrap(); // if this panics, its a bug
|
||||
let this = this.as_ref::<T>()?;
|
||||
f(lua, this).and_then(|r| r.as_lua(lua))
|
||||
|
@ -52,68 +136,81 @@ impl<'a, T> UserdataBuilder<'a, T> {
|
|||
self
|
||||
}
|
||||
|
||||
pub fn into_meta_table(self, state: &'a State, table: &Table<'a>) -> crate::Result<()> {
|
||||
//let this: Self = unsafe { std::mem::transmute(self) };
|
||||
//let getters = *self.field_getters;
|
||||
|
||||
/* let index_fn = state.create_function(move |lua: &'a State, (ud, key): (AnyUserdata<'a>, String)| {
|
||||
let ud_ref = ud.as_ref::<T>()?;
|
||||
pub fn field_setter<F, V>(&mut self, name: &str, f: F) -> &mut Self
|
||||
where
|
||||
F: Fn(&'a State, &mut T, V) -> () + 'static,
|
||||
V: FromLua<'a>,
|
||||
{
|
||||
let wrap = move |lua: &'a State, mut val: ValueVec<'a>| {
|
||||
let lua_val = val.pop_front().unwrap();
|
||||
let this = lua_val.as_userdata().unwrap(); // if this panics, its a bug
|
||||
let this = this.as_mut::<T>()?;
|
||||
|
||||
if let Some(getter) = self.field_getters.get(&key) {
|
||||
let r = getter(lua, ome(ud_ref), Value::Nil)?;
|
||||
let lua_val = val.pop_front().unwrap();
|
||||
let v_arg = V::from_lua(lua, lua_val)?;
|
||||
|
||||
let r = unsafe { std::mem::transmute(r) };
|
||||
f(lua, this, v_arg).as_lua(lua)
|
||||
};
|
||||
self.field_setters.insert(name.to_string(), Box::new(wrap));
|
||||
|
||||
Ok(r)
|
||||
} else {
|
||||
//ud.get::<_, Value<'a>>(key)
|
||||
Ok(Value::Nil)
|
||||
}
|
||||
})?;
|
||||
|
||||
table.set("__index", index_fn)?; */
|
||||
|
||||
Ok(())
|
||||
self
|
||||
}
|
||||
|
||||
/* pub fn wrap_builder<U, F>(this: &mut UserdataBuilder<'a, T>, other: UserdataBuilder<'a, U>, to_proxied: F)
|
||||
pub fn function<F, R, A>(&mut self, name: &str, f: F) -> &mut Self
|
||||
where
|
||||
F: Fn(&'a T) -> &'a U + 'a,
|
||||
F: Fn(&'a State, A) -> crate::Result<R> + 'static,
|
||||
A: FromLuaVec<'a>,
|
||||
R: AsLua<'a>,
|
||||
{
|
||||
let to_proxied = Arc::new(to_proxied);
|
||||
for (lbl, getter) in other.field_getters.into_iter() {
|
||||
let to_proxied_cl = to_proxied.clone();
|
||||
let new_getter = move |lua: &'a State, this: Option<&T>, val: Value<'a>| {
|
||||
let proxy = this.map(|t| to_proxied_cl(t));
|
||||
getter(lua, proxy, val)
|
||||
};
|
||||
this.field_getters.insert(lbl, Box::new(new_getter));
|
||||
}
|
||||
} */
|
||||
let wrap = move |lua: &'a State, val: ValueVec<'a>| {
|
||||
let args = A::from_lua_value_vec(lua, val)?;
|
||||
f(lua, args).and_then(|r| r.as_lua(lua))
|
||||
};
|
||||
self.functions.insert(name.to_string(), Box::new(wrap));
|
||||
|
||||
/* pub fn into_userdata(self, state: &'a State, data: T) -> crate::Result<Table<'a>> {
|
||||
let name = self.name
|
||||
.expect("No name was set for userdata!");
|
||||
self
|
||||
}
|
||||
|
||||
let getters = self.field_getters.clone();
|
||||
let index_fn = state.create_function(move |lua: &'a State, (ud, key): (AnyUserdata<'a>, String)| {
|
||||
let ud_ref = ud.as_ref::<T>()?;
|
||||
pub fn method<F, R, A>(&mut self, name: &str, f: F) -> &mut Self
|
||||
where
|
||||
F: Fn(&'a State, &T, A) -> crate::Result<R> + 'static,
|
||||
A: FromLuaVec<'a>,
|
||||
R: AsLua<'a>,
|
||||
{
|
||||
let wrap = move |lua: &'a State, mut val: ValueVec<'a>| {
|
||||
let this_val = val.pop_front().unwrap();
|
||||
let this = this_val.as_userdata().unwrap(); // if this panics, its a bug
|
||||
let this = this.as_ref::<T>()?;
|
||||
|
||||
if let Some(getter) = getters.get(&key) {
|
||||
getter(lua, Some(ud_ref), Value::Nil)
|
||||
} else {
|
||||
//ud.get::<_, Value<'a>>(key)
|
||||
Ok(Value::Nil)
|
||||
}
|
||||
let args = A::from_lua_value_vec(lua, val)?;
|
||||
|
||||
f(lua, this, args).and_then(|r| r.as_lua(lua))
|
||||
};
|
||||
self.functions.insert(name.to_string(), Box::new(wrap));
|
||||
|
||||
//todo!()
|
||||
})?;
|
||||
self
|
||||
}
|
||||
|
||||
let mt = state.create_meta_table(&name)?;
|
||||
mt.set("__index", index_fn)?;
|
||||
pub fn meta_method<N, F, R, A>(&mut self, name: N, f: F) -> &mut Self
|
||||
where
|
||||
N: AsRef<str>,
|
||||
F: Fn(&'a State, &T, A) -> crate::Result<R> + 'static,
|
||||
A: FromLuaVec<'a>,
|
||||
R: AsLua<'a>,
|
||||
{
|
||||
let wrap = move |lua: &'a State, mut val: ValueVec<'a>| {
|
||||
let this_val = val.pop_front().unwrap();
|
||||
let this = this_val.as_userdata().unwrap(); // if this panics, its a bug
|
||||
let this = this.as_ref::<T>()?;
|
||||
|
||||
Ok(mt)
|
||||
} */
|
||||
let args = A::from_lua_value_vec(lua, val)?;
|
||||
|
||||
f(lua, this, args).and_then(|r| r.as_lua(lua))
|
||||
};
|
||||
self.meta_methods.insert(name.as_ref().to_string(), Box::new(wrap));
|
||||
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Userdata: Sized {
|
||||
|
@ -141,7 +238,17 @@ impl<'a> AnyUserdata<'a> {
|
|||
|
||||
self.lref.push_to_lua_stack(self.state)?;
|
||||
let cptr = lua::lua_touserdata(s, -1);
|
||||
Ok(cptr.cast::<T>().as_ref().unwrap())
|
||||
Ok(cptr.cast::<T>().as_ref().unwrap()) // TODO: Ensure this userdata matches the type of T
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_mut<T>(&self) -> crate::Result<&'a mut T> {
|
||||
unsafe {
|
||||
let s = self.state.state_ptr();
|
||||
|
||||
self.lref.push_to_lua_stack(self.state)?;
|
||||
let cptr = lua::lua_touserdata(s, -1);
|
||||
Ok(cptr.cast::<T>().as_mut().unwrap()) // TODO: Ensure this userdata matches the type of T
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -161,4 +268,23 @@ impl<'a> PushToLuaStack<'a> for AnyUserdata<'a> {
|
|||
unsafe fn push_to_lua_stack(&self, state: &'a State) -> crate::Result<()> {
|
||||
self.lref.push_to_lua_stack(state)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> AsLua<'a> for AnyUserdata<'a> {
|
||||
fn as_lua(&self, _lua: &'a State) -> crate::Result<Value<'a>> {
|
||||
Ok(Value::Userdata(self.clone()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FromLua<'a> for AnyUserdata<'a> {
|
||||
fn from_lua(_lua: &State, val: Value<'a>) -> crate::Result<Self> {
|
||||
val.into_userdata()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Userdata> FromLua<'a> for &'a T {
|
||||
fn from_lua(_lua: &'a State, val: Value<'a>) -> crate::Result<Self> {
|
||||
let ud = val.into_userdata()?;
|
||||
ud.as_ref::<T>()
|
||||
}
|
||||
}
|
|
@ -22,8 +22,6 @@ pub unsafe fn ensure_type(state: &State, typ: i32, idx: i32) -> crate::Result<()
|
|||
let s = cstr.to_str()
|
||||
.expect("Lua type has invalid bytes!");
|
||||
|
||||
println!("ty = {}, typ = {}", lua_type, typ);
|
||||
|
||||
Err(crate::Error::UnexpectedType(exp_s.to_string(), s.to_string()))
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
use std::ffi::CStr;
|
||||
use std::{collections::VecDeque, ffi::CStr, ops::{Deref, DerefMut}};
|
||||
|
||||
use crate::{AnyUserdata, FromLuaStack, Function, PushToLuaStack, State, Table};
|
||||
|
||||
|
@ -6,6 +6,9 @@ use mlua_sys as lua;
|
|||
|
||||
#[derive(Clone)]
|
||||
pub enum Value<'a> {
|
||||
/// A None value means that nothing will be pushed to the lua stack.
|
||||
/// This is different than Nil, which does get pushed to the stack.
|
||||
None,
|
||||
Nil,
|
||||
Number(f64),
|
||||
String(String),
|
||||
|
@ -21,6 +24,7 @@ impl<'a> Value<'a> {
|
|||
|
||||
pub fn type_name(&self) -> &'static str {
|
||||
match self {
|
||||
Value::None => "None",
|
||||
Value::Nil => "Nil",
|
||||
Value::Number(_) => "Number",
|
||||
Value::String(_) => "String",
|
||||
|
@ -38,6 +42,27 @@ impl<'a> Value<'a> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Consumes self, and attempts to get `AnyUserdata`.
|
||||
///
|
||||
/// Returns an error if this value is not userdata
|
||||
pub fn into_userdata(self) -> crate::Result<AnyUserdata<'a>> {
|
||||
match self {
|
||||
Value::Userdata(ud) => Ok(ud),
|
||||
_ => {
|
||||
Err(crate::Error::UnexpectedType("Userdata".to_string(), self.type_name().to_string()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_string(&self) -> crate::Result<&String> {
|
||||
match self {
|
||||
Value::String(s) => Ok(s),
|
||||
_ => {
|
||||
Err(crate::Error::UnexpectedType("String".to_string(), self.type_name().to_string()))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> PushToLuaStack<'a> for Value<'a> {
|
||||
|
@ -45,6 +70,9 @@ impl<'a> PushToLuaStack<'a> for Value<'a> {
|
|||
let s = state.state_ptr();
|
||||
|
||||
match self {
|
||||
Value::None => {
|
||||
|
||||
},
|
||||
Value::Nil => {
|
||||
state.ensure_stack(1)?;
|
||||
lua::lua_pushnil(s);
|
||||
|
@ -103,6 +131,10 @@ impl<'a> FromLuaStack<'a> for Value<'a> {
|
|||
Table::from_lua_stack(state)
|
||||
.map(|t| Value::Table(t))
|
||||
},
|
||||
lua::LUA_TUSERDATA => {
|
||||
AnyUserdata::from_lua_stack(state)
|
||||
.map(|ud| Value::Userdata(ud))
|
||||
},
|
||||
_ => {
|
||||
let s = lua::lua_typename(s, ty);
|
||||
let s = CStr::from_ptr(s);
|
||||
|
@ -119,32 +151,121 @@ pub trait AsLua<'a> {
|
|||
fn as_lua(&self, lua: &'a State) -> crate::Result<Value<'a>>;
|
||||
}
|
||||
|
||||
pub trait FromLua: Sized {
|
||||
fn from_lua(lua: &State, val: Value) -> crate::Result<Self>;
|
||||
}
|
||||
|
||||
/* impl<'a> AsLua<'a> for Value<'a> {
|
||||
fn as_lua(&self, lua: &'a State) -> crate::Result<Value<'a>> {
|
||||
Ok(self.clone())
|
||||
}
|
||||
} */
|
||||
|
||||
/* impl<'a, T: AsLua<'a>> PushToLuaStack<'a> for T {
|
||||
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<'a, T: FromLua> FromLuaStack<'a> for T {
|
||||
unsafe fn from_lua_stack(state: &'a State) -> crate::Result<Self> {
|
||||
let v = Value::from_lua_stack(state)?;
|
||||
T::from_lua(state, v)
|
||||
}
|
||||
pub trait FromLua<'a>: Sized {
|
||||
fn from_lua(lua: &'a State, val: Value<'a>) -> crate::Result<Self>;
|
||||
}
|
||||
|
||||
impl<'a> AsLua<'a> for () {
|
||||
fn as_lua(&self, lua: &'a State) -> crate::Result<Value<'a>> {
|
||||
fn as_lua(&self, _lua: &'a State) -> crate::Result<Value<'a>> {
|
||||
Ok(Value::Nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FromLua<'a> for Value<'a> {
|
||||
fn from_lua(_lua: &'a State, val: Value<'a>) -> crate::Result<Self> {
|
||||
Ok(val)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct ValueVec<'a>(VecDeque<Value<'a>>);
|
||||
|
||||
impl<'a> Deref for ValueVec<'a> {
|
||||
type Target = VecDeque<Value<'a>>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> DerefMut for ValueVec<'a> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FromLuaStack<'a> for ValueVec<'a> {
|
||||
unsafe fn from_lua_stack(state: &'a State) -> crate::Result<Self> {
|
||||
let s = state.state_ptr();
|
||||
|
||||
let top = lua::lua_gettop(s);
|
||||
|
||||
let mut vec = VecDeque::new();
|
||||
for _ in 1..(top + 1) {
|
||||
let v = Value::from_lua_stack(state)?;
|
||||
vec.push_front(v);
|
||||
}
|
||||
|
||||
Ok(ValueVec(vec))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ValueVec<'a> {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<Value<'a>> for ValueVec<'a> {
|
||||
fn from(value: Value<'a>) -> Self {
|
||||
let mut v = VecDeque::new();
|
||||
v.push_back(value);
|
||||
ValueVec(v)
|
||||
}
|
||||
}
|
||||
|
||||
/// A trait for getting something from a ValueVec from Lua
|
||||
pub trait FromLuaVec<'a>: Sized {
|
||||
fn from_lua_value_vec(state: &'a State, values: ValueVec<'a>) -> crate::Result<Self>;
|
||||
}
|
||||
|
||||
impl<'a> FromLuaVec<'a> for () {
|
||||
fn from_lua_value_vec(_state: &'a State, _values: ValueVec<'a>) -> crate::Result<Self> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FromLuaVec<'a> for ValueVec<'a> {
|
||||
fn from_lua_value_vec(_state: &'a State, values: ValueVec<'a>) -> crate::Result<Self> {
|
||||
Ok(values)
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_from_lua_vec_tuple {
|
||||
( $count: expr, $first: tt, $( $name: tt ),+ ) => (
|
||||
#[allow(non_snake_case)]
|
||||
impl<'a, $first: FromLua<'a>, $($name: FromLua<'a>,)+> FromLuaVec<'a> for ($first, $($name,)+) {
|
||||
|
||||
fn from_lua_value_vec(state: &'a State, mut values: ValueVec<'a>) -> crate::Result<Self> {
|
||||
if values.len() != $count {
|
||||
panic!("Not same length"); // TODO
|
||||
}
|
||||
|
||||
let f = $first::from_lua(state, values.pop_front().unwrap())?;
|
||||
let ($( $name, )+) = ( $( $name::from_lua(state, values.pop_front().unwrap())?, )+ );
|
||||
|
||||
Ok( (f, $( $name, )+) )
|
||||
}
|
||||
}
|
||||
|
||||
impl_from_lua_vec_tuple!( $count - 1, $( $name ),+ );
|
||||
);
|
||||
|
||||
( $count: expr, $only: tt ) => {
|
||||
#[allow(non_snake_case)]
|
||||
impl<'a, $only: FromLua<'a>> FromLuaVec<'a> for ($only,) {
|
||||
fn from_lua_value_vec(state: &'a State, mut values: ValueVec<'a>) -> crate::Result<Self> {
|
||||
if values.len() != 1 {
|
||||
panic!("Not same length"); // TODO
|
||||
}
|
||||
|
||||
Ok( ( $only::from_lua(state, values.pop_front().unwrap())?, ) )
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
// hopefully 16 is more than enough
|
||||
// if you have 16 function results, and need more than 16, you NEED help
|
||||
impl_from_lua_vec_tuple! { 16, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16 }
|
||||
|
|
Loading…
Reference in New Issue