Merge pull request #32 from chucklefish/tuples

Major API changes before 0.9
This commit is contained in:
kyren 2017-08-01 14:52:46 -04:00 committed by GitHub
commit 64ac8b4679
6 changed files with 263 additions and 211 deletions

View File

@ -20,6 +20,3 @@ builtin-lua = ["gcc"]
[build-dependencies] [build-dependencies]
gcc = { version = "0.3", optional = true } gcc = { version = "0.3", optional = true }
[dependencies]
hlist-macro = "0.1"

View File

@ -1,5 +1,3 @@
#[macro_use]
extern crate hlist_macro;
extern crate rlua; extern crate rlua;
use std::f32; use std::f32;
@ -79,40 +77,28 @@ fn examples() -> Result<()> {
let print: Function = globals.get("print")?; let print: Function = globals.get("print")?;
print.call::<_, ()>("hello from rust")?; print.call::<_, ()>("hello from rust")?;
// This API handles variadics using Heterogeneous Lists. This is one way to // This API handles variadics using tuples. This is one way to call a function with multiple
// call a function with multiple parameters: // parameters:
print.call::<_, ()>( print.call::<_, ()>(("hello", "again", "from", "rust"))?;
hlist!["hello", "again", "from", "rust"],
)?;
// You can bind rust functions to lua as well // You can bind rust functions to lua as well. Callbacks receive the lua state itself as their
// first parameter, and the arguments given to the function as the second parameter. The type
let check_equal = lua.create_function(|lua, args| { // of the arguments can be anything that is convertible from the set of parameters, in this
// Functions wrapped in lua receive their arguments packed together as // case, the function expects two string sequences.
// MultiValue. The first thing that most wrapped functions will do let check_equal = lua.create_function(|_, (list1, list2): (Vec<String>, Vec<String>)| {
// is "unpack" this MultiValue into its parts. Due to lifetime type // This function just checks whether two string lists are equal, and in an inefficient way.
// signature limitations, this cannot be done automatically from the // Lua callbacks return rlua::Result, an Ok value is a normal return, and an Err return
// function signature, but this will be fixed with ATCs. Notice the use // turns into a Lua 'error'. Again, any type that is convertible to lua may be returned.
// of the hlist macros again. Ok(list1 == list2)
let hlist_pat![list1, list2] = lua.unpack::<HList![Vec<String>, Vec<String>]>(args)?;
// This function just checks whether two string lists are equal, and in
// an inefficient way. Results are returned with lua.pack, which takes
// any number of values and turns them back into MultiValue. In this
// way, multiple values can also be returned to Lua. Again, this cannot
// be inferred as part of the function signature due to the same
// lifetime type signature limitations.
lua.pack(list1 == list2)
}); });
globals.set("check_equal", check_equal)?; globals.set("check_equal", check_equal)?;
// You can also accept variadic arguments to rust callbacks. // You can also accept runtime variadic arguments to rust callbacks.
let join = lua.create_function(|lua, args| { let join = lua.create_function(|_, strings: Variadic<String>| {
let strings = lua.unpack::<Variadic<String>>(args)?.0;
// (This is quadratic!, it's just an example!) // (This is quadratic!, it's just an example!)
lua.pack(strings.iter().fold("".to_owned(), |a, b| a + b)) Ok(strings.iter().fold("".to_owned(), |a, b| a + b))
}); });
globals.set("join", join)?; globals.set("join", join)?;
@ -141,22 +127,18 @@ fn examples() -> Result<()> {
impl UserData for Vec2 { impl UserData for Vec2 {
fn add_methods(methods: &mut UserDataMethods<Self>) { fn add_methods(methods: &mut UserDataMethods<Self>) {
methods.add_method("magnitude", |lua, vec, _| { methods.add_method("magnitude", |_, vec, ()| {
let mag_squared = vec.0 * vec.0 + vec.1 * vec.1; let mag_squared = vec.0 * vec.0 + vec.1 * vec.1;
lua.pack(mag_squared.sqrt()) Ok(mag_squared.sqrt())
}); });
methods.add_meta_function(MetaMethod::Add, |lua, params| { methods.add_meta_function(MetaMethod::Add, |_, (vec1, vec2): (Vec2, Vec2)| {
let hlist_pat![vec1, vec2] = lua.unpack::<HList![Vec2, Vec2]>(params)?; Ok(Vec2(vec1.0 + vec2.0, vec1.1 + vec2.1))
lua.pack(Vec2(vec1.0 + vec2.0, vec1.1 + vec2.1))
}); });
} }
} }
let vec2_constructor = lua.create_function(|lua, args| { let vec2_constructor = lua.create_function(|_, (x, y): (f32, f32)| Ok(Vec2(x, y)));
let hlist_pat![x, y] = lua.unpack::<HList![f32, f32]>(args)?;
lua.pack(Vec2(x, y))
});
globals.set("vec2", vec2_constructor)?; globals.set("vec2", vec2_constructor)?;
assert!( assert!(

View File

@ -1,9 +1,6 @@
// Deny warnings inside doc tests / examples // Deny warnings inside doc tests / examples
#![doc(test(attr(deny(warnings))))] #![doc(test(attr(deny(warnings))))]
#[cfg_attr(test, macro_use)]
extern crate hlist_macro;
pub mod ffi; pub mod ffi;
#[macro_use] #[macro_use]
mod util; mod util;

View File

@ -39,12 +39,10 @@ pub enum Value<'lua> {
Function(Function<'lua>), Function(Function<'lua>),
/// Reference to a Lua thread (or coroutine). /// Reference to a Lua thread (or coroutine).
Thread(Thread<'lua>), Thread(Thread<'lua>),
/// Reference to a userdata object that holds a custom type which implements /// Reference to a userdata object that holds a custom type which implements `UserData`.
/// `UserData`. Special builtin userdata types will be represented as /// Special builtin userdata types will be represented as other `Value` variants.
/// other `Value` variants.
UserData(AnyUserData<'lua>), UserData(AnyUserData<'lua>),
/// `Error` is a special builtin userdata type. When received from Lua /// `Error` is a special builtin userdata type. When received from Lua it is implicitly cloned.
/// it is implicitly cloned.
Error(Error), Error(Error),
} }
pub use self::Value::Nil; pub use self::Value::Nil;
@ -647,7 +645,6 @@ impl<'lua> Function<'lua> {
/// ///
/// ``` /// ```
/// # extern crate rlua; /// # extern crate rlua;
/// # #[macro_use] extern crate hlist_macro;
/// # use rlua::{Lua, Function, Result}; /// # use rlua::{Lua, Function, Result};
/// # fn try_main() -> Result<()> { /// # fn try_main() -> Result<()> {
/// let lua = Lua::new(); /// let lua = Lua::new();
@ -658,7 +655,7 @@ impl<'lua> Function<'lua> {
/// end /// end
/// "#, None)?; /// "#, None)?;
/// ///
/// assert_eq!(sum.call::<_, u32>(hlist![3, 4])?, 3 + 4); /// assert_eq!(sum.call::<_, u32>((3, 4))?, 3 + 4);
/// ///
/// # Ok(()) /// # Ok(())
/// # } /// # }
@ -966,9 +963,11 @@ impl<'lua, T: UserData> UserDataMethods<'lua, T> {
/// ///
/// If `add_meta_method` is used to override the `__index` metamethod, this approach will fall /// If `add_meta_method` is used to override the `__index` metamethod, this approach will fall
/// back to the user-provided metamethod if no regular method was found. /// back to the user-provided metamethod if no regular method was found.
pub fn add_method<M>(&mut self, name: &str, method: M) pub fn add_method<A, R, M>(&mut self, name: &str, method: M)
where where
M: 'static + for<'a> FnMut(&'lua Lua, &'a T, MultiValue<'lua>) -> Result<MultiValue<'lua>>, A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
M: 'static + for<'a> FnMut(&'lua Lua, &'a T, A) -> Result<R>,
{ {
self.methods.insert( self.methods.insert(
name.to_owned(), name.to_owned(),
@ -981,9 +980,11 @@ impl<'lua, T: UserData> UserDataMethods<'lua, T> {
/// Refer to [`add_method`] for more information about the implementation. /// Refer to [`add_method`] for more information about the implementation.
/// ///
/// [`add_method`]: #method.add_method /// [`add_method`]: #method.add_method
pub fn add_method_mut<M>(&mut self, name: &str, method: M) pub fn add_method_mut<A, R, M>(&mut self, name: &str, method: M)
where where
M: 'static + for<'a> FnMut(&'lua Lua, &'a mut T, MultiValue<'lua>) -> Result<MultiValue<'lua>>, A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
M: 'static + for<'a> FnMut(&'lua Lua, &'a mut T, A) -> Result<R>,
{ {
self.methods.insert( self.methods.insert(
name.to_owned(), name.to_owned(),
@ -998,11 +999,16 @@ impl<'lua, T: UserData> UserDataMethods<'lua, T> {
/// ///
/// [`add_method`]: #method.add_method /// [`add_method`]: #method.add_method
/// [`add_method_mut`]: #method.add_method_mut /// [`add_method_mut`]: #method.add_method_mut
pub fn add_function<F>(&mut self, name: &str, function: F) pub fn add_function<A, R, F>(&mut self, name: &str, function: F)
where where
F: 'static + FnMut(&'lua Lua, MultiValue<'lua>) -> Result<MultiValue<'lua>>, A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
F: 'static + FnMut(&'lua Lua, A) -> Result<R>,
{ {
self.methods.insert(name.to_owned(), Box::new(function)); self.methods.insert(
name.to_owned(),
Self::box_function(function),
);
} }
/// Add a metamethod which accepts a `&T` as the first parameter. /// Add a metamethod which accepts a `&T` as the first parameter.
@ -1013,9 +1019,11 @@ impl<'lua, T: UserData> UserDataMethods<'lua, T> {
/// side has a metatable. To prevent this, use [`add_meta_function`]. /// side has a metatable. To prevent this, use [`add_meta_function`].
/// ///
/// [`add_meta_function`]: #method.add_meta_function /// [`add_meta_function`]: #method.add_meta_function
pub fn add_meta_method<M>(&mut self, meta: MetaMethod, method: M) pub fn add_meta_method<A, R, M>(&mut self, meta: MetaMethod, method: M)
where where
M: 'static + for<'a> FnMut(&'lua Lua, &'a T, MultiValue<'lua>) -> Result<MultiValue<'lua>>, A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
M: 'static + for<'a> FnMut(&'lua Lua, &'a T, A) -> Result<R>,
{ {
self.meta_methods.insert(meta, Self::box_method(method)); self.meta_methods.insert(meta, Self::box_method(method));
} }
@ -1028,9 +1036,11 @@ impl<'lua, T: UserData> UserDataMethods<'lua, T> {
/// side has a metatable. To prevent this, use [`add_meta_function`]. /// side has a metatable. To prevent this, use [`add_meta_function`].
/// ///
/// [`add_meta_function`]: #method.add_meta_function /// [`add_meta_function`]: #method.add_meta_function
pub fn add_meta_method_mut<M>(&mut self, meta: MetaMethod, method: M) pub fn add_meta_method_mut<A, R, M>(&mut self, meta: MetaMethod, method: M)
where where
M: 'static + for<'a> FnMut(&'lua Lua, &'a mut T, MultiValue<'lua>) -> Result<MultiValue<'lua>>, A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
M: 'static + for<'a> FnMut(&'lua Lua, &'a mut T, A) -> Result<R>,
{ {
self.meta_methods.insert(meta, Self::box_method_mut(method)); self.meta_methods.insert(meta, Self::box_method_mut(method));
} }
@ -1040,21 +1050,39 @@ impl<'lua, T: UserData> UserDataMethods<'lua, T> {
/// Metamethods for binary operators can be triggered if either the left or right argument to /// Metamethods for binary operators can be triggered if either the left or right argument to
/// the binary operator has a metatable, so the first argument here is not necessarily a /// the binary operator has a metatable, so the first argument here is not necessarily a
/// userdata of type `T`. /// userdata of type `T`.
pub fn add_meta_function<F>(&mut self, meta: MetaMethod, function: F) pub fn add_meta_function<A, R, F>(&mut self, meta: MetaMethod, function: F)
where where
F: 'static + FnMut(&'lua Lua, MultiValue<'lua>) -> Result<MultiValue<'lua>>, A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
F: 'static + FnMut(&'lua Lua, A) -> Result<R>,
{ {
self.meta_methods.insert(meta, Box::new(function)); self.meta_methods.insert(meta, Self::box_function(function));
} }
fn box_method<M>(mut method: M) -> Callback<'lua> fn box_function<A, R, F>(mut function: F) -> Callback<'lua>
where where
M: 'static + for<'a> FnMut(&'lua Lua, &'a T, MultiValue<'lua>) -> Result<MultiValue<'lua>>, A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
F: 'static + for<'a> FnMut(&'lua Lua, A) -> Result<R>,
{
Box::new(move |lua, args| {
function(lua, A::from_lua_multi(args, lua)?)?.to_lua_multi(
lua,
)
})
}
fn box_method<A, R, M>(mut method: M) -> Callback<'lua>
where
A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
M: 'static + for<'a> FnMut(&'lua Lua, &'a T, A) -> Result<R>,
{ {
Box::new(move |lua, mut args| if let Some(front) = args.pop_front() { Box::new(move |lua, mut args| if let Some(front) = args.pop_front() {
let userdata = AnyUserData::from_lua(front, lua)?; let userdata = AnyUserData::from_lua(front, lua)?;
let userdata = userdata.borrow::<T>()?; let userdata = userdata.borrow::<T>()?;
method(lua, &userdata, args) method(lua, &userdata, A::from_lua_multi(args, lua)?)?
.to_lua_multi(lua)
} else { } else {
Err(Error::FromLuaConversionError( Err(Error::FromLuaConversionError(
"No userdata supplied as first argument to method" "No userdata supplied as first argument to method"
@ -1063,14 +1091,17 @@ impl<'lua, T: UserData> UserDataMethods<'lua, T> {
}) })
} }
fn box_method_mut<M>(mut method: M) -> Callback<'lua> fn box_method_mut<A, R, M>(mut method: M) -> Callback<'lua>
where where
M: 'static + for<'a> FnMut(&'lua Lua, &'a mut T, MultiValue<'lua>) -> Result<MultiValue<'lua>>, A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
M: 'static + for<'a> FnMut(&'lua Lua, &'a mut T, A) -> Result<R>,
{ {
Box::new(move |lua, mut args| if let Some(front) = args.pop_front() { Box::new(move |lua, mut args| if let Some(front) = args.pop_front() {
let userdata = AnyUserData::from_lua(front, lua)?; let userdata = AnyUserData::from_lua(front, lua)?;
let mut userdata = userdata.borrow_mut::<T>()?; let mut userdata = userdata.borrow_mut::<T>()?;
method(lua, &mut userdata, args) method(lua, &mut userdata, A::from_lua_multi(args, lua)?)?
.to_lua_multi(lua)
} else { } else {
Err( Err(
Error::FromLuaConversionError( Error::FromLuaConversionError(
@ -1078,7 +1109,6 @@ impl<'lua, T: UserData> UserDataMethods<'lua, T> {
).into(), ).into(),
) )
}) })
} }
} }
@ -1121,21 +1151,17 @@ impl<'lua, T: UserData> UserDataMethods<'lua, T> {
/// ///
/// impl UserData for MyUserData { /// impl UserData for MyUserData {
/// fn add_methods(methods: &mut UserDataMethods<Self>) { /// fn add_methods(methods: &mut UserDataMethods<Self>) {
/// methods.add_method("get", |lua, this, args| { /// methods.add_method("get", |_, this, _: ()| {
/// # let _ = (lua, args); // used /// Ok(this.0)
/// lua.pack(this.0)
/// }); /// });
/// ///
/// methods.add_method_mut("add", |lua, this, args| { /// methods.add_method_mut("add", |_, this, value: i32| {
/// let value: i32 = lua.unpack(args)?;
///
/// this.0 += value; /// this.0 += value;
/// lua.pack(()) /// Ok(())
/// }); /// });
/// ///
/// methods.add_meta_method(MetaMethod::Add, |lua, this, args| { /// methods.add_meta_method(MetaMethod::Add, |_, this, value: i32| {
/// let value: i32 = lua.unpack(args)?; /// Ok(this.0 + value)
/// lua.pack(this.0 + value)
/// }); /// });
/// } /// }
/// } /// }
@ -1478,10 +1504,9 @@ impl Lua {
/// # fn try_main() -> Result<()> { /// # fn try_main() -> Result<()> {
/// let lua = Lua::new(); /// let lua = Lua::new();
/// ///
/// let greet = lua.create_function(|lua, args| { /// let greet = lua.create_function(|_, name: String| {
/// let name: String = lua.unpack(args)?;
/// println!("Hello, {}!", name); /// println!("Hello, {}!", name);
/// lua.pack(()) /// Ok(())
/// }); /// });
/// # let _ = greet; // used /// # let _ = greet; // used
/// # Ok(()) /// # Ok(())
@ -1491,19 +1516,17 @@ impl Lua {
/// # } /// # }
/// ``` /// ```
/// ///
/// Use the `hlist_macro` crate to use multiple arguments: /// Use tuples to accept multiple arguments:
/// ///
/// ``` /// ```
/// #[macro_use] extern crate hlist_macro;
/// # extern crate rlua; /// # extern crate rlua;
/// # use rlua::{Lua, Result}; /// # use rlua::{Lua, Result};
/// # fn try_main() -> Result<()> { /// # fn try_main() -> Result<()> {
/// let lua = Lua::new(); /// let lua = Lua::new();
/// ///
/// let print_person = lua.create_function(|lua, args| { /// let print_person = lua.create_function(|_, (name, age): (String, u8)| {
/// let hlist_pat![name, age]: HList![String, u8] = lua.unpack(args)?;
/// println!("{} is {} years old!", name, age); /// println!("{} is {} years old!", name, age);
/// lua.pack(()) /// Ok(())
/// }); /// });
/// # let _ = print_person; // used /// # let _ = print_person; // used
/// # Ok(()) /// # Ok(())
@ -1512,11 +1535,15 @@ impl Lua {
/// # try_main().unwrap(); /// # try_main().unwrap();
/// # } /// # }
/// ``` /// ```
pub fn create_function<'lua, F>(&'lua self, func: F) -> Function<'lua> pub fn create_function<'lua, A, R, F>(&'lua self, mut func: F) -> Function<'lua>
where where
F: 'static + FnMut(&'lua Lua, MultiValue<'lua>) -> Result<MultiValue<'lua>>, A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
F: 'static + FnMut(&'lua Lua, A) -> Result<R>,
{ {
self.create_callback_function(Box::new(func)) self.create_callback_function(Box::new(move |lua, args| {
func(lua, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
}))
} }
/// Wraps a Lua function into a new thread (or coroutine). /// Wraps a Lua function into a new thread (or coroutine).
@ -1645,25 +1672,26 @@ impl Lua {
} }
} }
pub fn from<'lua, T: ToLua<'lua>>(&'lua self, t: T) -> Result<Value<'lua>> { /// Converts a value that implements `ToLua` into a `Value` instance.
pub fn pack<'lua, T: ToLua<'lua>>(&'lua self, t: T) -> Result<Value<'lua>> {
t.to_lua(self) t.to_lua(self)
} }
pub fn to<'lua, T: FromLua<'lua>>(&'lua self, value: Value<'lua>) -> Result<T> { /// Converts a `Value` instance into a value that implements `FromLua`.
pub fn unpack<'lua, T: FromLua<'lua>>(&'lua self, value: Value<'lua>) -> Result<T> {
T::from_lua(value, self) T::from_lua(value, self)
} }
/// Packs up a value that implements `ToLuaMulti` into a `MultiValue` instance. /// Converts a value that implements `ToLuaMulti` into a `MultiValue` instance.
/// pub fn pack_multi<'lua, T: ToLuaMulti<'lua>>(&'lua self, t: T) -> Result<MultiValue<'lua>> {
/// This can be used to return arbitrary Lua values from a Rust function back to Lua.
pub fn pack<'lua, T: ToLuaMulti<'lua>>(&'lua self, t: T) -> Result<MultiValue<'lua>> {
t.to_lua_multi(self) t.to_lua_multi(self)
} }
/// Unpacks a `MultiValue` instance into a value that implements `FromLuaMulti`. /// Converts a `MultiValue` instance into a value that implements `FromLuaMulti`.
/// pub fn unpack_multi<'lua, T: FromLuaMulti<'lua>>(
/// This can be used to convert the arguments of a Rust function called by Lua. &'lua self,
pub fn unpack<'lua, T: FromLuaMulti<'lua>>(&'lua self, value: MultiValue<'lua>) -> Result<T> { value: MultiValue<'lua>,
) -> Result<T> {
T::from_lua_multi(value, self) T::from_lua_multi(value, self)
} }

View File

@ -1,22 +1,10 @@
use std::ops::{Deref, DerefMut};
use std::iter::FromIterator;
use std::result::Result as StdResult; use std::result::Result as StdResult;
use hlist_macro::{HNil, HCons};
use error::*; use error::*;
use lua::*; use lua::*;
impl<'lua> ToLuaMulti<'lua> for () {
fn to_lua_multi(self, _: &'lua Lua) -> Result<MultiValue> {
Ok(MultiValue::new())
}
}
impl<'lua> FromLuaMulti<'lua> for () {
fn from_lua_multi(_: MultiValue, _: &'lua Lua) -> Result<Self> {
Ok(())
}
}
/// Result is convertible to `MultiValue` following the common lua idiom of returning the result /// Result is convertible to `MultiValue` following the common lua idiom of returning the result
/// on success, or in the case of an error, returning nil followed by the error /// on success, or in the case of an error, returning nil followed by the error
impl<'lua, T: ToLua<'lua>, E: ToLua<'lua>> ToLuaMulti<'lua> for StdResult<T, E> { impl<'lua, T: ToLua<'lua>, E: ToLua<'lua>> ToLuaMulti<'lua> for StdResult<T, E> {
@ -63,9 +51,45 @@ impl<'lua> FromLuaMulti<'lua> for MultiValue<'lua> {
/// Can be used to pass variadic values to or receive variadic values from Lua, where the type of /// Can be used to pass variadic values to or receive variadic values from Lua, where the type of
/// the values is all the same and the number of values is defined at runtime. This can be included /// the values is all the same and the number of values is defined at runtime. This can be included
/// in an hlist when unpacking, but must be the final entry, and will consume the rest of the /// in tuple when unpacking, but must be the final entry, and will consume the rest of the
/// parameters given. /// parameters given.
pub struct Variadic<T>(pub Vec<T>); #[derive(Debug, Clone)]
pub struct Variadic<T>(Vec<T>);
impl<T> Variadic<T> {
pub fn new() -> Variadic<T> {
Variadic(Vec::new())
}
}
impl<T> FromIterator<T> for Variadic<T> {
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
Variadic(Vec::from_iter(iter))
}
}
impl<T> IntoIterator for Variadic<T> {
type Item = T;
type IntoIter = <Vec<T> as IntoIterator>::IntoIter;
fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
}
}
impl<T> Deref for Variadic<T> {
type Target = Vec<T>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<T> DerefMut for Variadic<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl<'lua, T: ToLua<'lua>> ToLuaMulti<'lua> for Variadic<T> { impl<'lua, T: ToLua<'lua>> ToLuaMulti<'lua> for Variadic<T> {
fn to_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>> { fn to_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>> {
@ -83,48 +107,75 @@ impl<'lua, T: FromLua<'lua>> FromLuaMulti<'lua> for Variadic<T> {
} }
} }
impl<'lua> ToLuaMulti<'lua> for HNil { macro_rules! impl_tuple {
fn to_lua_multi(self, _: &'lua Lua) -> Result<MultiValue<'lua>> { () => (
Ok(MultiValue::new()) impl<'lua> ToLuaMulti<'lua> for () {
} fn to_lua_multi(self, _: &'lua Lua) -> Result<MultiValue> {
Ok(MultiValue::new())
}
}
impl<'lua> FromLuaMulti<'lua> for () {
fn from_lua_multi(_: MultiValue, _: &'lua Lua) -> Result<Self> {
Ok(())
}
}
);
($last:ident $($name:ident)*) => (
impl<'lua, $($name,)* $last> ToLuaMulti<'lua> for ($($name,)* $last,)
where $($name: ToLua<'lua>,)*
$last: ToLuaMulti<'lua>
{
#[allow(unused_mut)]
#[allow(non_snake_case)]
fn to_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>> {
let ($($name,)* $last,) = self;
let mut results = $last.to_lua_multi(lua)?;
push_reverse!(results, $($name.to_lua(lua)?,)*);
Ok(results)
}
}
impl<'lua, $($name,)* $last> FromLuaMulti<'lua> for ($($name,)* $last,)
where $($name: FromLua<'lua>,)*
$last: FromLuaMulti<'lua>
{
#[allow(unused_mut)]
#[allow(non_snake_case)]
fn from_lua_multi(mut values: MultiValue<'lua>, lua: &'lua Lua) -> Result<Self> {
$(let $name = values.pop_front().unwrap_or(Nil);)*
let $last = FromLuaMulti::from_lua_multi(values, lua)?;
Ok(($(FromLua::from_lua($name, lua)?,)* $last,))
}
}
);
} }
impl<'lua> FromLuaMulti<'lua> for HNil { macro_rules! push_reverse {
fn from_lua_multi(_: MultiValue<'lua>, _: &'lua Lua) -> Result<Self> { ($multi_value:expr, $first:expr, $($rest:expr,)*) => (
Ok(HNil) push_reverse!($multi_value, $($rest,)*);
} $multi_value.push_front($first);
);
($multi_value:expr, $first:expr) => (
$multi_value.push_front($first);
);
($multi_value:expr,) => ();
} }
impl<'lua, T: ToLuaMulti<'lua>> ToLuaMulti<'lua> for HCons<T, HNil> { impl_tuple!{}
fn to_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>> { impl_tuple!{A}
self.0.to_lua_multi(lua) impl_tuple!{A B}
} impl_tuple!{A B C}
} impl_tuple!{A B C D}
impl_tuple!{A B C D E}
impl<'lua, T: FromLuaMulti<'lua>> FromLuaMulti<'lua> for HCons<T, HNil> { impl_tuple!{A B C D E F}
fn from_lua_multi(values: MultiValue<'lua>, lua: &'lua Lua) -> Result<Self> { impl_tuple!{A B C D E F G}
Ok(HCons(T::from_lua_multi(values, lua)?, HNil)) impl_tuple!{A B C D E F G H}
} impl_tuple!{A B C D E F G H I}
} impl_tuple!{A B C D E F G H I J}
impl_tuple!{A B C D E F G H I J K}
impl<'lua, H: ToLua<'lua>, A, B> ToLuaMulti<'lua> for HCons<H, HCons<A, B>> impl_tuple!{A B C D E F G H I J K L}
where
HCons<A, B>: ToLuaMulti<'lua>,
{
fn to_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>> {
let mut results = self.1.to_lua_multi(lua)?;
results.push_front(self.0.to_lua(lua)?);
Ok(results)
}
}
impl<'lua, H: FromLua<'lua>, A, B> FromLuaMulti<'lua> for HCons<H, HCons<A, B>>
where
HCons<A, B>: FromLuaMulti<'lua>,
{
fn from_lua_multi(mut values: MultiValue<'lua>, lua: &'lua Lua) -> Result<Self> {
let val = H::from_lua(values.pop_front().unwrap_or(Nil), lua)?;
let res = HCons::<A, B>::from_lua_multi(values, lua)?;
Ok(HCons(val, res))
}
}

View File

@ -173,10 +173,7 @@ fn test_function() {
).unwrap(); ).unwrap();
let concat = globals.get::<_, Function>("concat").unwrap(); let concat = globals.get::<_, Function>("concat").unwrap();
assert_eq!( assert_eq!(concat.call::<_, String>(("foo", "bar")).unwrap(), "foobar");
concat.call::<_, String>(hlist!["foo", "bar"]).unwrap(),
"foobar"
);
} }
#[test] #[test]
@ -199,9 +196,9 @@ fn test_bind() {
let mut concat = globals.get::<_, Function>("concat").unwrap(); let mut concat = globals.get::<_, Function>("concat").unwrap();
concat = concat.bind("foo").unwrap(); concat = concat.bind("foo").unwrap();
concat = concat.bind("bar").unwrap(); concat = concat.bind("bar").unwrap();
concat = concat.bind(hlist!["baz", "baf"]).unwrap(); concat = concat.bind(("baz", "baf")).unwrap();
assert_eq!( assert_eq!(
concat.call::<_, String>(hlist!["hi", "wut"]).unwrap(), concat.call::<_, String>(("hi", "wut")).unwrap(),
"foobarbazbafhiwut" "foobarbazbafhiwut"
); );
} }
@ -223,9 +220,7 @@ fn test_rust_function() {
).unwrap(); ).unwrap();
let lua_function = globals.get::<_, Function>("lua_function").unwrap(); let lua_function = globals.get::<_, Function>("lua_function").unwrap();
let rust_function = lua.create_function(|lua, _| { let rust_function = lua.create_function(|_, ()| Ok("hello"));
lua.pack("hello")
});
globals.set("rust_function", rust_function).unwrap(); globals.set("rust_function", rust_function).unwrap();
assert_eq!(lua_function.call::<_, String>(()).unwrap(), "hello"); assert_eq!(lua_function.call::<_, String>(()).unwrap(), "hello");
@ -259,10 +254,10 @@ fn test_methods() {
impl UserData for MyUserData { impl UserData for MyUserData {
fn add_methods(methods: &mut UserDataMethods<Self>) { fn add_methods(methods: &mut UserDataMethods<Self>) {
methods.add_method("get_value", |lua, data, _| lua.pack(data.0)); methods.add_method("get_value", |_, data, ()| Ok(data.0));
methods.add_method_mut("set_value", |lua, data, args| { methods.add_method_mut("set_value", |_, data, args| {
data.0 = lua.unpack(args)?; data.0 = args;
lua.pack(()) Ok(())
}); });
} }
} }
@ -299,23 +294,21 @@ fn test_metamethods() {
impl UserData for MyUserData { impl UserData for MyUserData {
fn add_methods(methods: &mut UserDataMethods<Self>) { fn add_methods(methods: &mut UserDataMethods<Self>) {
methods.add_method("get", |lua, data, _| lua.pack(data.0)); methods.add_method("get", |_, data, ()| Ok(data.0));
methods.add_meta_function(MetaMethod::Add, |lua, args| { methods.add_meta_function(MetaMethod::Add, |_, (lhs, rhs): (MyUserData, MyUserData)| {
let hlist_pat![lhs, rhs] = lua.unpack::<HList![MyUserData, MyUserData]>(args)?; Ok(MyUserData(lhs.0 + rhs.0))
lua.pack(MyUserData(lhs.0 + rhs.0))
}); });
methods.add_meta_function(MetaMethod::Sub, |lua, args| { methods.add_meta_function(MetaMethod::Sub, |_, (lhs, rhs): (MyUserData, MyUserData)| {
let hlist_pat![lhs, rhs] = lua.unpack::<HList![MyUserData, MyUserData]>(args)?; Ok(MyUserData(lhs.0 - rhs.0))
lua.pack(MyUserData(lhs.0 - rhs.0))
}); });
methods.add_meta_method(MetaMethod::Index, |lua, data, args| { methods.add_meta_method(
let index = lua.unpack::<LuaString>(args)?; MetaMethod::Index,
if index.to_str()? == "inner" { |_, data, index: LuaString| if index.to_str()? == "inner" {
lua.pack(data.0) Ok(data.0)
} else { } else {
Err("no such custom index".to_lua_err()) Err("no such custom index".to_lua_err())
} },
}); );
} }
} }
@ -385,16 +378,12 @@ fn test_lua_multi() {
let concat = globals.get::<_, Function>("concat").unwrap(); let concat = globals.get::<_, Function>("concat").unwrap();
let mreturn = globals.get::<_, Function>("mreturn").unwrap(); let mreturn = globals.get::<_, Function>("mreturn").unwrap();
assert_eq!( assert_eq!(concat.call::<_, String>(("foo", "bar")).unwrap(), "foobar");
concat.call::<_, String>(hlist!["foo", "bar"]).unwrap(), let (a, b) = mreturn.call::<_, (u64, u64)>(()).unwrap();
"foobar"
);
let hlist_pat![a, b] = mreturn.call::<_, HList![u64, u64]>(hlist![]).unwrap();
assert_eq!((a, b), (1, 2)); assert_eq!((a, b), (1, 2));
let hlist_pat![a, b, Variadic(v)] = let (a, b, v) = mreturn.call::<_, (u64, u64, Variadic<u64>)>(()).unwrap();
mreturn.call::<_, HList![u64, u64, Variadic<u64>]>(hlist![]).unwrap();
assert_eq!((a, b), (1, 2)); assert_eq!((a, b), (1, 2));
assert_eq!(v, vec![3, 4, 5, 6]); assert_eq!(v[..], [3, 4, 5, 6]);
} }
#[test] #[test]
@ -491,7 +480,8 @@ fn test_error() {
None, None,
).unwrap(); ).unwrap();
let rust_error_function = lua.create_function(|_, _| Err(TestError.to_lua_err())); let rust_error_function =
lua.create_function(|_, ()| -> Result<()> { Err(TestError.to_lua_err()) });
globals globals
.set("rust_error_function", rust_error_function) .set("rust_error_function", rust_error_function)
.unwrap(); .unwrap();
@ -550,7 +540,7 @@ fn test_error() {
"#, "#,
None, None,
)?; )?;
let rust_panic_function = lua.create_function(|_, _| { let rust_panic_function = lua.create_function(|_, ()| -> Result<()> {
panic!("expected panic, this panic should be caught in rust") panic!("expected panic, this panic should be caught in rust")
}); });
globals.set("rust_panic_function", rust_panic_function)?; globals.set("rust_panic_function", rust_panic_function)?;
@ -576,7 +566,7 @@ fn test_error() {
"#, "#,
None, None,
)?; )?;
let rust_panic_function = lua.create_function(|_, _| { let rust_panic_function = lua.create_function(|_, ()| -> Result<()> {
panic!("expected panic, this panic should be caught in rust") panic!("expected panic, this panic should be caught in rust")
}); });
globals.set("rust_panic_function", rust_panic_function)?; globals.set("rust_panic_function", rust_panic_function)?;
@ -733,12 +723,12 @@ fn test_result_conversions() {
let lua = Lua::new(); let lua = Lua::new();
let globals = lua.globals(); let globals = lua.globals();
let err = lua.create_function(|lua, _| { let err = lua.create_function(|_, ()| {
lua.pack(Err::<String, _>( Ok(Err::<String, _>(
"only through failure can we succeed".to_lua_err(), "only through failure can we succeed".to_lua_err(),
)) ))
}); });
let ok = lua.create_function(|lua, _| lua.pack(Ok::<_, Error>("!".to_owned()))); let ok = lua.create_function(|_, ()| Ok(Ok::<_, Error>("!".to_owned())));
globals.set("err", err).unwrap(); globals.set("err", err).unwrap();
globals.set("ok", ok).unwrap(); globals.set("ok", ok).unwrap();
@ -790,9 +780,9 @@ fn test_expired_userdata() {
impl UserData for MyUserdata { impl UserData for MyUserdata {
fn add_methods(methods: &mut UserDataMethods<Self>) { fn add_methods(methods: &mut UserDataMethods<Self>) {
methods.add_method("access", |lua, this, _| { methods.add_method("access", |_, this, ()| {
assert!(this.id == 123); assert!(this.id == 123);
lua.pack(()) Ok(())
}); });
} }
} }
@ -851,17 +841,26 @@ fn detroys_userdata() {
#[test] #[test]
fn string_views() { fn string_views() {
let lua = Lua::new(); let lua = Lua::new();
lua.eval::<()>(r#" lua.eval::<()>(
r#"
ok = "null bytes are valid utf-8, wh\0 knew?" ok = "null bytes are valid utf-8, wh\0 knew?"
err = "but \xff isn't :(" err = "but \xff isn't :("
"#, None).unwrap(); "#,
None,
).unwrap();
let globals = lua.globals(); let globals = lua.globals();
let ok: LuaString = globals.get("ok").unwrap(); let ok: LuaString = globals.get("ok").unwrap();
let err: LuaString = globals.get("err").unwrap(); let err: LuaString = globals.get("err").unwrap();
assert_eq!(ok.to_str().unwrap(), "null bytes are valid utf-8, wh\0 knew?"); assert_eq!(
assert_eq!(ok.as_bytes(), &b"null bytes are valid utf-8, wh\0 knew?"[..]); ok.to_str().unwrap(),
"null bytes are valid utf-8, wh\0 knew?"
);
assert_eq!(
ok.as_bytes(),
&b"null bytes are valid utf-8, wh\0 knew?"[..]
);
assert!(err.to_str().is_err()); assert!(err.to_str().is_err());
assert_eq!(err.as_bytes(), &b"but \xff isn't :("[..]); assert_eq!(err.as_bytes(), &b"but \xff isn't :("[..]);
@ -870,9 +869,7 @@ fn string_views() {
#[test] #[test]
fn coroutine_from_closure() { fn coroutine_from_closure() {
let lua = Lua::new(); let lua = Lua::new();
let thrd_main = lua.create_function(|lua, _| { let thrd_main = lua.create_function(|_, ()| Ok(()));
lua.pack(())
});
lua.globals().set("main", thrd_main).unwrap(); lua.globals().set("main", thrd_main).unwrap();
let thrd: Thread = lua.eval("coroutine.create(main)", None).unwrap(); let thrd: Thread = lua.eval("coroutine.create(main)", None).unwrap();
thrd.resume::<_, ()>(()).unwrap(); thrd.resume::<_, ()>(()).unwrap();
@ -882,10 +879,10 @@ fn coroutine_from_closure() {
#[should_panic] #[should_panic]
fn coroutine_panic() { fn coroutine_panic() {
let lua = Lua::new(); let lua = Lua::new();
let thrd_main = lua.create_function(|lua, _| { let thrd_main = lua.create_function(|lua, ()| {
// whoops, 'main' has a wrong type // whoops, 'main' has a wrong type
let _coro: u32 = lua.globals().get("main").unwrap(); let _coro: u32 = lua.globals().get("main").unwrap();
lua.pack(()) Ok(())
}); });
lua.globals().set("main", thrd_main.clone()).unwrap(); lua.globals().set("main", thrd_main.clone()).unwrap();
let thrd: Thread = lua.create_thread(thrd_main); let thrd: Thread = lua.create_thread(thrd_main);