mirror of
https://github.com/mlua-rs/mlua
synced 2026-06-08 16:05:43 +00:00
Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e9c2b8d306 | |||
| d672e19365 | |||
| bda399a5b4 | |||
| fe5e87b0f5 | |||
| 0f32e9cb43 | |||
| 0efa0fcb6a | |||
| 4e19ae6ccf | |||
| c826798a6d | |||
| ee08050c1f | |||
| d8897d867b | |||
| 222f4df668 |
+3
-3
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "mlua"
|
name = "mlua"
|
||||||
version = "0.3.0"
|
version = "0.3.2"
|
||||||
authors = ["Aleksandr Orlenko <zxteam@pm.me>", "kyren <catherine@chucklefish.org>"]
|
authors = ["Aleksandr Orlenko <zxteam@pm.me>", "kyren <catherine@chucklefish.org>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
repository = "https://github.com/khvzak/mlua"
|
repository = "https://github.com/khvzak/mlua"
|
||||||
@@ -38,6 +38,7 @@ async = ["futures-core", "futures-task", "futures-util"]
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bstr = { version = "0.2", features = ["std"], default_features = false }
|
bstr = { version = "0.2", features = ["std"], default_features = false }
|
||||||
|
lazy_static = { version = "1.4" }
|
||||||
num-traits = { version = "0.2.11" }
|
num-traits = { version = "0.2.11" }
|
||||||
futures-core = { version = "0.3.4", optional = true }
|
futures-core = { version = "0.3.4", optional = true }
|
||||||
futures-task = { version = "0.3.4", optional = true }
|
futures-task = { version = "0.3.4", optional = true }
|
||||||
@@ -53,10 +54,9 @@ luajit-src = { version = "210.0.0", optional = true }
|
|||||||
rustyline = "6.0"
|
rustyline = "6.0"
|
||||||
criterion = "0.3"
|
criterion = "0.3"
|
||||||
trybuild = "1.0"
|
trybuild = "1.0"
|
||||||
|
futures = "0.3.4"
|
||||||
hyper = "0.13"
|
hyper = "0.13"
|
||||||
tokio = { version = "0.2.18", features = ["full"] }
|
tokio = { version = "0.2.18", features = ["full"] }
|
||||||
futures-executor = "0.3.4"
|
|
||||||
futures-util = "0.3.4"
|
|
||||||
futures-timer = "3.0"
|
futures-timer = "3.0"
|
||||||
|
|
||||||
[[bench]]
|
[[bench]]
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ modules in Rust.
|
|||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
### Async
|
### Async/await support
|
||||||
|
|
||||||
Starting from 0.3, mlua supports async/await for all Lua versions. This works using Lua [coroutines](https://www.lua.org/manual/5.3/manual.html#2.6) and require running [Thread](https://docs.rs/mlua/latest/mlua/struct.Thread.html) along with enabling `async` feature in `Cargo.toml`.
|
Starting from 0.3, mlua supports async/await for all Lua versions. This works using Lua [coroutines](https://www.lua.org/manual/5.3/manual.html#2.6) and require running [Thread](https://docs.rs/mlua/latest/mlua/struct.Thread.html) along with enabling `async` feature in `Cargo.toml`.
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,34 @@
|
|||||||
|
use std::cell::RefCell;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
use hyper::Client as HyperClient;
|
use bstr::BString;
|
||||||
|
use hyper::{body::Body as HyperBody, Client as HyperClient};
|
||||||
|
use tokio::stream::StreamExt;
|
||||||
|
|
||||||
use mlua::{Error, Lua, Result, Thread};
|
use mlua::{Error, Lua, Result, UserData, UserDataMethods};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct BodyReader(Rc<RefCell<HyperBody>>);
|
||||||
|
|
||||||
|
impl BodyReader {
|
||||||
|
fn new(body: HyperBody) -> Self {
|
||||||
|
BodyReader(Rc::new(RefCell::new(body)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UserData for BodyReader {
|
||||||
|
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||||
|
methods.add_async_method("read", |_, reader, ()| async move {
|
||||||
|
let mut reader = reader.0.borrow_mut();
|
||||||
|
let bytes = reader.try_next().await.map_err(Error::external)?;
|
||||||
|
if let Some(bytes) = bytes {
|
||||||
|
return Ok(Some(BString::from(bytes.as_ref())));
|
||||||
|
}
|
||||||
|
Ok(None)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> Result<()> {
|
async fn main() -> Result<()> {
|
||||||
@@ -23,10 +49,9 @@ async fn main() -> Result<()> {
|
|||||||
.or_insert(Vec::new())
|
.or_insert(Vec::new())
|
||||||
.push(value.to_str().unwrap());
|
.push(value.to_str().unwrap());
|
||||||
}
|
}
|
||||||
lua_resp.set("headers", headers)?;
|
|
||||||
|
|
||||||
let buf = hyper::body::to_bytes(resp).await.map_err(Error::external)?;
|
lua_resp.set("headers", headers)?;
|
||||||
lua_resp.set("body", String::from_utf8_lossy(&buf).into_owned())?;
|
lua_resp.set("body", BodyReader::new(resp.into_body()))?;
|
||||||
|
|
||||||
Ok(lua_resp)
|
Ok(lua_resp)
|
||||||
})?;
|
})?;
|
||||||
@@ -34,22 +59,25 @@ async fn main() -> Result<()> {
|
|||||||
let globals = lua.globals();
|
let globals = lua.globals();
|
||||||
globals.set("fetch_url", fetch_url)?;
|
globals.set("fetch_url", fetch_url)?;
|
||||||
|
|
||||||
let thread = lua
|
let f = lua
|
||||||
.load(
|
.load(
|
||||||
r#"
|
r#"
|
||||||
coroutine.create(function ()
|
local res = fetch_url(...);
|
||||||
local res = fetch_url("http://httpbin.org/ip");
|
print(res.status)
|
||||||
print(res.status)
|
for key, vals in pairs(res.headers) do
|
||||||
for key, vals in pairs(res.headers) do
|
for _, val in ipairs(vals) do
|
||||||
for _, val in ipairs(vals) do
|
print(key..": "..val)
|
||||||
print(key..": "..val)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
print(res.body)
|
end
|
||||||
end)
|
repeat
|
||||||
|
local body = res.body:read()
|
||||||
|
if body then
|
||||||
|
print(body)
|
||||||
|
end
|
||||||
|
until not body
|
||||||
"#,
|
"#,
|
||||||
)
|
)
|
||||||
.eval::<Thread>()?;
|
.into_function()?;
|
||||||
|
|
||||||
thread.into_async(()).await
|
f.call_async("http://httpbin.org/ip").await
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,30 +1,37 @@
|
|||||||
|
use std::cell::RefCell;
|
||||||
use std::net::Shutdown;
|
use std::net::Shutdown;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use bstr::BString;
|
use bstr::BString;
|
||||||
use tokio::net::{TcpListener, TcpStream};
|
use tokio::net::{TcpListener, TcpStream};
|
||||||
use tokio::prelude::*;
|
use tokio::prelude::*;
|
||||||
use tokio::sync::Mutex;
|
|
||||||
use tokio::task;
|
use tokio::task;
|
||||||
|
|
||||||
use mlua::{Function, Lua, Result, Thread, UserData, UserDataMethods};
|
use mlua::{Function, Lua, Result, UserData, UserDataMethods};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct LuaTcpListener(Option<Rc<Mutex<TcpListener>>>);
|
struct LuaTcp;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct LuaTcpStream(Rc<Mutex<TcpStream>>);
|
struct LuaTcpListener(Rc<RefCell<TcpListener>>);
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct LuaTcpStream(Rc<RefCell<TcpStream>>);
|
||||||
|
|
||||||
|
impl UserData for LuaTcp {
|
||||||
|
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||||
|
methods.add_async_function("bind", |_, addr: String| async move {
|
||||||
|
let listener = TcpListener::bind(addr).await?;
|
||||||
|
Ok(LuaTcpListener(Rc::new(RefCell::new(listener))))
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl UserData for LuaTcpListener {
|
impl UserData for LuaTcpListener {
|
||||||
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||||
methods.add_async_function("bind", |_, addr: String| async {
|
methods.add_async_method("accept", |_, listener, ()| async move {
|
||||||
let listener = TcpListener::bind(addr).await?;
|
let (stream, _) = listener.0.borrow_mut().accept().await?;
|
||||||
Ok(LuaTcpListener(Some(Rc::new(Mutex::new(listener)))))
|
Ok(LuaTcpStream(Rc::new(RefCell::new(stream))))
|
||||||
});
|
|
||||||
|
|
||||||
methods.add_async_method("accept", |_, listener, ()| async {
|
|
||||||
let (stream, _) = listener.0.unwrap().lock().await.accept().await?;
|
|
||||||
Ok(LuaTcpStream(Rc::new(Mutex::new(stream))))
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -32,25 +39,23 @@ impl UserData for LuaTcpListener {
|
|||||||
impl UserData for LuaTcpStream {
|
impl UserData for LuaTcpStream {
|
||||||
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||||
methods.add_async_method("peer_addr", |_, stream, ()| async move {
|
methods.add_async_method("peer_addr", |_, stream, ()| async move {
|
||||||
Ok(stream.0.lock().await.peer_addr()?.to_string())
|
Ok(stream.0.borrow().peer_addr()?.to_string())
|
||||||
});
|
});
|
||||||
|
|
||||||
methods.add_async_method("read", |_, stream, size: usize| async move {
|
methods.add_async_method("read", |_, stream, size: usize| async move {
|
||||||
let mut buf = vec![0; size];
|
let mut buf = vec![0; size];
|
||||||
let mut stream = stream.0.lock().await;
|
let n = stream.0.borrow_mut().read(&mut buf).await?;
|
||||||
let n = stream.read(&mut buf).await?;
|
|
||||||
buf.truncate(n);
|
buf.truncate(n);
|
||||||
Ok(BString::from(buf))
|
Ok(BString::from(buf))
|
||||||
});
|
});
|
||||||
|
|
||||||
methods.add_async_method("write", |_, stream, data: BString| async move {
|
methods.add_async_method("write", |_, stream, data: BString| async move {
|
||||||
let mut stream = stream.0.lock().await;
|
let n = stream.0.borrow_mut().write(&data).await?;
|
||||||
let n = stream.write(&data).await?;
|
|
||||||
Ok(n)
|
Ok(n)
|
||||||
});
|
});
|
||||||
|
|
||||||
methods.add_async_method("close", |_, stream, ()| async move {
|
methods.add_method("close", |_, stream, ()| {
|
||||||
stream.0.lock().await.shutdown(Shutdown::Both)?;
|
stream.0.borrow().shutdown(Shutdown::Both)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -60,44 +65,43 @@ impl UserData for LuaTcpStream {
|
|||||||
async fn main() -> Result<()> {
|
async fn main() -> Result<()> {
|
||||||
let lua = Lua::new();
|
let lua = Lua::new();
|
||||||
|
|
||||||
|
let spawn = lua.create_function(move |_, func: Function| {
|
||||||
|
task::spawn_local(async move { func.call_async::<_, ()>(()).await.unwrap() });
|
||||||
|
Ok(())
|
||||||
|
})?;
|
||||||
|
|
||||||
let globals = lua.globals();
|
let globals = lua.globals();
|
||||||
globals.set("tcp", LuaTcpListener(None))?;
|
globals.set("tcp", LuaTcp)?;
|
||||||
|
globals.set("spawn", spawn)?;
|
||||||
|
|
||||||
globals.set(
|
let server = lua
|
||||||
"spawn",
|
|
||||||
lua.create_function(move |lua: &Lua, func: Function| {
|
|
||||||
let fut = lua.create_thread(func)?.into_async::<_, ()>(());
|
|
||||||
task::spawn_local(async move { fut.await.unwrap() });
|
|
||||||
Ok(())
|
|
||||||
})?,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let thread = lua
|
|
||||||
.load(
|
.load(
|
||||||
r#"
|
r#"
|
||||||
coroutine.create(function ()
|
local addr = ...
|
||||||
local listener = tcp.bind("0.0.0.0:1234")
|
local listener = tcp.bind(addr)
|
||||||
print("listening on 0.0.0.0:1234")
|
print("listening on "..addr)
|
||||||
while true do
|
while true do
|
||||||
local stream = listener:accept()
|
local stream = listener:accept()
|
||||||
print("connected from " .. stream:peer_addr())
|
local peer_addr = stream:peer_addr()
|
||||||
spawn(function()
|
print("connected from "..peer_addr)
|
||||||
while true do
|
spawn(function()
|
||||||
local data = stream:read(100)
|
while true do
|
||||||
data = data:match("^%s*(.-)%s*$") -- trim
|
local data = stream:read(100)
|
||||||
print(data)
|
data = data:match("^%s*(.-)%s*$") -- trim
|
||||||
stream:write("got: "..data.."\n")
|
print("["..peer_addr.."] "..data)
|
||||||
if data == "exit" then
|
stream:write("got: "..data.."\n")
|
||||||
stream:close()
|
if data == "exit" then
|
||||||
break
|
stream:close()
|
||||||
end
|
break
|
||||||
end
|
end
|
||||||
end)
|
end
|
||||||
end
|
end)
|
||||||
end)
|
end
|
||||||
"#,
|
"#,
|
||||||
)
|
)
|
||||||
.eval::<Thread>()?;
|
.into_function()?;
|
||||||
|
|
||||||
thread.into_async(()).await
|
task::LocalSet::new()
|
||||||
|
.run_until(server.call_async::<_, ()>("0.0.0.0:1234"))
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -373,6 +373,50 @@ macro_rules! lua_convert_float {
|
|||||||
lua_convert_float!(f32);
|
lua_convert_float!(f32);
|
||||||
lua_convert_float!(f64);
|
lua_convert_float!(f64);
|
||||||
|
|
||||||
|
impl<'lua, T> ToLua<'lua> for &'_ [T]
|
||||||
|
where
|
||||||
|
T: Clone + ToLua<'lua>,
|
||||||
|
{
|
||||||
|
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||||
|
Ok(Value::Table(
|
||||||
|
lua.create_sequence_from(self.into_iter().cloned())?,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! lua_convert_array {
|
||||||
|
($($N:literal)+) => {
|
||||||
|
$(
|
||||||
|
impl<'lua, T> ToLua<'lua> for [T; $N]
|
||||||
|
where
|
||||||
|
T: Clone + ToLua<'lua>,
|
||||||
|
{
|
||||||
|
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||||
|
(&self).to_lua(lua)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'lua, T> ToLua<'lua> for &'_ [T; $N]
|
||||||
|
where
|
||||||
|
T: Clone + ToLua<'lua>,
|
||||||
|
{
|
||||||
|
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||||
|
Ok(Value::Table(
|
||||||
|
lua.create_sequence_from(self.iter().cloned())?,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)+
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lua_convert_array! {
|
||||||
|
0 1 2 3 4 5 6 7 8 9
|
||||||
|
10 11 12 13 14 15 16 17 18 19
|
||||||
|
20 21 22 23 24 25 26 27 28 29
|
||||||
|
30 31 32
|
||||||
|
}
|
||||||
|
|
||||||
impl<'lua, T: ToLua<'lua>> ToLua<'lua> for Vec<T> {
|
impl<'lua, T: ToLua<'lua>> ToLua<'lua> for Vec<T> {
|
||||||
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||||
Ok(Value::Table(lua.create_sequence_from(self)?))
|
Ok(Value::Table(lua.create_sequence_from(self)?))
|
||||||
|
|||||||
@@ -9,6 +9,9 @@ use crate::util::{
|
|||||||
};
|
};
|
||||||
use crate::value::{FromLuaMulti, MultiValue, ToLuaMulti};
|
use crate::value::{FromLuaMulti, MultiValue, ToLuaMulti};
|
||||||
|
|
||||||
|
#[cfg(feature = "async")]
|
||||||
|
use {futures_core::future::LocalBoxFuture, futures_util::future};
|
||||||
|
|
||||||
/// Handle to an internal Lua function.
|
/// Handle to an internal Lua function.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Function<'lua>(pub(crate) LuaRef<'lua>);
|
pub struct Function<'lua>(pub(crate) LuaRef<'lua>);
|
||||||
@@ -86,6 +89,47 @@ impl<'lua> Function<'lua> {
|
|||||||
R::from_lua_multi(results, lua)
|
R::from_lua_multi(results, lua)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns a Feature that, when polled, calls `self`, passing `args` as function arguments,
|
||||||
|
/// and drives the execution.
|
||||||
|
///
|
||||||
|
/// Internaly it wraps the function to an [`AsyncThread`].
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use std::time::Duration;
|
||||||
|
/// use futures_timer::Delay;
|
||||||
|
/// # use mlua::{Lua, Result};
|
||||||
|
/// # #[tokio::main]
|
||||||
|
/// # async fn main() -> Result<()> {
|
||||||
|
/// # let lua = Lua::new();
|
||||||
|
///
|
||||||
|
/// let sleep = lua.create_async_function(move |_lua, n: u64| async move {
|
||||||
|
/// Delay::new(Duration::from_millis(n)).await;
|
||||||
|
/// Ok(())
|
||||||
|
/// })?;
|
||||||
|
///
|
||||||
|
/// sleep.call_async(10).await?;
|
||||||
|
///
|
||||||
|
/// # Ok(())
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// [`AsyncThread`]: struct.AsyncThread.html
|
||||||
|
#[cfg(feature = "async")]
|
||||||
|
pub fn call_async<'fut, A, R>(&self, args: A) -> LocalBoxFuture<'fut, Result<R>>
|
||||||
|
where
|
||||||
|
'lua: 'fut,
|
||||||
|
A: ToLuaMulti<'lua>,
|
||||||
|
R: FromLuaMulti<'lua> + 'fut,
|
||||||
|
{
|
||||||
|
let lua = self.0.lua;
|
||||||
|
match lua.create_thread(self.clone()) {
|
||||||
|
Ok(t) => Box::pin(t.into_async(args)),
|
||||||
|
Err(e) => Box::pin(future::err(e)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns a function that, when called, calls `self`, passing `args` as the first set of
|
/// Returns a function that, when called, calls `self`, passing `args` as the first set of
|
||||||
/// arguments.
|
/// arguments.
|
||||||
///
|
///
|
||||||
|
|||||||
+11
-1
@@ -24,6 +24,12 @@
|
|||||||
//! The [`UserData`] trait can be implemented by user-defined types to make them available to Lua.
|
//! The [`UserData`] trait can be implemented by user-defined types to make them available to Lua.
|
||||||
//! Methods and operators to be used from Lua can be added using the [`UserDataMethods`] API.
|
//! Methods and operators to be used from Lua can be added using the [`UserDataMethods`] API.
|
||||||
//!
|
//!
|
||||||
|
//! # Async/await support
|
||||||
|
//!
|
||||||
|
//! The [`create_async_function`] allows creating non-blocking functions that returns [`Future`].
|
||||||
|
//! Lua code with async capabilities can be executed by [`call_async`] family of functions or polling
|
||||||
|
//! [`AsyncThread`] using any runtime (eg. Tokio).
|
||||||
|
//!
|
||||||
//! [Lua programming language]: https://www.lua.org/
|
//! [Lua programming language]: https://www.lua.org/
|
||||||
//! [`Lua`]: struct.Lua.html
|
//! [`Lua`]: struct.Lua.html
|
||||||
//! [executing]: struct.Lua.html#method.exec
|
//! [executing]: struct.Lua.html#method.exec
|
||||||
@@ -35,6 +41,10 @@
|
|||||||
//! [`FromLuaMulti`]: trait.FromLuaMulti.html
|
//! [`FromLuaMulti`]: trait.FromLuaMulti.html
|
||||||
//! [`UserData`]: trait.UserData.html
|
//! [`UserData`]: trait.UserData.html
|
||||||
//! [`UserDataMethods`]: trait.UserDataMethods.html
|
//! [`UserDataMethods`]: trait.UserDataMethods.html
|
||||||
|
//! [`create_async_function`]: struct.Lua.html#method.create_async_function
|
||||||
|
//! [`call_async`]: struct.Function.html#method.call_async
|
||||||
|
//! [`AsyncThread`]: struct.AsyncThread.html
|
||||||
|
//! [`Future`]: ../futures_core/future/trait.Future.html
|
||||||
|
|
||||||
// Deny warnings inside doc tests / examples. When this isn't present, rustdoc doesn't show *any*
|
// Deny warnings inside doc tests / examples. When this isn't present, rustdoc doesn't show *any*
|
||||||
// warnings at all.
|
// warnings at all.
|
||||||
@@ -66,7 +76,7 @@ pub use crate::lua::{Chunk, Lua};
|
|||||||
pub use crate::multi::Variadic;
|
pub use crate::multi::Variadic;
|
||||||
pub use crate::stdlib::StdLib;
|
pub use crate::stdlib::StdLib;
|
||||||
pub use crate::string::String;
|
pub use crate::string::String;
|
||||||
pub use crate::table::{Table, TablePairs, TableSequence};
|
pub use crate::table::{Table, TableExt, TablePairs, TableSequence};
|
||||||
pub use crate::thread::{Thread, ThreadStatus};
|
pub use crate::thread::{Thread, ThreadStatus};
|
||||||
pub use crate::types::{Integer, LightUserData, Number, RegistryKey};
|
pub use crate::types::{Integer, LightUserData, Number, RegistryKey};
|
||||||
pub use crate::userdata::{AnyUserData, MetaMethod, UserData, UserDataMethods};
|
pub use crate::userdata::{AnyUserData, MetaMethod, UserData, UserDataMethods};
|
||||||
|
|||||||
+98
-46
@@ -27,13 +27,12 @@ use crate::value::{FromLua, FromLuaMulti, MultiValue, Nil, ToLua, ToLuaMulti, Va
|
|||||||
#[cfg(feature = "async")]
|
#[cfg(feature = "async")]
|
||||||
use {
|
use {
|
||||||
crate::types::AsyncCallback,
|
crate::types::AsyncCallback,
|
||||||
futures_core::future::LocalBoxFuture,
|
futures_core::{
|
||||||
futures_task::noop_waker,
|
future::{Future, LocalBoxFuture},
|
||||||
futures_util::future::{self, FutureExt, TryFutureExt},
|
|
||||||
std::{
|
|
||||||
future::Future,
|
|
||||||
task::{Context, Poll, Waker},
|
task::{Context, Poll, Waker},
|
||||||
},
|
},
|
||||||
|
futures_task::noop_waker,
|
||||||
|
futures_util::future::{self, TryFutureExt},
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Top level Lua struct which holds the Lua state itself.
|
/// Top level Lua struct which holds the Lua state itself.
|
||||||
@@ -482,39 +481,37 @@ impl Lua {
|
|||||||
/// Wraps a Rust async function or closure, creating a callable Lua function handle to it.
|
/// Wraps a Rust async function or closure, creating a callable Lua function handle to it.
|
||||||
///
|
///
|
||||||
/// While executing the function Rust will poll Future and if the result is not ready, call
|
/// While executing the function Rust will poll Future and if the result is not ready, call
|
||||||
/// `lua_yield()` returning internal representation of a `Poll::Pending` value.
|
/// `yield()` passing internal representation of a `Poll::Pending` value.
|
||||||
///
|
///
|
||||||
/// The function must be called inside [`Thread`] coroutine to be able to suspend its execution.
|
/// The function must be called inside Lua coroutine ([`Thread`]) to be able to suspend its execution.
|
||||||
/// An executor could be used together with [`ThreadStream`] and mlua will use a provided Waker
|
/// An executor should be used to poll [`AsyncThread`] and mlua will take a provided Waker
|
||||||
/// in that case. Otherwise noop waker will be used if try to call the function outside of Rust
|
/// in that case. Otherwise noop waker will be used if try to call the function outside of Rust
|
||||||
/// executors.
|
/// executors.
|
||||||
///
|
///
|
||||||
|
/// The family of `call_async()` functions takes care about creating [`Thread`].
|
||||||
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// Non blocking sleep:
|
/// Non blocking sleep:
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// use std::time::Duration;
|
/// use std::time::Duration;
|
||||||
/// use futures_executor::block_on;
|
|
||||||
/// use futures_timer::Delay;
|
/// use futures_timer::Delay;
|
||||||
/// # use mlua::{Lua, Result, Thread};
|
/// use mlua::{Lua, Result};
|
||||||
///
|
///
|
||||||
/// async fn sleep(_lua: &Lua, n: u64) -> Result<&'static str> {
|
/// async fn sleep(_lua: &Lua, n: u64) -> Result<&'static str> {
|
||||||
/// Delay::new(Duration::from_secs(n)).await;
|
/// Delay::new(Duration::from_millis(n)).await;
|
||||||
/// Ok("done")
|
/// Ok("done")
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// # fn main() -> Result<()> {
|
/// #[tokio::main]
|
||||||
/// # let lua = Lua::new();
|
/// async fn main() -> Result<()> {
|
||||||
/// lua.globals().set("async_sleep", lua.create_async_function(sleep)?)?;
|
/// let lua = Lua::new();
|
||||||
/// let thr = lua.load("coroutine.create(function(n) return async_sleep(n) end)").eval::<Thread>()?;
|
/// lua.globals().set("sleep", lua.create_async_function(sleep)?)?;
|
||||||
/// let res: String = block_on(async {
|
/// let res: String = lua.load("return sleep(...)").call_async(100).await?; // Sleep 100ms
|
||||||
/// thr.into_async(1).await // Sleep 1 second
|
/// assert_eq!(res, "done");
|
||||||
/// })?;
|
/// Ok(())
|
||||||
///
|
/// }
|
||||||
/// assert_eq!(res, "done");
|
|
||||||
/// # Ok(())
|
|
||||||
/// # }
|
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// [`Thread`]: struct.Thread.html
|
/// [`Thread`]: struct.Thread.html
|
||||||
@@ -532,12 +529,10 @@ impl Lua {
|
|||||||
{
|
{
|
||||||
self.create_async_callback(Box::new(move |lua, args| {
|
self.create_async_callback(Box::new(move |lua, args| {
|
||||||
let args = match A::from_lua_multi(args, lua) {
|
let args = match A::from_lua_multi(args, lua) {
|
||||||
Ok(x) => x,
|
Ok(args) => args,
|
||||||
Err(e) => return future::err(e).boxed_local(),
|
Err(e) => return Box::pin(future::err(e)),
|
||||||
};
|
};
|
||||||
func(lua, args)
|
Box::pin(func(lua, args).and_then(move |ret| future::ready(ret.to_lua_multi(lua))))
|
||||||
.and_then(move |x| future::ready(x.to_lua_multi(lua)))
|
|
||||||
.boxed_local()
|
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1239,9 +1234,11 @@ impl Lua {
|
|||||||
Function(self.pop_ref())
|
Function(self.pop_ref())
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let coroutine = self.globals().get::<_, Table>("coroutine")?;
|
||||||
|
|
||||||
let env = self.create_table()?;
|
let env = self.create_table()?;
|
||||||
env.set("get_poll", get_poll)?;
|
env.set("get_poll", get_poll)?;
|
||||||
env.set("coroutine", self.globals().get::<_, Value>("coroutine")?)?;
|
env.set("yield", coroutine.get::<_, Function>("yield")?)?;
|
||||||
env.set(
|
env.set(
|
||||||
"unpack",
|
"unpack",
|
||||||
self.create_function(|_, tbl: Table| {
|
self.create_function(|_, tbl: Table| {
|
||||||
@@ -1259,7 +1256,7 @@ impl Lua {
|
|||||||
if ready then
|
if ready then
|
||||||
return unpack(res)
|
return unpack(res)
|
||||||
end
|
end
|
||||||
coroutine.yield(res)
|
yield(res)
|
||||||
end
|
end
|
||||||
"#,
|
"#,
|
||||||
)
|
)
|
||||||
@@ -1347,6 +1344,19 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Asynchronously execute this chunk of code.
|
||||||
|
///
|
||||||
|
/// See [`Chunk::exec`] for more details.
|
||||||
|
///
|
||||||
|
/// [`Chunk::exec`]: struct.Chunk.html#method.exec
|
||||||
|
#[cfg(feature = "async")]
|
||||||
|
pub fn exec_async<'fut>(self) -> LocalBoxFuture<'fut, Result<()>>
|
||||||
|
where
|
||||||
|
'lua: 'fut,
|
||||||
|
{
|
||||||
|
self.call_async(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Evaluate the chunk as either an expression or block.
|
/// Evaluate the chunk as either an expression or block.
|
||||||
///
|
///
|
||||||
/// If the chunk can be parsed as an expression, this loads and executes the chunk and returns
|
/// If the chunk can be parsed as an expression, this loads and executes the chunk and returns
|
||||||
@@ -1356,18 +1366,39 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
|
|||||||
// First, try interpreting the lua as an expression by adding
|
// First, try interpreting the lua as an expression by adding
|
||||||
// "return", then as a statement. This is the same thing the
|
// "return", then as a statement. This is the same thing the
|
||||||
// actual lua repl does.
|
// actual lua repl does.
|
||||||
let mut expression_source = b"return ".to_vec();
|
if let Ok(function) = self.lua.load_chunk(
|
||||||
expression_source.extend(self.source);
|
&self.expression_source(),
|
||||||
if let Ok(function) =
|
self.name.as_ref(),
|
||||||
self.lua
|
self.env.clone(),
|
||||||
.load_chunk(&expression_source, self.name.as_ref(), self.env.clone())
|
) {
|
||||||
{
|
|
||||||
function.call(())
|
function.call(())
|
||||||
} else {
|
} else {
|
||||||
self.call(())
|
self.call(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Asynchronously evaluate the chunk as either an expression or block.
|
||||||
|
///
|
||||||
|
/// See [`Chunk::eval`] for more details.
|
||||||
|
///
|
||||||
|
/// [`Chunk::eval`]: struct.Chunk.html#method.eval
|
||||||
|
#[cfg(feature = "async")]
|
||||||
|
pub fn eval_async<'fut, R>(self) -> LocalBoxFuture<'fut, Result<R>>
|
||||||
|
where
|
||||||
|
'lua: 'fut,
|
||||||
|
R: FromLuaMulti<'lua> + 'fut,
|
||||||
|
{
|
||||||
|
if let Ok(function) = self.lua.load_chunk(
|
||||||
|
&self.expression_source(),
|
||||||
|
self.name.as_ref(),
|
||||||
|
self.env.clone(),
|
||||||
|
) {
|
||||||
|
function.call_async(())
|
||||||
|
} else {
|
||||||
|
self.call_async(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Load the chunk function and call it with the given arguemnts.
|
/// Load the chunk function and call it with the given arguemnts.
|
||||||
///
|
///
|
||||||
/// This is equivalent to `into_function` and calling the resulting function.
|
/// This is equivalent to `into_function` and calling the resulting function.
|
||||||
@@ -1375,6 +1406,24 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
|
|||||||
self.into_function()?.call(args)
|
self.into_function()?.call(args)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Load the chunk function and asynchronously call it with the given arguemnts.
|
||||||
|
///
|
||||||
|
/// See [`Chunk::call`] for more details.
|
||||||
|
///
|
||||||
|
/// [`Chunk::call`]: struct.Chunk.html#method.call
|
||||||
|
#[cfg(feature = "async")]
|
||||||
|
pub fn call_async<'fut, A, R>(self, args: A) -> LocalBoxFuture<'fut, Result<R>>
|
||||||
|
where
|
||||||
|
'lua: 'fut,
|
||||||
|
A: ToLuaMulti<'lua>,
|
||||||
|
R: FromLuaMulti<'lua> + 'fut,
|
||||||
|
{
|
||||||
|
match self.into_function() {
|
||||||
|
Ok(func) => func.call_async(args),
|
||||||
|
Err(e) => Box::pin(future::err(e)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Load this chunk into a regular `Function`.
|
/// Load this chunk into a regular `Function`.
|
||||||
///
|
///
|
||||||
/// This simply compiles the chunk without actually executing it.
|
/// This simply compiles the chunk without actually executing it.
|
||||||
@@ -1382,6 +1431,13 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
|
|||||||
self.lua
|
self.lua
|
||||||
.load_chunk(self.source, self.name.as_ref(), self.env)
|
.load_chunk(self.source, self.name.as_ref(), self.env)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn expression_source(&self) -> Vec<u8> {
|
||||||
|
let mut buf = Vec::with_capacity(b"return ".len() + self.source.len());
|
||||||
|
buf.extend(b"return ");
|
||||||
|
buf.extend(self.source);
|
||||||
|
buf
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn load_from_std_lib(state: *mut ffi::lua_State, libs: StdLib) {
|
unsafe fn load_from_std_lib(state: *mut ffi::lua_State, libs: StdLib) {
|
||||||
@@ -1685,7 +1741,7 @@ impl<'lua, T: 'static + UserData> StaticUserDataMethods<'lua, T> {
|
|||||||
MR: 'static + Future<Output = Result<R>>,
|
MR: 'static + Future<Output = Result<R>>,
|
||||||
{
|
{
|
||||||
Box::new(move |lua, mut args| {
|
Box::new(move |lua, mut args| {
|
||||||
let fut = || {
|
let fut_res = || {
|
||||||
if let Some(front) = args.pop_front() {
|
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>()?.clone();
|
let userdata = userdata.borrow::<T>()?.clone();
|
||||||
@@ -1698,11 +1754,9 @@ impl<'lua, T: 'static + UserData> StaticUserDataMethods<'lua, T> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
match fut() {
|
match fut_res() {
|
||||||
Ok(f) => f
|
Ok(fut) => Box::pin(fut.and_then(move |ret| future::ready(ret.to_lua_multi(lua)))),
|
||||||
.and_then(move |fr| future::ready(fr.to_lua_multi(lua)))
|
Err(e) => Box::pin(future::err(e)),
|
||||||
.boxed_local(),
|
|
||||||
Err(e) => future::err(e).boxed_local(),
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -1741,12 +1795,10 @@ impl<'lua, T: 'static + UserData> StaticUserDataMethods<'lua, T> {
|
|||||||
{
|
{
|
||||||
Box::new(move |lua, args| {
|
Box::new(move |lua, args| {
|
||||||
let args = match A::from_lua_multi(args, lua) {
|
let args = match A::from_lua_multi(args, lua) {
|
||||||
Ok(x) => x,
|
Ok(args) => args,
|
||||||
Err(e) => return future::err(e).boxed_local(),
|
Err(e) => return Box::pin(future::err(e)),
|
||||||
};
|
};
|
||||||
function(lua, args)
|
Box::pin(function(lua, args).and_then(move |ret| future::ready(ret.to_lua_multi(lua))))
|
||||||
.and_then(move |x| future::ready(x.to_lua_multi(lua)))
|
|
||||||
.boxed_local()
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-3
@@ -6,9 +6,9 @@ pub use crate::{
|
|||||||
Function as LuaFunction, Integer as LuaInteger, LightUserData as LuaLightUserData, Lua,
|
Function as LuaFunction, Integer as LuaInteger, LightUserData as LuaLightUserData, Lua,
|
||||||
MetaMethod as LuaMetaMethod, MultiValue as LuaMultiValue, Nil as LuaNil, Number as LuaNumber,
|
MetaMethod as LuaMetaMethod, MultiValue as LuaMultiValue, Nil as LuaNil, Number as LuaNumber,
|
||||||
RegistryKey as LuaRegistryKey, Result as LuaResult, String as LuaString, Table as LuaTable,
|
RegistryKey as LuaRegistryKey, Result as LuaResult, String as LuaString, Table as LuaTable,
|
||||||
TablePairs as LuaTablePairs, TableSequence as LuaTableSequence, Thread as LuaThread,
|
TableExt as LuaTableExt, TablePairs as LuaTablePairs, TableSequence as LuaTableSequence,
|
||||||
ThreadStatus as LuaThreadStatus, ToLua, ToLuaMulti, UserData as LuaUserData,
|
Thread as LuaThread, ThreadStatus as LuaThreadStatus, ToLua, ToLuaMulti,
|
||||||
UserDataMethods as LuaUserDataMethods, Value as LuaValue,
|
UserData as LuaUserData, UserDataMethods as LuaUserDataMethods, Value as LuaValue,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(feature = "async")]
|
#[cfg(feature = "async")]
|
||||||
|
|||||||
+124
-21
@@ -8,6 +8,9 @@ use crate::types::{Integer, LuaRef};
|
|||||||
use crate::util::{assert_stack, protect_lua, protect_lua_closure, StackGuard};
|
use crate::util::{assert_stack, protect_lua, protect_lua_closure, StackGuard};
|
||||||
use crate::value::{FromLua, FromLuaMulti, Nil, ToLua, ToLuaMulti, Value};
|
use crate::value::{FromLua, FromLuaMulti, Nil, ToLua, ToLuaMulti, Value};
|
||||||
|
|
||||||
|
#[cfg(feature = "async")]
|
||||||
|
use {futures_core::future::LocalBoxFuture, futures_util::future};
|
||||||
|
|
||||||
/// Handle to an internal Lua table.
|
/// Handle to an internal Lua table.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Table<'lua>(pub(crate) LuaRef<'lua>);
|
pub struct Table<'lua>(pub(crate) LuaRef<'lua>);
|
||||||
@@ -134,26 +137,15 @@ impl<'lua> Table<'lua> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the function associated to `key` from the table and executes it,
|
/// Gets the function associated to `key` from the table and executes it,
|
||||||
/// passing the table itself as the first argument.
|
/// passing the table itself along with `args` as function arguments.
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// This function is deprecated since 0.3.1 in favor of [`call_method`]
|
||||||
///
|
/// in the `TableExt` trait.
|
||||||
/// Execute the table method with name "concat":
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// # use mlua::{Lua, Result, Table};
|
|
||||||
/// # fn main() -> Result<()> {
|
|
||||||
/// # let lua = Lua::new();
|
|
||||||
/// # let object = lua.create_table()?;
|
|
||||||
/// # let concat = lua.create_function(|_, (_, a, b): (Table, String, String)| Ok(a + &b))?;
|
|
||||||
/// # object.set("concat", concat)?;
|
|
||||||
/// // simiar to: object:concat("param1", "param2")
|
|
||||||
/// object.call("concat", ("param1", "param2"))?;
|
|
||||||
/// # Ok(())
|
|
||||||
/// # }
|
|
||||||
/// ```
|
|
||||||
///
|
///
|
||||||
/// This might invoke the `__index` metamethod.
|
/// This might invoke the `__index` metamethod.
|
||||||
|
///
|
||||||
|
/// [`call_method`]: trait.TableExt.html#tymethod.call_method
|
||||||
|
#[deprecated(since = "0.3.1", note = "Please use `call_method` instead")]
|
||||||
pub fn call<K, A, R>(&self, key: K, args: A) -> Result<R>
|
pub fn call<K, A, R>(&self, key: K, args: A) -> Result<R>
|
||||||
where
|
where
|
||||||
K: ToLua<'lua>,
|
K: ToLua<'lua>,
|
||||||
@@ -262,7 +254,7 @@ impl<'lua> Table<'lua> {
|
|||||||
V::from_lua(value, lua)
|
V::from_lua(value, lua)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Inserts element value at position idx to the table, shifting up the elements from table[idx].
|
/// Inserts element value at position `idx` to the table, shifting up the elements from `table[idx]`.
|
||||||
/// The worst case complexity is O(n), where n is the table length.
|
/// The worst case complexity is O(n), where n is the table length.
|
||||||
pub fn raw_insert<V: ToLua<'lua>>(&self, idx: Integer, value: V) -> Result<()> {
|
pub fn raw_insert<V: ToLua<'lua>>(&self, idx: Integer, value: V) -> Result<()> {
|
||||||
let lua = self.0.lua;
|
let lua = self.0.lua;
|
||||||
@@ -292,11 +284,11 @@ impl<'lua> Table<'lua> {
|
|||||||
|
|
||||||
/// Removes a key from the table.
|
/// Removes a key from the table.
|
||||||
///
|
///
|
||||||
/// If `key` is an integer, mlua shifts down the elements from table[key+1],
|
/// If `key` is an integer, mlua shifts down the elements from `table[key+1]`,
|
||||||
/// and erases element table[key]. The complexity is O(n) in worst case,
|
/// and erases element `table[key]`. The complexity is O(n) in worst case,
|
||||||
/// where n is the table length.
|
/// where n is the table length.
|
||||||
///
|
///
|
||||||
/// For othey key types this is equivalent to setting table[key] = nil.
|
/// For othey key types this is equivalent to setting `table[key] = nil`.
|
||||||
pub fn raw_remove<K: ToLua<'lua>>(&self, key: K) -> Result<()> {
|
pub fn raw_remove<K: ToLua<'lua>>(&self, key: K) -> Result<()> {
|
||||||
let lua = self.0.lua;
|
let lua = self.0.lua;
|
||||||
let key = key.to_lua(lua)?;
|
let key = key.to_lua(lua)?;
|
||||||
@@ -494,6 +486,117 @@ impl<'lua> AsRef<Table<'lua>> for Table<'lua> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// An extension trait for `Table`s that provides a variety of convenient functionality.
|
||||||
|
pub trait TableExt<'lua> {
|
||||||
|
/// Gets the function associated to `key` from the table and executes it,
|
||||||
|
/// passing the table itself along with `args` as function arguments.
|
||||||
|
///
|
||||||
|
/// This is a shortcut for
|
||||||
|
/// `table.get::<_, Function>(key)?.call((table.clone(), arg1, ..., argN))`
|
||||||
|
///
|
||||||
|
/// This might invoke the `__index` metamethod.
|
||||||
|
fn call_method<K, A, R>(&self, key: K, args: A) -> Result<R>
|
||||||
|
where
|
||||||
|
K: ToLua<'lua>,
|
||||||
|
A: ToLuaMulti<'lua>,
|
||||||
|
R: FromLuaMulti<'lua>;
|
||||||
|
|
||||||
|
/// Gets the function associated to `key` from the table and executes it,
|
||||||
|
/// passing `args` as function arguments.
|
||||||
|
///
|
||||||
|
/// This is a shortcut for
|
||||||
|
/// `table.get::<_, Function>(key)?.call(args)`
|
||||||
|
///
|
||||||
|
/// This might invoke the `__index` metamethod.
|
||||||
|
fn call_function<K, A, R>(&self, key: K, args: A) -> Result<R>
|
||||||
|
where
|
||||||
|
K: ToLua<'lua>,
|
||||||
|
A: ToLuaMulti<'lua>,
|
||||||
|
R: FromLuaMulti<'lua>;
|
||||||
|
|
||||||
|
/// Gets the function associated to `key` from the table and asynchronously executes it,
|
||||||
|
/// passing the table itself along with `args` as function arguments and returning Future.
|
||||||
|
///
|
||||||
|
/// This might invoke the `__index` metamethod.
|
||||||
|
#[cfg(feature = "async")]
|
||||||
|
fn call_async_method<'fut, K, A, R>(&self, key: K, args: A) -> LocalBoxFuture<'fut, Result<R>>
|
||||||
|
where
|
||||||
|
'lua: 'fut,
|
||||||
|
K: ToLua<'lua>,
|
||||||
|
A: ToLuaMulti<'lua>,
|
||||||
|
R: FromLuaMulti<'lua> + 'fut;
|
||||||
|
|
||||||
|
/// Gets the function associated to `key` from the table and asynchronously executes it,
|
||||||
|
/// passing `args` as function arguments and returning Future.
|
||||||
|
///
|
||||||
|
/// This might invoke the `__index` metamethod.
|
||||||
|
#[cfg(feature = "async")]
|
||||||
|
fn call_async_function<'fut, K, A, R>(
|
||||||
|
&self,
|
||||||
|
key: K,
|
||||||
|
args: A,
|
||||||
|
) -> LocalBoxFuture<'fut, Result<R>>
|
||||||
|
where
|
||||||
|
'lua: 'fut,
|
||||||
|
K: ToLua<'lua>,
|
||||||
|
A: ToLuaMulti<'lua>,
|
||||||
|
R: FromLuaMulti<'lua> + 'fut;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'lua> TableExt<'lua> for Table<'lua> {
|
||||||
|
fn call_method<K, A, R>(&self, key: K, args: A) -> Result<R>
|
||||||
|
where
|
||||||
|
K: ToLua<'lua>,
|
||||||
|
A: ToLuaMulti<'lua>,
|
||||||
|
R: FromLuaMulti<'lua>,
|
||||||
|
{
|
||||||
|
let lua = self.0.lua;
|
||||||
|
let mut args = args.to_lua_multi(lua)?;
|
||||||
|
args.push_front(Value::Table(self.clone()));
|
||||||
|
self.get::<_, Function>(key)?.call(args)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn call_function<K, A, R>(&self, key: K, args: A) -> Result<R>
|
||||||
|
where
|
||||||
|
K: ToLua<'lua>,
|
||||||
|
A: ToLuaMulti<'lua>,
|
||||||
|
R: FromLuaMulti<'lua>,
|
||||||
|
{
|
||||||
|
self.get::<_, Function>(key)?.call(args)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "async")]
|
||||||
|
fn call_async_method<'fut, K, A, R>(&self, key: K, args: A) -> LocalBoxFuture<'fut, Result<R>>
|
||||||
|
where
|
||||||
|
'lua: 'fut,
|
||||||
|
K: ToLua<'lua>,
|
||||||
|
A: ToLuaMulti<'lua>,
|
||||||
|
R: FromLuaMulti<'lua> + 'fut,
|
||||||
|
{
|
||||||
|
let lua = self.0.lua;
|
||||||
|
let mut args = match args.to_lua_multi(lua) {
|
||||||
|
Ok(args) => args,
|
||||||
|
Err(e) => return Box::pin(future::err(e)),
|
||||||
|
};
|
||||||
|
args.push_front(Value::Table(self.clone()));
|
||||||
|
self.call_async_function(key, args)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "async")]
|
||||||
|
fn call_async_function<'fut, K, A, R>(&self, key: K, args: A) -> LocalBoxFuture<'fut, Result<R>>
|
||||||
|
where
|
||||||
|
'lua: 'fut,
|
||||||
|
K: ToLua<'lua>,
|
||||||
|
A: ToLuaMulti<'lua>,
|
||||||
|
R: FromLuaMulti<'lua> + 'fut,
|
||||||
|
{
|
||||||
|
match self.get::<_, Function>(key) {
|
||||||
|
Ok(func) => func.call_async(args),
|
||||||
|
Err(e) => Box::pin(future::err(e)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// An iterator over the pairs of a Lua table.
|
/// An iterator over the pairs of a Lua table.
|
||||||
///
|
///
|
||||||
/// This struct is created by the [`Table::pairs`] method.
|
/// This struct is created by the [`Table::pairs`] method.
|
||||||
|
|||||||
+15
-15
@@ -45,7 +45,10 @@ pub enum ThreadStatus {
|
|||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Thread<'lua>(pub(crate) LuaRef<'lua>);
|
pub struct Thread<'lua>(pub(crate) LuaRef<'lua>);
|
||||||
|
|
||||||
/// Thread (coroutine) representation as an async Future or Stream.
|
/// Thread (coroutine) representation as an async [`Future`] or [`Stream`].
|
||||||
|
///
|
||||||
|
/// [`Future`]: ../futures_core/future/trait.Future.html
|
||||||
|
/// [`Stream`]: ../futures_core/stream/trait.Stream.html
|
||||||
#[cfg(feature = "async")]
|
#[cfg(feature = "async")]
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct AsyncThread<'lua, R> {
|
pub struct AsyncThread<'lua, R> {
|
||||||
@@ -183,13 +186,13 @@ impl<'lua> Thread<'lua> {
|
|||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// # use mlua::{Error, Lua, Result, Thread};
|
/// # use mlua::{Lua, Result, Thread};
|
||||||
/// use futures_executor::block_on;
|
/// use futures::stream::TryStreamExt;
|
||||||
/// use futures_util::stream::TryStreamExt;
|
/// # #[tokio::main]
|
||||||
/// # fn main() -> Result<()> {
|
/// # async fn main() -> Result<()> {
|
||||||
/// # let lua = Lua::new();
|
/// # let lua = Lua::new();
|
||||||
/// let thread: Thread = lua.load(r#"
|
/// let thread: Thread = lua.load(r#"
|
||||||
/// coroutine.create(function(sum)
|
/// coroutine.create(function (sum)
|
||||||
/// for i = 1,10 do
|
/// for i = 1,10 do
|
||||||
/// sum = sum + i
|
/// sum = sum + i
|
||||||
/// coroutine.yield(sum)
|
/// coroutine.yield(sum)
|
||||||
@@ -198,16 +201,13 @@ impl<'lua> Thread<'lua> {
|
|||||||
/// end)
|
/// end)
|
||||||
/// "#).eval()?;
|
/// "#).eval()?;
|
||||||
///
|
///
|
||||||
/// let result = block_on(async {
|
/// let mut stream = thread.into_async::<_, i64>(1);
|
||||||
/// let mut s = thread.into_async::<_, i64>(1);
|
/// let mut sum = 0;
|
||||||
/// let mut sum = 0;
|
/// while let Some(n) = stream.try_next().await? {
|
||||||
/// while let Some(n) = s.try_next().await? {
|
/// sum += n;
|
||||||
/// sum += n;
|
/// }
|
||||||
/// }
|
|
||||||
/// Ok::<_, Error>(sum)
|
|
||||||
/// })?;
|
|
||||||
///
|
///
|
||||||
/// assert_eq!(result, 286);
|
/// assert_eq!(sum, 286);
|
||||||
///
|
///
|
||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
|
|||||||
+20
-8
@@ -1,18 +1,19 @@
|
|||||||
use std::any::{Any, TypeId};
|
use std::any::{Any, TypeId};
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::cell::RefCell;
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
use std::os::raw::{c_char, c_int, c_void};
|
use std::os::raw::{c_char, c_int, c_void};
|
||||||
use std::panic::{catch_unwind, resume_unwind, AssertUnwindSafe};
|
use std::panic::{catch_unwind, resume_unwind, AssertUnwindSafe};
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
use std::sync::Mutex;
|
||||||
use std::{mem, ptr, slice};
|
use std::{mem, ptr, slice};
|
||||||
|
|
||||||
use crate::error::{Error, Result};
|
use crate::error::{Error, Result};
|
||||||
use crate::ffi;
|
use crate::ffi;
|
||||||
|
|
||||||
thread_local! {
|
lazy_static::lazy_static! {
|
||||||
static METATABLE_CACHE: RefCell<HashMap<TypeId, c_int>> = RefCell::new(HashMap::new());
|
// The capacity must(!) be greater than number of stored keys
|
||||||
|
static ref METATABLE_CACHE: Mutex<HashMap<TypeId, u8>> = Mutex::new(HashMap::with_capacity(32));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Checks that Lua has enough free stack space for future stack operations. On failure, this will
|
// Checks that Lua has enough free stack space for future stack operations. On failure, this will
|
||||||
@@ -519,6 +520,16 @@ pub unsafe fn init_gc_metatable_for<T: Any>(
|
|||||||
) {
|
) {
|
||||||
let type_id = TypeId::of::<T>();
|
let type_id = TypeId::of::<T>();
|
||||||
|
|
||||||
|
let ref_addr = {
|
||||||
|
let mut mt_cache = mlua_expect!(METATABLE_CACHE.lock(), "cannot lock metatable cache");
|
||||||
|
mlua_assert!(
|
||||||
|
mt_cache.capacity() - mt_cache.len() > 0,
|
||||||
|
"out of metatable cache capacity"
|
||||||
|
);
|
||||||
|
mt_cache.insert(type_id, 0);
|
||||||
|
&mt_cache[&type_id] as *const u8
|
||||||
|
};
|
||||||
|
|
||||||
ffi::lua_newtable(state);
|
ffi::lua_newtable(state);
|
||||||
|
|
||||||
ffi::lua_pushstring(state, cstr!("__gc"));
|
ffi::lua_pushstring(state, cstr!("__gc"));
|
||||||
@@ -533,15 +544,16 @@ pub unsafe fn init_gc_metatable_for<T: Any>(
|
|||||||
f(state)
|
f(state)
|
||||||
}
|
}
|
||||||
|
|
||||||
let ref_addr = ffi::luaL_ref(state, ffi::LUA_REGISTRYINDEX);
|
ffi::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, ref_addr as *mut c_void);
|
||||||
METATABLE_CACHE.with(|mc| mc.borrow_mut().insert(type_id, ref_addr));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub unsafe fn get_gc_metatable_for<T: Any>(state: *mut ffi::lua_State) {
|
pub unsafe fn get_gc_metatable_for<T: Any>(state: *mut ffi::lua_State) {
|
||||||
let type_id = TypeId::of::<T>();
|
let type_id = TypeId::of::<T>();
|
||||||
let ref_addr = METATABLE_CACHE
|
let ref_addr = {
|
||||||
.with(|mc| *mlua_expect!(mc.borrow().get(&type_id), "gc metatable does not exist"));
|
let mt_cache = mlua_expect!(METATABLE_CACHE.lock(), "cannot lock metatable cache");
|
||||||
ffi::lua_rawgeti(state, ffi::LUA_REGISTRYINDEX, ref_addr as ffi::lua_Integer);
|
mlua_expect!(mt_cache.get(&type_id), "gc metatable does not exist") as *const u8
|
||||||
|
};
|
||||||
|
ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, ref_addr as *mut c_void);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize the error, panic, and destructed userdata metatables.
|
// Initialize the error, panic, and destructed userdata metatables.
|
||||||
|
|||||||
+295
@@ -0,0 +1,295 @@
|
|||||||
|
#![cfg(feature = "async")]
|
||||||
|
|
||||||
|
use std::cell::Cell;
|
||||||
|
use std::rc::Rc;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use futures_timer::Delay;
|
||||||
|
use futures_util::stream::TryStreamExt;
|
||||||
|
|
||||||
|
use mlua::{Error, Function, Lua, Result, Table, TableExt, UserData, UserDataMethods};
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_async_function() -> Result<()> {
|
||||||
|
let lua = Lua::new();
|
||||||
|
|
||||||
|
let f = lua
|
||||||
|
.create_async_function(|_lua, (a, b, c): (i64, i64, i64)| async move { Ok((a + b) * c) })?;
|
||||||
|
lua.globals().set("f", f)?;
|
||||||
|
|
||||||
|
let res: i64 = lua.load("f(1, 2, 3)").eval_async().await?;
|
||||||
|
assert_eq!(res, 9);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_async_sleep() -> Result<()> {
|
||||||
|
let lua = Lua::new();
|
||||||
|
|
||||||
|
let sleep = lua.create_async_function(move |_lua, n: u64| async move {
|
||||||
|
Delay::new(Duration::from_millis(n)).await;
|
||||||
|
Ok(format!("elapsed:{}ms", n))
|
||||||
|
})?;
|
||||||
|
lua.globals().set("sleep", sleep)?;
|
||||||
|
|
||||||
|
let res: String = lua.load(r"return sleep(...)").call_async(100).await?;
|
||||||
|
assert_eq!(res, "elapsed:100ms");
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_async_call() -> Result<()> {
|
||||||
|
let lua = Lua::new();
|
||||||
|
|
||||||
|
let hello = lua.create_async_function(|_lua, name: String| async move {
|
||||||
|
Delay::new(Duration::from_millis(10)).await;
|
||||||
|
Ok(format!("hello, {}!", name))
|
||||||
|
})?;
|
||||||
|
|
||||||
|
match hello.call::<_, ()>("alex") {
|
||||||
|
Err(Error::RuntimeError(_)) => {}
|
||||||
|
_ => panic!(
|
||||||
|
"non-async executing async function must fail on the yield stage with RuntimeError"
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(hello.call_async::<_, String>("alex").await?, "hello, alex!");
|
||||||
|
|
||||||
|
// Executing non-async functions using async call is allowed
|
||||||
|
let sum = lua.create_function(|_lua, (a, b): (i64, i64)| return Ok(a + b))?;
|
||||||
|
assert_eq!(sum.call_async::<_, i64>((5, 1)).await?, 6);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_async_bind_call() -> Result<()> {
|
||||||
|
let lua = Lua::new();
|
||||||
|
|
||||||
|
let sum = lua.create_async_function(|_lua, (a, b): (i64, i64)| async move { Ok(a + b) })?;
|
||||||
|
|
||||||
|
let plus_10 = sum.bind(10)?;
|
||||||
|
lua.globals().set("plus_10", plus_10)?;
|
||||||
|
|
||||||
|
assert_eq!(lua.load("plus_10(-1)").eval_async::<i64>().await?, 9);
|
||||||
|
assert_eq!(lua.load("plus_10(1)").eval_async::<i64>().await?, 11);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_async_handle_yield() -> Result<()> {
|
||||||
|
let lua = Lua::new();
|
||||||
|
|
||||||
|
let sum = lua.create_async_function(|_lua, (a, b): (i64, i64)| async move {
|
||||||
|
Delay::new(Duration::from_millis(10)).await;
|
||||||
|
Ok(a + b)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
lua.globals().set("sleep_sum", sum)?;
|
||||||
|
|
||||||
|
let res: String = lua
|
||||||
|
.load(
|
||||||
|
r#"
|
||||||
|
sum = sleep_sum(6, 7)
|
||||||
|
assert(sum == 13)
|
||||||
|
coroutine.yield("in progress")
|
||||||
|
return "done"
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.call_async(())
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
assert_eq!(res, "done");
|
||||||
|
|
||||||
|
let min = lua
|
||||||
|
.load(
|
||||||
|
r#"
|
||||||
|
function (a, b)
|
||||||
|
coroutine.yield("ignore me")
|
||||||
|
if a < b then return a else return b end
|
||||||
|
end
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.eval::<Function>()?;
|
||||||
|
assert_eq!(min.call_async::<_, i64>((-1, 1)).await?, -1);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_async_return_async_closure() -> Result<()> {
|
||||||
|
let lua = Lua::new();
|
||||||
|
|
||||||
|
let f = lua.create_async_function(|lua, a: i64| async move {
|
||||||
|
Delay::new(Duration::from_millis(10)).await;
|
||||||
|
|
||||||
|
let g = lua.create_async_function(move |_, b: i64| async move {
|
||||||
|
Delay::new(Duration::from_millis(10)).await;
|
||||||
|
return Ok(a + b);
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(g)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
lua.globals().set("f", f)?;
|
||||||
|
|
||||||
|
let res: i64 = lua
|
||||||
|
.load("local g = f(1); return g(2) + g(3)")
|
||||||
|
.call_async(())
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
assert_eq!(res, 7);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_async_thread_stream() -> Result<()> {
|
||||||
|
let lua = Lua::new();
|
||||||
|
|
||||||
|
let thread = lua.create_thread(
|
||||||
|
lua.load(
|
||||||
|
r#"
|
||||||
|
function (sum)
|
||||||
|
for i = 1,10 do
|
||||||
|
sum = sum + i
|
||||||
|
coroutine.yield(sum)
|
||||||
|
end
|
||||||
|
return sum
|
||||||
|
end
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.eval()?,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let mut stream = thread.into_async::<_, i64>(1);
|
||||||
|
let mut sum = 0;
|
||||||
|
while let Some(n) = stream.try_next().await? {
|
||||||
|
sum += n;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_eq!(sum, 286);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_async_thread() -> Result<()> {
|
||||||
|
let lua = Lua::new();
|
||||||
|
|
||||||
|
let cnt = Rc::new(10); // sleep 10ms
|
||||||
|
let cnt2 = cnt.clone();
|
||||||
|
let f = lua.create_async_function(move |_lua, ()| {
|
||||||
|
let cnt3 = cnt2.clone();
|
||||||
|
async move {
|
||||||
|
Delay::new(Duration::from_millis(*cnt3.as_ref())).await;
|
||||||
|
Ok("done")
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let res: String = lua.create_thread(f)?.into_async(()).await?;
|
||||||
|
|
||||||
|
assert_eq!(res, "done");
|
||||||
|
|
||||||
|
assert_eq!(Rc::strong_count(&cnt), 2);
|
||||||
|
lua.gc_collect()?; // thread_s is non-resumable and subject to garbage collection
|
||||||
|
assert_eq!(Rc::strong_count(&cnt), 1);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_async_table() -> Result<()> {
|
||||||
|
let lua = Lua::new();
|
||||||
|
|
||||||
|
let table = lua.create_table()?;
|
||||||
|
table.set("val", 10)?;
|
||||||
|
|
||||||
|
let get_value = lua.create_async_function(|_, table: Table| async move {
|
||||||
|
Delay::new(Duration::from_millis(10)).await;
|
||||||
|
table.get::<_, i64>("val")
|
||||||
|
})?;
|
||||||
|
table.set("get_value", get_value)?;
|
||||||
|
|
||||||
|
let set_value = lua.create_async_function(|_, (table, n): (Table, i64)| async move {
|
||||||
|
Delay::new(Duration::from_millis(10)).await;
|
||||||
|
table.set("val", n)
|
||||||
|
})?;
|
||||||
|
table.set("set_value", set_value)?;
|
||||||
|
|
||||||
|
let sleep = lua.create_async_function(|_, n| async move {
|
||||||
|
Delay::new(Duration::from_millis(n)).await;
|
||||||
|
Ok(format!("elapsed:{}ms", n))
|
||||||
|
})?;
|
||||||
|
table.set("sleep", sleep)?;
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
table
|
||||||
|
.call_async_method::<_, _, i64>("get_value", ())
|
||||||
|
.await?,
|
||||||
|
10
|
||||||
|
);
|
||||||
|
table.call_async_method("set_value", 15).await?;
|
||||||
|
assert_eq!(
|
||||||
|
table
|
||||||
|
.call_async_method::<_, _, i64>("get_value", ())
|
||||||
|
.await?,
|
||||||
|
15
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
table
|
||||||
|
.call_async_function::<_, _, String>("sleep", 7)
|
||||||
|
.await?,
|
||||||
|
"elapsed:7ms"
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_async_userdata() -> Result<()> {
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct MyUserData(Rc<Cell<i64>>);
|
||||||
|
|
||||||
|
impl UserData for MyUserData {
|
||||||
|
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||||
|
methods.add_async_method("get_value", |_, data, ()| async move {
|
||||||
|
Delay::new(Duration::from_millis(10)).await;
|
||||||
|
Ok(data.0.get())
|
||||||
|
});
|
||||||
|
|
||||||
|
methods.add_async_method("set_value", |_, data, n| async move {
|
||||||
|
Delay::new(Duration::from_millis(10)).await;
|
||||||
|
data.0.set(n);
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
|
||||||
|
methods.add_async_function("sleep", |_, n| async move {
|
||||||
|
Delay::new(Duration::from_millis(n)).await;
|
||||||
|
Ok(format!("elapsed:{}ms", n))
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let lua = Lua::new();
|
||||||
|
let globals = lua.globals();
|
||||||
|
|
||||||
|
let userdata = lua.create_userdata(MyUserData(Rc::new(Cell::new(11))))?;
|
||||||
|
globals.set("userdata", userdata.clone())?;
|
||||||
|
|
||||||
|
lua.load(
|
||||||
|
r#"
|
||||||
|
assert(userdata:get_value() == 11)
|
||||||
|
userdata:set_value(12)
|
||||||
|
assert(userdata:get_value() == 12)
|
||||||
|
assert(userdata.sleep(5) == "elapsed:5ms")
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.exec_async()
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
+1
-38
@@ -1,10 +1,4 @@
|
|||||||
#![allow(unused_imports)]
|
use mlua::{Function, Lua, Result, String};
|
||||||
|
|
||||||
use std::{string::String as StdString, time::Duration};
|
|
||||||
|
|
||||||
use futures_executor::block_on;
|
|
||||||
|
|
||||||
use mlua::{Error, Function, Lua, Result, String, Thread};
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_function() -> Result<()> {
|
fn test_function() -> Result<()> {
|
||||||
@@ -81,34 +75,3 @@ fn test_rust_function() -> Result<()> {
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "async")]
|
|
||||||
#[tokio::test]
|
|
||||||
async fn test_async_function() -> Result<()> {
|
|
||||||
let lua = Lua::new();
|
|
||||||
|
|
||||||
let f = lua.create_async_function(move |_lua, n: u64| async move {
|
|
||||||
futures_timer::Delay::new(Duration::from_secs(n)).await;
|
|
||||||
Ok("hello")
|
|
||||||
})?;
|
|
||||||
lua.globals().set("rust_async_sleep", f)?;
|
|
||||||
|
|
||||||
let thread = lua
|
|
||||||
.load(
|
|
||||||
r#"
|
|
||||||
coroutine.create(function ()
|
|
||||||
ret = rust_async_sleep(1)
|
|
||||||
assert(ret == "hello")
|
|
||||||
coroutine.yield()
|
|
||||||
return "world"
|
|
||||||
end)
|
|
||||||
"#,
|
|
||||||
)
|
|
||||||
.eval::<Thread>()?;
|
|
||||||
|
|
||||||
let fut = thread.into_async(());
|
|
||||||
let ret: StdString = fut.await?;
|
|
||||||
assert_eq!(ret, "world");
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|||||||
+72
-1
@@ -1,4 +1,4 @@
|
|||||||
use mlua::{Lua, Nil, Result, Table, Value};
|
use mlua::{Lua, Nil, Result, Table, TableExt, Value};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_set_get() -> Result<()> {
|
fn test_set_get() -> Result<()> {
|
||||||
@@ -109,6 +109,47 @@ fn test_table() -> Result<()> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_table_sequence_from() -> Result<()> {
|
||||||
|
let lua = Lua::new();
|
||||||
|
|
||||||
|
let get_table = lua.create_function(|_, t: Table| Ok(t))?;
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
get_table
|
||||||
|
.call::<_, Table>(vec![1, 2, 3])?
|
||||||
|
.sequence_values()
|
||||||
|
.collect::<Result<Vec<i64>>>()?,
|
||||||
|
vec![1, 2, 3]
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
get_table
|
||||||
|
.call::<_, Table>([1, 2, 3].as_ref())?
|
||||||
|
.sequence_values()
|
||||||
|
.collect::<Result<Vec<i64>>>()?,
|
||||||
|
vec![1, 2, 3]
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
get_table
|
||||||
|
.call::<_, Table>([1, 2, 3])?
|
||||||
|
.sequence_values()
|
||||||
|
.collect::<Result<Vec<i64>>>()?,
|
||||||
|
vec![1, 2, 3]
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
get_table
|
||||||
|
.call::<_, Table>(&[1, 2, 3])?
|
||||||
|
.sequence_values()
|
||||||
|
.collect::<Result<Vec<i64>>>()?,
|
||||||
|
vec![1, 2, 3]
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_table_scope() -> Result<()> {
|
fn test_table_scope() -> Result<()> {
|
||||||
let lua = Lua::new();
|
let lua = Lua::new();
|
||||||
@@ -226,3 +267,33 @@ fn test_table_error() -> Result<()> {
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_table_call() -> Result<()> {
|
||||||
|
let lua = Lua::new();
|
||||||
|
|
||||||
|
lua.load(
|
||||||
|
r#"
|
||||||
|
table = {a = 1}
|
||||||
|
|
||||||
|
function table.func(key)
|
||||||
|
return "func_"..key
|
||||||
|
end
|
||||||
|
|
||||||
|
function table:method(key)
|
||||||
|
return "method_"..self[key]
|
||||||
|
end
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.exec()?;
|
||||||
|
|
||||||
|
let table: Table = lua.globals().get("table")?;
|
||||||
|
|
||||||
|
assert_eq!(table.call_function::<_, _, String>("func", "a")?, "func_a");
|
||||||
|
assert_eq!(
|
||||||
|
table.call_method::<_, _, String>("method", "a")?,
|
||||||
|
"method_1"
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,11 +1,4 @@
|
|||||||
#![allow(unused_imports)]
|
|
||||||
|
|
||||||
use std::panic::catch_unwind;
|
use std::panic::catch_unwind;
|
||||||
use std::rc::Rc;
|
|
||||||
use std::time::Duration;
|
|
||||||
|
|
||||||
use futures_executor::block_on;
|
|
||||||
use futures_util::stream::TryStreamExt;
|
|
||||||
|
|
||||||
use mlua::{Error, Function, Lua, Result, Thread, ThreadStatus};
|
use mlua::{Error, Function, Lua, Result, Thread, ThreadStatus};
|
||||||
|
|
||||||
@@ -100,38 +93,6 @@ fn test_thread() -> Result<()> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "async")]
|
|
||||||
#[tokio::test]
|
|
||||||
async fn test_thread_stream() -> Result<()> {
|
|
||||||
let lua = Lua::new();
|
|
||||||
|
|
||||||
let thread = lua.create_thread(
|
|
||||||
lua.load(
|
|
||||||
r#"
|
|
||||||
function (s)
|
|
||||||
local sum = s
|
|
||||||
for i = 1,10 do
|
|
||||||
sum = sum + i
|
|
||||||
coroutine.yield(sum)
|
|
||||||
end
|
|
||||||
return sum
|
|
||||||
end
|
|
||||||
"#,
|
|
||||||
)
|
|
||||||
.eval()?,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let mut s = thread.into_async::<_, i64>(0);
|
|
||||||
let mut sum = 0;
|
|
||||||
while let Some(n) = s.try_next().await? {
|
|
||||||
sum += n;
|
|
||||||
}
|
|
||||||
|
|
||||||
assert_eq!(sum, 275);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn coroutine_from_closure() -> Result<()> {
|
fn coroutine_from_closure() -> Result<()> {
|
||||||
let lua = Lua::new();
|
let lua = Lua::new();
|
||||||
@@ -167,30 +128,3 @@ fn coroutine_panic() {
|
|||||||
Err(p) => assert!(*p.downcast::<&str>().unwrap() == "test_panic"),
|
Err(p) => assert!(*p.downcast::<&str>().unwrap() == "test_panic"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "async")]
|
|
||||||
#[tokio::test]
|
|
||||||
async fn test_thread_async() -> Result<()> {
|
|
||||||
let lua = Lua::new();
|
|
||||||
|
|
||||||
let cnt = Rc::new(1); // sleep 1 second
|
|
||||||
let cnt2 = cnt.clone();
|
|
||||||
let f = lua.create_async_function(move |_lua, ()| {
|
|
||||||
let cnt3 = cnt2.clone();
|
|
||||||
async move {
|
|
||||||
futures_timer::Delay::new(Duration::from_secs(*cnt3.as_ref())).await;
|
|
||||||
Ok("hello")
|
|
||||||
}
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let mut thread_s = lua.create_thread(f)?.into_async(());
|
|
||||||
let val: String = thread_s.try_next().await?.unwrap_or_default();
|
|
||||||
|
|
||||||
// thread_s is non-resumable and subject to garbage collection
|
|
||||||
|
|
||||||
lua.gc_collect()?;
|
|
||||||
assert_eq!(Rc::strong_count(&cnt), 1);
|
|
||||||
assert_eq!(val, "hello");
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user