diff --git a/.gitignore b/.gitignore old mode 100644 new mode 100755 diff --git a/.vscode/launch.json b/.vscode/launch.json old mode 100644 new mode 100755 diff --git a/Cargo.lock b/Cargo.lock old mode 100644 new mode 100755 diff --git a/Cargo.toml b/Cargo.toml old mode 100644 new mode 100755 diff --git a/LICENSE b/LICENSE old mode 100644 new mode 100755 diff --git a/src/function.rs b/src/function.rs old mode 100644 new mode 100755 index 97ad2ef..2175155 --- a/src/function.rs +++ b/src/function.rs @@ -61,7 +61,7 @@ impl<'a> Function<'a> { } impl<'a> AsLua<'a> for Function<'a> { - fn as_lua(&self, lua: &'a State) -> crate::Result> { + fn as_lua(&self, _lua: &'a State) -> crate::Result> { 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 diff --git a/src/guard.rs b/src/guard.rs old mode 100644 new mode 100755 diff --git a/src/main.rs b/src/main.rs old mode 100644 new mode 100755 index ca48ddf..92f08a8 --- a/src/main.rs +++ b/src/main.rs @@ -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; } +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 { 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 { 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 { + 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 UserdataProxy { } impl<'a, T: Userdata> AsLua<'a> for UserdataProxy { - fn as_lua(&self, lua: &'a State) -> crate::Result> { + fn as_lua(&self, _lua: &'a State) -> crate::Result> { todo!() } } @@ -314,12 +351,12 @@ impl Userdata for UserdataProxy { 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(()) } diff --git a/src/state.rs b/src/state.rs old mode 100644 new mode 100755 index d82a50b..c8be57b --- a/src/state.rs +++ b/src/state.rs @@ -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 { 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 where - A: FromLuaStackMulti<'a>, + A: FromLuaVec<'a>, R: PushToLuaStackMulti<'a>, F: Fn(&'a State, A) -> Result + '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::()?; - //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) diff --git a/src/table.rs b/src/table.rs old mode 100644 new mode 100755 index d27960d..8f03e9c --- a/src/table.rs +++ b/src/table.rs @@ -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(()) diff --git a/src/userdata.rs b/src/userdata.rs old mode 100644 new mode 100755 index 30d18db..884f259 --- a/src/userdata.rs +++ b/src/userdata.rs @@ -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 = fn(lua: &State, this: &T); //pub type FieldGetter = 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 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) -> crate::Result>>; +type UserdataFn<'a> = Box) -> crate::Result>>; pub struct UserdataBuilder<'a, T> { pub(crate) name: Option, pub(crate) field_getters: HashMap>, + pub(crate) field_setters: HashMap>, + pub(crate) functions: HashMap>, + pub(crate) meta_methods: HashMap>, _marker: PhantomData, } @@ -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 + '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::()?; 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::()?; + pub fn field_setter(&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::()?; - 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(this: &mut UserdataBuilder<'a, T>, other: UserdataBuilder<'a, U>, to_proxied: F) + pub fn function(&mut self, name: &str, f: F) -> &mut Self where - F: Fn(&'a T) -> &'a U + 'a, + F: Fn(&'a State, A) -> crate::Result + '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> { - 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::()?; + pub fn method(&mut self, name: &str, f: F) -> &mut Self + where + F: Fn(&'a State, &T, A) -> crate::Result + '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::()?; - 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(&mut self, name: N, f: F) -> &mut Self + where + N: AsRef, + F: Fn(&'a State, &T, A) -> crate::Result + '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::()?; - 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::().as_ref().unwrap()) + Ok(cptr.cast::().as_ref().unwrap()) // TODO: Ensure this userdata matches the type of T + } + } + + pub fn as_mut(&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::().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> { + Ok(Value::Userdata(self.clone())) + } +} + +impl<'a> FromLua<'a> for AnyUserdata<'a> { + fn from_lua(_lua: &State, val: Value<'a>) -> crate::Result { + val.into_userdata() + } +} + +impl<'a, T: Userdata> FromLua<'a> for &'a T { + fn from_lua(_lua: &'a State, val: Value<'a>) -> crate::Result { + let ud = val.into_userdata()?; + ud.as_ref::() + } } \ No newline at end of file diff --git a/src/util.rs b/src/util.rs old mode 100644 new mode 100755 index b7cbbe6..0d92715 --- a/src/util.rs +++ b/src/util.rs @@ -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())) } } \ No newline at end of file diff --git a/src/value.rs b/src/value.rs old mode 100644 new mode 100755 index 940dc20..9a1f5a9 --- a/src/value.rs +++ b/src/value.rs @@ -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> { + 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>; } -pub trait FromLua: Sized { - fn from_lua(lua: &State, val: Value) -> crate::Result; -} - -/* impl<'a> AsLua<'a> for Value<'a> { - fn as_lua(&self, lua: &'a State) -> crate::Result> { - 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 { - 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; } impl<'a> AsLua<'a> for () { - fn as_lua(&self, lua: &'a State) -> crate::Result> { + fn as_lua(&self, _lua: &'a State) -> crate::Result> { Ok(Value::Nil) } -} \ No newline at end of file +} + +impl<'a> FromLua<'a> for Value<'a> { + fn from_lua(_lua: &'a State, val: Value<'a>) -> crate::Result { + Ok(val) + } +} + +#[derive(Default)] +pub struct ValueVec<'a>(VecDeque>); + +impl<'a> Deref for ValueVec<'a> { + type Target = VecDeque>; + + 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 { + 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> 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; +} + +impl<'a> FromLuaVec<'a> for () { + fn from_lua_value_vec(_state: &'a State, _values: ValueVec<'a>) -> crate::Result { + Ok(()) + } +} + +impl<'a> FromLuaVec<'a> for ValueVec<'a> { + fn from_lua_value_vec(_state: &'a State, values: ValueVec<'a>) -> crate::Result { + 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 { + 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 { + 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 }