Add meta methods, methods, functions, and field setters to UserData

This commit is contained in:
SeanOMik 2024-01-27 14:54:11 -05:00
parent 9ef4203619
commit a8db62fe08
13 changed files with 433 additions and 110 deletions

0
.gitignore vendored Normal file → Executable file
View File

0
.vscode/launch.json vendored Normal file → Executable file
View File

0
Cargo.lock generated Normal file → Executable file
View File

0
Cargo.toml Normal file → Executable file
View File

0
LICENSE Normal file → Executable file
View File

3
src/function.rs Normal file → Executable file
View File

@ -61,7 +61,7 @@ impl<'a> Function<'a> {
} }
impl<'a> AsLua<'a> for 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())) 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,)+) { impl<'a, $first: FromLuaStack<'a>, $($name: FromLuaStack<'a>,)+> FromLuaStackMulti<'a> for ($first, $($name,)+) {
fn len() -> usize { fn len() -> usize {
$count $count

0
src/guard.rs Normal file → Executable file
View File

55
src/main.rs Normal file → Executable file
View File

@ -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; use mlua_sys as lua;
pub mod state; pub mod state;
@ -90,8 +89,18 @@ fn main() -> Result<()> {
print("Vec2: " .. dump_table(Vec2)) print("Vec2: " .. dump_table(Vec2))
print("Meta Vec2: " .. dump_table(getmetatable(Vec2))) print("Meta Vec2: " .. dump_table(getmetatable(Vec2)))
--local vec2 = Vec2.new(50, 50)
print("Vec2 is (" .. Vec2.x .. ", " .. Vec2.y .. ")") 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(); "#).unwrap();
unsafe { unsafe {
@ -198,6 +207,12 @@ pub trait FromLuaStack<'a>: Sized {
unsafe fn from_lua_stack(state: &'a State) -> Result<Self>; 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 /// Implements PushToLuaStack for a number
macro_rules! impl_as_lua_number { macro_rules! impl_as_lua_number {
($ty: ident) => { ($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> { fn from_lua(_lua: &State, val: Value) -> crate::Result<Self> {
match val { match val {
Value::Number(v) => Ok(v as $ty), 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> { unsafe fn from_lua_stack(state: &'a State) -> Result<Self> {
let s = state.state_ptr(); let s = state.state_ptr();
@ -263,6 +278,12 @@ impl<'a> FromLuaStack<'a> for String {
let cstr = CStr::from_ptr(cstr); let cstr = CStr::from_ptr(cstr);
Ok(cstr.to_str().unwrap().to_string()) 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 { impl<'a> PushToLuaStack<'a> for &str {
@ -286,7 +307,23 @@ impl Userdata for Vec2 {
fn build<'a>(builder: &mut UserdataBuilder<'a, Vec2>) -> crate::Result<()> { fn build<'a>(builder: &mut UserdataBuilder<'a, Vec2>) -> crate::Result<()> {
builder.name("Vec2") builder.name("Vec2")
.field_getter("x", |_, this| Ok(this.x)) .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(()) Ok(())
} }
@ -301,7 +338,7 @@ impl<T: Userdata> UserdataProxy<T> {
} }
impl<'a, T: Userdata> AsLua<'a> for 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!() todo!()
} }
} }
@ -314,12 +351,12 @@ impl<T: Userdata> Userdata for UserdataProxy<T> {
builder.name(&other.name.unwrap()); 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| { let wrap = |state: &State, data: &Self| {
Ok(()) Ok(())
}; };
builder.field_getter(&lbl, wrap); builder.field_getter(&lbl, wrap);
} } */
Ok(()) Ok(())
} }

75
src/state.rs Normal file → Executable file
View File

@ -1,9 +1,9 @@
use core::ffi; 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 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> { pub fn ptr_to_string(ptr: *const i8) -> std::result::Result<String, Utf8Error> {
let c = unsafe { CStr::from_ptr(ptr) }; let c = unsafe { CStr::from_ptr(ptr) };
@ -117,7 +117,12 @@ impl State {
if lua::luaL_dostring(lua, text_c) != 0 { if lua::luaL_dostring(lua, text_c) != 0 {
let error_c = CStr::from_ptr(lua::lua_tostring(lua, -1)); let error_c = CStr::from_ptr(lua::lua_tostring(lua, -1));
let error_str = error_c.to_str() 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)); return Err(Error::runtime(error_str));
} }
} }
@ -177,6 +182,7 @@ impl State {
} }
} }
#[allow(dead_code)]
pub(crate) unsafe fn print_stack(state: &State) { pub(crate) unsafe fn print_stack(state: &State) {
let s = state.state_ptr(); let s = state.state_ptr();
let t = lua::lua_gettop(s); 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> pub fn create_function<'a, A, R, F>(&self, f: F) -> Result<Function>
where where
A: FromLuaStackMulti<'a>, A: FromLuaVec<'a>,
R: PushToLuaStackMulti<'a>, R: PushToLuaStackMulti<'a>,
F: Fn(&'a State, A) -> Result<R> + 'static, F: Fn(&'a State, A) -> Result<R> + 'static,
{ {
@ -236,17 +242,13 @@ impl State {
state: &'a State, state: &'a State,
} }
let wrapper_fn = move |lua: &State, narg: i32| -> i32 { let wrapper_fn = move |lua: &State, _narg: i32| -> i32 {
unsafe { unsafe {
let lua: &State = mem::transmute(lua); // transmute lifetimes 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, || { 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)?; let r = f(lua, args)?;
r.push_args_to_lua_stack(lua)?; r.push_args_to_lua_stack(lua)?;
@ -291,13 +293,12 @@ impl State {
// attempt to get the metatable // attempt to get the metatable
lua::lua_pushstring(s, name_cstr); lua::lua_pushstring(s, name_cstr);
let ty = lua::lua_rawget(s, lua::LUA_REGISTRYINDEX); let ty = lua::lua_rawget(s, lua::LUA_REGISTRYINDEX);
if ty == lua::LUA_TNIL { if ty == lua::LUA_TNIL {
lua::lua_pop(s, 1); // remove nil lua::lua_pop(s, 1); // remove nil
// if the metatable is not made yet, make it // if the metatable is not made yet, make it
let mt = self.create_userdata_metatable::<T>()?; 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)?; mt.push_to_lua_stack(self)?;
} else if ty != lua::LUA_TTABLE { } else if ty != lua::LUA_TTABLE {
return Err(Error::RegistryConflict(name.to_string())); return Err(Error::RegistryConflict(name.to_string()));
@ -314,21 +315,63 @@ impl State {
T::build(&mut builder)?; T::build(&mut builder)?;
let getters = builder.field_getters; let getters = builder.field_getters;
let setters = builder.field_setters;
let name = builder.name.unwrap(); 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)?; 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)| { let index_fn = self.create_function(move |lua: &State, (ud, key): (AnyUserdata, String)| {
if let Some(getter) = getters.get(&key) { 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) Ok(r)
} else if let Some(function) = fns_table.get(&key) {
Ok(Value::Function(function.clone()))
} else { } else {
Ok(Value::Nil) 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) Ok(mt)

3
src/table.rs Normal file → Executable file
View File

@ -1,6 +1,4 @@
use std::{ffi::CStr, ops::Deref, sync::Arc};
use mlua_sys as lua; use mlua_sys as lua;
use crate::{ensure_type, FromLuaStack, LuaRef, PushToLuaStack, Result, StackGuard, State}; 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)?; val.push_to_lua_stack(self.state)?;
lua::lua_rawset(s, -3); lua::lua_rawset(s, -3);
lua::lua_pop(self.state.state_ptr(), -1);
} }
Ok(()) Ok(())

234
src/userdata.rs Normal file → Executable file
View File

@ -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; use mlua_sys as lua;
//pub type FieldSetter<T> = fn(lua: &State, this: &T); //pub type FieldSetter<T> = fn(lua: &State, this: &T);
//pub type FieldGetter<T, U> = fn(lua: &State, this: &T, val: &U); //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 { pub trait FieldSetter {
fn set_field(&self, val: Value); fn set_field(&self, val: Value);
} }
@ -15,11 +92,14 @@ pub trait FieldGetter {
fn get_field(&self) -> Value; 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 struct UserdataBuilder<'a, T> {
pub(crate) name: Option<String>, pub(crate) name: Option<String>,
pub(crate) field_getters: HashMap<String, UserdataFn<'a>>, 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>, _marker: PhantomData<T>,
} }
@ -28,6 +108,9 @@ impl<'a, T> UserdataBuilder<'a, T> {
Self { Self {
name: None, name: None,
field_getters: HashMap::new(), field_getters: HashMap::new(),
field_setters: HashMap::new(),
functions: HashMap::new(),
meta_methods: HashMap::new(),
_marker: PhantomData, _marker: PhantomData,
} }
} }
@ -42,7 +125,8 @@ impl<'a, T> UserdataBuilder<'a, T> {
F: Fn(&'a State, &T) -> crate::Result<R> + 'static, F: Fn(&'a State, &T) -> crate::Result<R> + 'static,
R: AsLua<'a>, 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 = val.as_userdata().unwrap(); // if this panics, its a bug
let this = this.as_ref::<T>()?; let this = this.as_ref::<T>()?;
f(lua, this).and_then(|r| r.as_lua(lua)) f(lua, this).and_then(|r| r.as_lua(lua))
@ -52,68 +136,81 @@ impl<'a, T> UserdataBuilder<'a, T> {
self self
} }
pub fn into_meta_table(self, state: &'a State, table: &Table<'a>) -> crate::Result<()> { pub fn field_setter<F, V>(&mut self, name: &str, f: F) -> &mut Self
//let this: Self = unsafe { std::mem::transmute(self) }; where
//let getters = *self.field_getters; F: Fn(&'a State, &mut T, V) -> () + 'static,
V: FromLua<'a>,
/* let index_fn = state.create_function(move |lua: &'a State, (ud, key): (AnyUserdata<'a>, String)| { {
let ud_ref = ud.as_ref::<T>()?; 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 lua_val = val.pop_front().unwrap();
let r = getter(lua, ome(ud_ref), Value::Nil)?; 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) self
} else {
//ud.get::<_, Value<'a>>(key)
Ok(Value::Nil)
}
})?;
table.set("__index", index_fn)?; */
Ok(())
} }
/* 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 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); let wrap = move |lua: &'a State, val: ValueVec<'a>| {
for (lbl, getter) in other.field_getters.into_iter() { let args = A::from_lua_value_vec(lua, val)?;
let to_proxied_cl = to_proxied.clone(); f(lua, args).and_then(|r| r.as_lua(lua))
let new_getter = move |lua: &'a State, this: Option<&T>, val: Value<'a>| { };
let proxy = this.map(|t| to_proxied_cl(t)); self.functions.insert(name.to_string(), Box::new(wrap));
getter(lua, proxy, val)
};
this.field_getters.insert(lbl, Box::new(new_getter));
}
} */
/* pub fn into_userdata(self, state: &'a State, data: T) -> crate::Result<Table<'a>> { self
let name = self.name }
.expect("No name was set for userdata!");
let getters = self.field_getters.clone(); pub fn method<F, R, A>(&mut self, name: &str, f: F) -> &mut Self
let index_fn = state.create_function(move |lua: &'a State, (ud, key): (AnyUserdata<'a>, String)| { where
let ud_ref = ud.as_ref::<T>()?; 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) { let args = A::from_lua_value_vec(lua, val)?;
getter(lua, Some(ud_ref), Value::Nil)
} else { f(lua, this, args).and_then(|r| r.as_lua(lua))
//ud.get::<_, Value<'a>>(key) };
Ok(Value::Nil) self.functions.insert(name.to_string(), Box::new(wrap));
}
//todo!() self
})?; }
let mt = state.create_meta_table(&name)?; pub fn meta_method<N, F, R, A>(&mut self, name: N, f: F) -> &mut Self
mt.set("__index", index_fn)?; 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 { pub trait Userdata: Sized {
@ -141,7 +238,17 @@ impl<'a> AnyUserdata<'a> {
self.lref.push_to_lua_stack(self.state)?; self.lref.push_to_lua_stack(self.state)?;
let cptr = lua::lua_touserdata(s, -1); 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<()> { unsafe fn push_to_lua_stack(&self, state: &'a State) -> crate::Result<()> {
self.lref.push_to_lua_stack(state) 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>()
}
} }

2
src/util.rs Normal file → Executable file
View File

@ -22,8 +22,6 @@ pub unsafe fn ensure_type(state: &State, typ: i32, idx: i32) -> crate::Result<()
let s = cstr.to_str() let s = cstr.to_str()
.expect("Lua type has invalid bytes!"); .expect("Lua type has invalid bytes!");
println!("ty = {}, typ = {}", lua_type, typ);
Err(crate::Error::UnexpectedType(exp_s.to_string(), s.to_string())) Err(crate::Error::UnexpectedType(exp_s.to_string(), s.to_string()))
} }
} }

171
src/value.rs Normal file → Executable file
View File

@ -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}; use crate::{AnyUserdata, FromLuaStack, Function, PushToLuaStack, State, Table};
@ -6,6 +6,9 @@ use mlua_sys as lua;
#[derive(Clone)] #[derive(Clone)]
pub enum Value<'a> { 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, Nil,
Number(f64), Number(f64),
String(String), String(String),
@ -21,6 +24,7 @@ impl<'a> Value<'a> {
pub fn type_name(&self) -> &'static str { pub fn type_name(&self) -> &'static str {
match self { match self {
Value::None => "None",
Value::Nil => "Nil", Value::Nil => "Nil",
Value::Number(_) => "Number", Value::Number(_) => "Number",
Value::String(_) => "String", 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> { impl<'a> PushToLuaStack<'a> for Value<'a> {
@ -45,6 +70,9 @@ impl<'a> PushToLuaStack<'a> for Value<'a> {
let s = state.state_ptr(); let s = state.state_ptr();
match self { match self {
Value::None => {
},
Value::Nil => { Value::Nil => {
state.ensure_stack(1)?; state.ensure_stack(1)?;
lua::lua_pushnil(s); lua::lua_pushnil(s);
@ -103,6 +131,10 @@ impl<'a> FromLuaStack<'a> for Value<'a> {
Table::from_lua_stack(state) Table::from_lua_stack(state)
.map(|t| Value::Table(t)) .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 = lua::lua_typename(s, ty);
let s = CStr::from_ptr(s); 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>>; fn as_lua(&self, lua: &'a State) -> crate::Result<Value<'a>>;
} }
pub trait FromLua: Sized { pub trait FromLua<'a>: Sized {
fn from_lua(lua: &State, val: Value) -> crate::Result<Self>; fn from_lua(lua: &'a State, val: Value<'a>) -> 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)
}
} }
impl<'a> AsLua<'a> for () { 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) 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 }