mirror of
https://github.com/mlua-rs/mlua
synced 2026-06-08 16:05:43 +00:00
Compare commits
32 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f2d48ce296 | |||
| 0fa39d431d | |||
| 0aa86c4d47 | |||
| ea2faa3755 | |||
| 59c9abbac7 | |||
| 8f3de8aa19 | |||
| 317ce7caa6 | |||
| 3a44729a48 | |||
| 3d46fad459 | |||
| ffc4bd599c | |||
| 1c969da286 | |||
| 45fd2fa40a | |||
| 806bd202d6 | |||
| a644049087 | |||
| ad31bed1db | |||
| 62f0bb97b0 | |||
| f67f8646ae | |||
| fa217d3706 | |||
| b62f2ee0f7 | |||
| 6e6c73e4c7 | |||
| 508517c45e | |||
| a79840afc9 | |||
| 39afe4c6f7 | |||
| 038cc5f974 | |||
| 59b14000f3 | |||
| 849206ef9d | |||
| 80fff4f2e7 | |||
| 9734146313 | |||
| 58be624222 | |||
| 3d43103431 | |||
| 5a22437d5f | |||
| 83c075c72b |
@@ -1,3 +1,20 @@
|
||||
## v0.9.8
|
||||
|
||||
- Fixed serializing same table multiple times (#408)
|
||||
- Use `mlua-sys` v0.6 (to support Luau 0.624+)
|
||||
- Fixed cross compilation of windows dlls from unix (#394)
|
||||
|
||||
## v0.9.7
|
||||
|
||||
- Implemented `IntoLua` for `RegistryKey`
|
||||
- Mark `__idiv` metamethod as available for luau
|
||||
- Added `Function::deep_clone()` method (Luau)
|
||||
- Added `SerializeOptions::detect_serde_json_arbitrary_precision` option
|
||||
- Added `Lua::create_buffer()` method (Luau)
|
||||
- Support serializing buffer type as a byte slice (Luau)
|
||||
- Perf: Implemented `push_into_stack`/`from_stack` for `Option<T>`
|
||||
- Added `Lua::create_ser_any_userdata()` method
|
||||
|
||||
## v0.9.6
|
||||
|
||||
- Added `to_pointer` function to `Function`/`Table`/`Thread`
|
||||
|
||||
+13
-11
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "mlua"
|
||||
version = "0.9.6" # remember to update mlua_derive
|
||||
version = "0.9.8" # remember to update mlua_derive
|
||||
authors = ["Aleksandr Orlenko <zxteam@pm.me>", "kyren <catherine@kyju.org>"]
|
||||
rust-version = "1.71"
|
||||
edition = "2021"
|
||||
@@ -32,19 +32,19 @@ lua52 = ["ffi/lua52"]
|
||||
lua51 = ["ffi/lua51"]
|
||||
luajit = ["ffi/luajit"]
|
||||
luajit52 = ["luajit", "ffi/luajit52"]
|
||||
luau = ["ffi/luau", "libloading"]
|
||||
luau = ["ffi/luau", "dep:libloading"]
|
||||
luau-jit = ["luau", "ffi/luau-codegen"]
|
||||
luau-vector4 = ["luau", "ffi/luau-vector4"]
|
||||
vendored = ["ffi/vendored"]
|
||||
module = ["mlua_derive", "ffi/module"]
|
||||
async = ["futures-util"]
|
||||
module = ["dep:mlua_derive", "ffi/module"]
|
||||
async = ["dep:futures-util"]
|
||||
send = []
|
||||
serialize = ["serde", "erased-serde", "serde-value"]
|
||||
serialize = ["dep:serde", "dep:erased-serde", "dep:serde-value"]
|
||||
macros = ["mlua_derive/macros"]
|
||||
unstable = []
|
||||
|
||||
[dependencies]
|
||||
mlua_derive = { version = "=0.9.2", optional = true, path = "mlua_derive" }
|
||||
mlua_derive = { version = "=0.9.3", optional = true, path = "mlua_derive" }
|
||||
bstr = { version = "1.0", features = ["std"], default_features = false }
|
||||
once_cell = { version = "1.0" }
|
||||
num-traits = { version = "0.2.14" }
|
||||
@@ -55,7 +55,7 @@ erased-serde = { version = "0.4", optional = true }
|
||||
serde-value = { version = "0.7", optional = true }
|
||||
parking_lot = { version = "0.12", optional = true }
|
||||
|
||||
ffi = { package = "mlua-sys", version = "0.5.1", path = "mlua-sys" }
|
||||
ffi = { package = "mlua-sys", version = "0.6.0", path = "mlua-sys" }
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
libloading = { version = "0.8", optional = true }
|
||||
@@ -63,18 +63,20 @@ libloading = { version = "0.8", optional = true }
|
||||
[dev-dependencies]
|
||||
trybuild = "1.0"
|
||||
futures = "0.3.5"
|
||||
hyper = { version = "0.14", features = ["client", "server"] }
|
||||
reqwest = { version = "0.11", features = ["json"] }
|
||||
hyper = { version = "1.2", features = ["full"] }
|
||||
hyper-util = { version = "0.1.3", features = ["full"] }
|
||||
http-body-util = "0.1.1"
|
||||
reqwest = { version = "0.12", features = ["json"] }
|
||||
tokio = { version = "1.0", features = ["macros", "rt", "time"] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
serde_json = { version = "1.0", features = ["arbitrary_precision"] }
|
||||
maplit = "1.0"
|
||||
tempfile = "3"
|
||||
static_assertions = "1.0"
|
||||
|
||||
[target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies]
|
||||
criterion = { version = "0.5", features = ["async_tokio"] }
|
||||
rustyline = "13.0"
|
||||
rustyline = "14.0"
|
||||
tokio = { version = "1.0", features = ["full"] }
|
||||
|
||||
[[bench]]
|
||||
|
||||
@@ -84,6 +84,20 @@ This works using Lua [coroutines](https://www.lua.org/manual/5.3/manual.html#2.6
|
||||
- [HTTP Server](examples/async_http_server.rs)
|
||||
- [TCP Server](examples/async_tcp_server.rs)
|
||||
|
||||
|
||||
**shell command examples**:
|
||||
```shell
|
||||
# async http client (hyper)
|
||||
cargo run --example async_http_client --features=lua54,async,macros
|
||||
|
||||
# async http client (reqwest)
|
||||
cargo run --example async_http_reqwest --features=lua54,async,macros,serialize
|
||||
|
||||
# async http server
|
||||
cargo run --example async_http_server --features=lua54,async,macros
|
||||
curl -v http://localhost:3000
|
||||
```
|
||||
|
||||
### Serialization (serde) support
|
||||
|
||||
With `serialize` feature flag enabled, `mlua` allows you to serialize/deserialize any type that implements [`serde::Serialize`] and [`serde::Deserialize`] into/from [`mlua::Value`]. In addition `mlua` provides [`serde::Serialize`] trait implementation for it (including `UserData` support).
|
||||
@@ -119,7 +133,7 @@ Add to `Cargo.toml` :
|
||||
|
||||
``` toml
|
||||
[dependencies]
|
||||
mlua = { version = "0.9.1", features = ["lua54", "vendored"] }
|
||||
mlua = { version = "0.9.8", features = ["lua54", "vendored"] }
|
||||
```
|
||||
|
||||
`main.rs`
|
||||
@@ -154,7 +168,7 @@ Add to `Cargo.toml` :
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
mlua = { version = "0.9.1", features = ["lua54", "module"] }
|
||||
mlua = { version = "0.9.8", features = ["lua54", "module"] }
|
||||
```
|
||||
|
||||
`lib.rs` :
|
||||
|
||||
@@ -269,7 +269,7 @@ fn registry_value_create(c: &mut Criterion) {
|
||||
}
|
||||
|
||||
fn userdata_create(c: &mut Criterion) {
|
||||
struct UserData(i64);
|
||||
struct UserData(#[allow(unused)] i64);
|
||||
impl LuaUserData for UserData {}
|
||||
|
||||
let lua = Lua::new();
|
||||
@@ -286,7 +286,7 @@ fn userdata_create(c: &mut Criterion) {
|
||||
}
|
||||
|
||||
fn userdata_call_index(c: &mut Criterion) {
|
||||
struct UserData(i64);
|
||||
struct UserData(#[allow(unused)] i64);
|
||||
impl LuaUserData for UserData {
|
||||
fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
methods.add_meta_method(LuaMetaMethod::Index, move |_, _, key: LuaString| Ok(key));
|
||||
|
||||
+35
-1
@@ -1,6 +1,7 @@
|
||||
use criterion::{criterion_group, criterion_main, BatchSize, Criterion};
|
||||
use std::time::Duration;
|
||||
|
||||
use criterion::{criterion_group, criterion_main, BatchSize, Criterion};
|
||||
|
||||
use mlua::prelude::*;
|
||||
|
||||
fn collect_gc_twice(lua: &Lua) {
|
||||
@@ -43,6 +44,38 @@ fn encode_json(c: &mut Criterion) {
|
||||
});
|
||||
}
|
||||
|
||||
fn decode_json(c: &mut Criterion) {
|
||||
let lua = Lua::new();
|
||||
|
||||
let decode = lua
|
||||
.create_function(|lua, s: String| {
|
||||
lua.to_value(&serde_json::from_str::<serde_json::Value>(&s).unwrap())
|
||||
})
|
||||
.unwrap();
|
||||
let json = r#"{
|
||||
"name": "Clark Kent",
|
||||
"address": {
|
||||
"city": "Smallville",
|
||||
"state": "Kansas",
|
||||
"country": "USA"
|
||||
},
|
||||
"age": 22,
|
||||
"parents": ["Jonathan Kent", "Martha Kent"],
|
||||
"superman": true,
|
||||
"interests": ["flying", "saving the world", "kryptonite"]
|
||||
}"#;
|
||||
|
||||
c.bench_function("deserialize json", |b| {
|
||||
b.iter_batched(
|
||||
|| collect_gc_twice(&lua),
|
||||
|_| {
|
||||
decode.call::<_, LuaTable>(json).unwrap();
|
||||
},
|
||||
BatchSize::SmallInput,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
criterion_group! {
|
||||
name = benches;
|
||||
config = Criterion::default()
|
||||
@@ -51,6 +84,7 @@ criterion_group! {
|
||||
.noise_threshold(0.02);
|
||||
targets =
|
||||
encode_json,
|
||||
decode_json,
|
||||
}
|
||||
|
||||
criterion_main!(benches);
|
||||
|
||||
@@ -1,18 +1,22 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use hyper::body::{Body as HyperBody, HttpBody as _};
|
||||
use hyper::Client as HyperClient;
|
||||
use http_body_util::BodyExt as _;
|
||||
use hyper::body::Incoming;
|
||||
use hyper_util::client::legacy::Client as HyperClient;
|
||||
use hyper_util::rt::TokioExecutor;
|
||||
|
||||
use mlua::{chunk, ExternalResult, Lua, Result, UserData, UserDataMethods};
|
||||
|
||||
struct BodyReader(HyperBody);
|
||||
struct BodyReader(Incoming);
|
||||
|
||||
impl UserData for BodyReader {
|
||||
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
// Every call returns a next chunk
|
||||
methods.add_async_method_mut("read", |lua, reader, ()| async move {
|
||||
if let Some(bytes) = reader.0.data().await {
|
||||
let bytes = bytes.into_lua_err()?;
|
||||
return Some(lua.create_string(&bytes)).transpose();
|
||||
if let Some(bytes) = reader.0.frame().await {
|
||||
if let Some(bytes) = bytes.into_lua_err()?.data_ref() {
|
||||
return Some(lua.create_string(&bytes)).transpose();
|
||||
}
|
||||
}
|
||||
Ok(None)
|
||||
});
|
||||
@@ -24,7 +28,7 @@ async fn main() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let fetch_url = lua.create_async_function(|lua, uri: String| async move {
|
||||
let client = HyperClient::new();
|
||||
let client = HyperClient::builder(TokioExecutor::new()).build_http::<String>();
|
||||
let uri = uri.parse().into_lua_err()?;
|
||||
let resp = client.get(uri).await.into_lua_err()?;
|
||||
|
||||
@@ -55,11 +59,11 @@ async fn main() -> Result<()> {
|
||||
end
|
||||
end
|
||||
repeat
|
||||
local body = res.body:read()
|
||||
if body then
|
||||
print(body)
|
||||
local chunk = res.body:read()
|
||||
if chunk then
|
||||
print(chunk)
|
||||
end
|
||||
until not body
|
||||
until not chunk
|
||||
})
|
||||
.into_function()?;
|
||||
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
use mlua::{chunk, ExternalResult, Lua, LuaSerdeExt, Result};
|
||||
use mlua::{chunk, ExternalResult, Lua, LuaSerdeExt, Result, Value};
|
||||
|
||||
#[tokio::main(flavor = "current_thread")]
|
||||
async fn main() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let null = lua.null();
|
||||
|
||||
let fetch_json = lua.create_async_function(|lua, uri: String| async move {
|
||||
let resp = reqwest::get(&uri)
|
||||
.await
|
||||
@@ -15,19 +13,15 @@ async fn main() -> Result<()> {
|
||||
lua.to_value(&json)
|
||||
})?;
|
||||
|
||||
let dbg = lua.create_function(|_, value: Value| {
|
||||
println!("{value:#?}");
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
let f = lua
|
||||
.load(chunk! {
|
||||
function print_r(t, indent)
|
||||
local indent = indent or ""
|
||||
for k, v in pairs(t) do
|
||||
io.write(indent, tostring(k))
|
||||
if type(v) == "table" then io.write(":\n") print_r(v, indent.." ")
|
||||
else io.write(": ", v == $null and "null" or tostring(v), "\n") end
|
||||
end
|
||||
end
|
||||
|
||||
local res = $fetch_json(...)
|
||||
print_r(res)
|
||||
$dbg(res)
|
||||
})
|
||||
.into_function()?;
|
||||
|
||||
|
||||
@@ -1,43 +1,63 @@
|
||||
use std::convert::Infallible;
|
||||
use std::future::Future;
|
||||
use std::net::SocketAddr;
|
||||
use std::pin::Pin;
|
||||
use std::rc::Rc;
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
use hyper::server::conn::AddrStream;
|
||||
use hyper::service::Service;
|
||||
use hyper::{Body, Request, Response, Server};
|
||||
use futures::future::LocalBoxFuture;
|
||||
use http_body_util::{combinators::BoxBody, BodyExt as _, Empty, Full};
|
||||
use hyper::body::{Bytes, Incoming};
|
||||
use hyper::{Request, Response};
|
||||
use hyper_util::rt::TokioIo;
|
||||
use hyper_util::server::conn::auto::Builder as ServerConnBuilder;
|
||||
use tokio::net::TcpListener;
|
||||
use tokio::task::LocalSet;
|
||||
|
||||
use mlua::{
|
||||
chunk, Error as LuaError, Function, Lua, String as LuaString, Table, UserData, UserDataMethods,
|
||||
chunk, Error as LuaError, Function, Lua, RegistryKey, String as LuaString, Table, UserData,
|
||||
UserDataMethods,
|
||||
};
|
||||
|
||||
struct LuaRequest(SocketAddr, Request<Body>);
|
||||
/// Wrapper around incoming request that implements UserData
|
||||
struct LuaRequest(SocketAddr, Request<Incoming>);
|
||||
|
||||
impl UserData for LuaRequest {
|
||||
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
methods.add_method("remote_addr", |_lua, req, ()| Ok((req.0).to_string()));
|
||||
methods.add_method("method", |_lua, req, ()| Ok((req.1).method().to_string()));
|
||||
methods.add_method("remote_addr", |_, req, ()| Ok((req.0).to_string()));
|
||||
methods.add_method("method", |_, req, ()| Ok((req.1).method().to_string()));
|
||||
methods.add_method("path", |_, req, ()| Ok(req.1.uri().path().to_string()));
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Svc(Rc<Lua>, SocketAddr);
|
||||
/// Service that handles incoming requests
|
||||
#[derive(Clone)]
|
||||
pub struct Svc {
|
||||
lua: Rc<Lua>,
|
||||
handler: Rc<RegistryKey>,
|
||||
peer_addr: SocketAddr,
|
||||
}
|
||||
|
||||
impl Service<Request<Body>> for Svc {
|
||||
type Response = Response<Body>;
|
||||
type Error = LuaError;
|
||||
type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>>>>;
|
||||
|
||||
fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
Poll::Ready(Ok(()))
|
||||
impl Svc {
|
||||
pub fn new(lua: Rc<Lua>, handler: Rc<RegistryKey>, peer_addr: SocketAddr) -> Self {
|
||||
Self {
|
||||
lua,
|
||||
handler,
|
||||
peer_addr,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn call(&mut self, req: Request<Body>) -> Self::Future {
|
||||
impl hyper::service::Service<Request<Incoming>> for Svc {
|
||||
type Response = Response<BoxBody<Bytes, Infallible>>;
|
||||
type Error = LuaError;
|
||||
type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;
|
||||
|
||||
fn call(&self, req: Request<Incoming>) -> Self::Future {
|
||||
// If handler returns an error then generate 5xx response
|
||||
let lua = self.0.clone();
|
||||
let lua_req = LuaRequest(self.1, req);
|
||||
let lua = self.lua.clone();
|
||||
let handler_key = self.handler.clone();
|
||||
let lua_req = LuaRequest(self.peer_addr, req);
|
||||
Box::pin(async move {
|
||||
let handler: Function = lua.named_registry_value("http_handler")?;
|
||||
let handler: Function = lua.registry_value(&handler_key)?;
|
||||
match handler.call_async::<_, Table>(lua_req).await {
|
||||
Ok(lua_resp) => {
|
||||
let status = lua_resp.get::<_, Option<u16>>("status")?.unwrap_or(200);
|
||||
@@ -51,10 +71,11 @@ impl Service<Request<Body>> for Svc {
|
||||
}
|
||||
}
|
||||
|
||||
// Set body
|
||||
let body = lua_resp
|
||||
.get::<_, Option<LuaString>>("body")?
|
||||
.map(|b| Body::from(b.as_bytes().to_vec()))
|
||||
.unwrap_or_else(Body::empty);
|
||||
.map(|b| Full::new(Bytes::copy_from_slice(b.as_bytes())).boxed())
|
||||
.unwrap_or_else(|| Empty::<Bytes>::new().boxed());
|
||||
|
||||
Ok(resp.body(body).unwrap())
|
||||
}
|
||||
@@ -62,7 +83,7 @@ impl Service<Request<Body>> for Svc {
|
||||
eprintln!("{}", err);
|
||||
Ok(Response::builder()
|
||||
.status(500)
|
||||
.body(Body::from("Internal Server Error"))
|
||||
.body(Full::new(Bytes::from("Internal Server Error")).boxed())
|
||||
.unwrap())
|
||||
}
|
||||
}
|
||||
@@ -75,13 +96,14 @@ async fn main() {
|
||||
let lua = Rc::new(Lua::new());
|
||||
|
||||
// Create Lua handler function
|
||||
let handler: Function = lua
|
||||
let handler: RegistryKey = lua
|
||||
.load(chunk! {
|
||||
function(req)
|
||||
return {
|
||||
status = 200,
|
||||
headers = {
|
||||
["X-Req-Method"] = req:method(),
|
||||
["X-Req-Path"] = req:path(),
|
||||
["X-Remote-Addr"] = req:remote_addr(),
|
||||
},
|
||||
body = "Hello from Lua!\n"
|
||||
@@ -89,37 +111,35 @@ async fn main() {
|
||||
end
|
||||
})
|
||||
.eval()
|
||||
.expect("cannot create Lua handler");
|
||||
.expect("Failed to create Lua handler");
|
||||
let handler = Rc::new(handler);
|
||||
|
||||
// Store it in the Registry
|
||||
lua.set_named_registry_value("http_handler", handler)
|
||||
.expect("cannot store Lua handler");
|
||||
let listen_addr = "127.0.0.1:3000";
|
||||
let listener = TcpListener::bind(listen_addr).await.unwrap();
|
||||
println!("Listening on http://{listen_addr}");
|
||||
|
||||
let addr = ([127, 0, 0, 1], 3000).into();
|
||||
let server = Server::bind(&addr).executor(LocalExec).serve(MakeSvc(lua));
|
||||
let local = LocalSet::new();
|
||||
loop {
|
||||
let (stream, peer_addr) = match listener.accept().await {
|
||||
Ok(x) => x,
|
||||
Err(err) => {
|
||||
eprintln!("Failed to accept connection: {err}");
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
println!("Listening on http://{}", addr);
|
||||
|
||||
// Create `LocalSet` to spawn !Send futures
|
||||
let local = tokio::task::LocalSet::new();
|
||||
local.run_until(server).await.expect("cannot run server")
|
||||
}
|
||||
|
||||
struct MakeSvc(Rc<Lua>);
|
||||
|
||||
impl Service<&AddrStream> for MakeSvc {
|
||||
type Response = Svc;
|
||||
type Error = hyper::Error;
|
||||
type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>>>>;
|
||||
|
||||
fn poll_ready(&mut self, _: &mut Context) -> Poll<Result<(), Self::Error>> {
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
|
||||
fn call(&mut self, stream: &AddrStream) -> Self::Future {
|
||||
let lua = self.0.clone();
|
||||
let remote_addr = stream.remote_addr();
|
||||
Box::pin(async move { Ok(Svc(lua, remote_addr)) })
|
||||
let svc = Svc::new(lua.clone(), handler.clone(), peer_addr);
|
||||
local
|
||||
.run_until(async move {
|
||||
let result = ServerConnBuilder::new(LocalExec)
|
||||
.http1()
|
||||
.serve_connection(TokioIo::new(stream), svc)
|
||||
.await;
|
||||
if let Err(err) = result {
|
||||
eprintln!("Error serving connection: {err:?}");
|
||||
}
|
||||
})
|
||||
.await;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,7 +148,7 @@ struct LocalExec;
|
||||
|
||||
impl<F> hyper::rt::Executor<F> for LocalExec
|
||||
where
|
||||
F: std::future::Future + 'static, // not requiring `Send`
|
||||
F: Future + 'static, // not requiring `Send`
|
||||
{
|
||||
fn execute(&self, fut: F) {
|
||||
tokio::task::spawn_local(fut);
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "mlua-sys"
|
||||
version = "0.5.1"
|
||||
version = "0.6.0"
|
||||
authors = ["Aleksandr Orlenko <zxteam@pm.me>"]
|
||||
rust-version = "1.71"
|
||||
edition = "2021"
|
||||
@@ -40,4 +40,4 @@ cfg-if = "1.0"
|
||||
pkg-config = "0.3.17"
|
||||
lua-src = { version = ">= 546.0.2, < 546.1.0", optional = true }
|
||||
luajit-src = { version = ">= 210.5.0, < 210.6.0", optional = true }
|
||||
luau0-src = { version = "0.8.0", optional = true }
|
||||
luau0-src = { version = "0.9.0", optional = true }
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use std::env;
|
||||
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(any(feature = "luau", feature = "vendored"))] {
|
||||
#[path = "find_vendored.rs"]
|
||||
@@ -17,8 +19,8 @@ fn main() {
|
||||
|
||||
println!("cargo:rerun-if-changed=build");
|
||||
|
||||
#[cfg(windows)]
|
||||
if cfg!(feature = "module") {
|
||||
let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap();
|
||||
if target_os == "windows" && cfg!(feature = "module") {
|
||||
if !std::env::var("LUA_LIB_NAME").unwrap_or_default().is_empty() {
|
||||
// Don't use raw-dylib linking
|
||||
find::probe_lua();
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
//!
|
||||
//! Based on github.com/keplerproject/lua-compat-5.3
|
||||
|
||||
use std::convert::TryInto;
|
||||
use std::mem;
|
||||
use std::os::raw::{c_char, c_int, c_void};
|
||||
use std::ptr;
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
//!
|
||||
//! Based on github.com/keplerproject/lua-compat-5.3
|
||||
|
||||
use std::convert::TryInto;
|
||||
use std::os::raw::{c_char, c_int, c_void};
|
||||
use std::ptr;
|
||||
|
||||
|
||||
@@ -525,6 +525,7 @@ pub struct lua_Debug {
|
||||
//
|
||||
|
||||
#[repr(C)]
|
||||
#[non_exhaustive]
|
||||
pub struct lua_Callbacks {
|
||||
/// arbitrary userdata pointer that is never overwritten by Luau
|
||||
pub userdata: *mut c_void,
|
||||
@@ -552,3 +553,8 @@ pub struct lua_Callbacks {
|
||||
extern "C" {
|
||||
pub fn lua_callbacks(L: *mut lua_State) -> *mut lua_Callbacks;
|
||||
}
|
||||
|
||||
// Functions from customization lib
|
||||
extern "C" {
|
||||
pub fn luau_setfflag(name: *const c_char, value: c_int) -> c_int;
|
||||
}
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
//! Contains definitions from `luacode.h`.
|
||||
|
||||
use std::os::raw::{c_char, c_int, c_void};
|
||||
use std::slice;
|
||||
use std::{ptr, slice};
|
||||
|
||||
#[repr(C)]
|
||||
#[non_exhaustive]
|
||||
pub struct lua_CompileOptions {
|
||||
pub optimizationLevel: c_int,
|
||||
pub debugLevel: c_int,
|
||||
pub typeInfoLevel: c_int,
|
||||
pub coverageLevel: c_int,
|
||||
pub vectorLib: *const c_char,
|
||||
pub vectorCtor: *const c_char,
|
||||
@@ -14,6 +16,21 @@ pub struct lua_CompileOptions {
|
||||
pub mutableGlobals: *const *const c_char,
|
||||
}
|
||||
|
||||
impl Default for lua_CompileOptions {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
optimizationLevel: 1,
|
||||
debugLevel: 1,
|
||||
typeInfoLevel: 0,
|
||||
coverageLevel: 0,
|
||||
vectorLib: ptr::null(),
|
||||
vectorCtor: ptr::null(),
|
||||
vectorType: ptr::null(),
|
||||
mutableGlobals: ptr::null(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern "C-unwind" {
|
||||
#[link_name = "luau_compile"]
|
||||
pub fn luau_compile_(
|
||||
@@ -36,6 +53,7 @@ pub unsafe fn luau_compile(source: &[u8], mut options: lua_CompileOptions) -> Ve
|
||||
&mut options,
|
||||
&mut outsize,
|
||||
);
|
||||
assert!(!data_ptr.is_null(), "luau_compile failed");
|
||||
let data = slice::from_raw_parts(data_ptr as *mut u8, outsize).to_vec();
|
||||
free(data_ptr as *mut c_void);
|
||||
data
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "mlua_derive"
|
||||
version = "0.9.2"
|
||||
version = "0.9.3"
|
||||
authors = ["Aleksandr Orlenko <zxteam@pm.me>"]
|
||||
edition = "2021"
|
||||
description = "Procedural macros for the mlua crate."
|
||||
|
||||
@@ -58,13 +58,13 @@ pub fn lua_module(attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||
};
|
||||
|
||||
let wrapped = quote! {
|
||||
::mlua::require_module_feature!();
|
||||
mlua::require_module_feature!();
|
||||
|
||||
#func
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C-unwind" fn #ext_entrypoint_name(state: *mut ::mlua::lua_State) -> ::std::os::raw::c_int {
|
||||
let lua = ::mlua::Lua::init_from_ptr(state);
|
||||
unsafe extern "C-unwind" fn #ext_entrypoint_name(state: *mut mlua::lua_State) -> ::std::os::raw::c_int {
|
||||
let lua = mlua::Lua::init_from_ptr(state);
|
||||
#skip_memory_check
|
||||
lua.entrypoint1(state, #func_name)
|
||||
}
|
||||
@@ -95,7 +95,7 @@ pub fn chunk(input: TokenStream) -> TokenStream {
|
||||
});
|
||||
|
||||
let wrapped_code = quote! {{
|
||||
use ::mlua::{AsChunk, ChunkMode, Lua, Result, Table};
|
||||
use mlua::{AsChunk, ChunkMode, Lua, Result, Table};
|
||||
use ::std::borrow::Cow;
|
||||
use ::std::cell::Cell;
|
||||
use ::std::io::Result as IoResult;
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
use std::{
|
||||
cmp::{Eq, PartialEq},
|
||||
fmt::{self, Display, Formatter},
|
||||
iter::IntoIterator,
|
||||
vec::IntoIter,
|
||||
};
|
||||
|
||||
|
||||
+21
-9
@@ -122,6 +122,7 @@ pub enum ChunkMode {
|
||||
pub struct Compiler {
|
||||
optimization_level: u8,
|
||||
debug_level: u8,
|
||||
type_info_level: u8,
|
||||
coverage_level: u8,
|
||||
vector_lib: Option<String>,
|
||||
vector_ctor: Option<String>,
|
||||
@@ -144,6 +145,7 @@ impl Compiler {
|
||||
Compiler {
|
||||
optimization_level: 1,
|
||||
debug_level: 1,
|
||||
type_info_level: 0,
|
||||
coverage_level: 0,
|
||||
vector_lib: None,
|
||||
vector_ctor: None,
|
||||
@@ -176,6 +178,16 @@ impl Compiler {
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets Luau type information level used to guide native code generation decisions.
|
||||
///
|
||||
/// Possible values:
|
||||
/// * 0 - generate for native modules (default)
|
||||
/// * 1 - generate for all modules
|
||||
pub const fn set_type_info_level(mut self, level: u8) -> Self {
|
||||
self.type_info_level = level;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets Luau compiler code coverage level.
|
||||
///
|
||||
/// Possible values:
|
||||
@@ -250,15 +262,15 @@ impl Compiler {
|
||||
}
|
||||
|
||||
unsafe {
|
||||
let options = ffi::lua_CompileOptions {
|
||||
optimizationLevel: self.optimization_level as c_int,
|
||||
debugLevel: self.debug_level as c_int,
|
||||
coverageLevel: self.coverage_level as c_int,
|
||||
vectorLib: vector_lib.map_or(ptr::null(), |s| s.as_ptr()),
|
||||
vectorCtor: vector_ctor.map_or(ptr::null(), |s| s.as_ptr()),
|
||||
vectorType: vector_type.map_or(ptr::null(), |s| s.as_ptr()),
|
||||
mutableGlobals: mutable_globals_ptr,
|
||||
};
|
||||
let mut options = ffi::lua_CompileOptions::default();
|
||||
options.optimizationLevel = self.optimization_level as c_int;
|
||||
options.debugLevel = self.debug_level as c_int;
|
||||
options.typeInfoLevel = self.type_info_level as c_int;
|
||||
options.coverageLevel = self.coverage_level as c_int;
|
||||
options.vectorLib = vector_lib.map_or(ptr::null(), |s| s.as_ptr());
|
||||
options.vectorCtor = vector_ctor.map_or(ptr::null(), |s| s.as_ptr());
|
||||
options.vectorType = vector_type.map_or(ptr::null(), |s| s.as_ptr());
|
||||
options.mutableGlobals = mutable_globals_ptr;
|
||||
ffi::luau_compile(source.as_ref(), options)
|
||||
}
|
||||
}
|
||||
|
||||
+85
-16
@@ -1,6 +1,5 @@
|
||||
use std::borrow::Cow;
|
||||
use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
|
||||
use std::convert::TryInto;
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::hash::{BuildHasher, Hash};
|
||||
use std::os::raw::c_int;
|
||||
@@ -67,7 +66,8 @@ impl<'lua> IntoLua<'lua> for &String<'lua> {
|
||||
|
||||
#[inline]
|
||||
unsafe fn push_into_stack(self, lua: &'lua Lua) -> Result<()> {
|
||||
Ok(lua.push_ref(&self.0))
|
||||
lua.push_ref(&self.0);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,7 +103,8 @@ impl<'lua> IntoLua<'lua> for &OwnedString {
|
||||
|
||||
#[inline]
|
||||
unsafe fn push_into_stack(self, lua: &'lua Lua) -> Result<()> {
|
||||
Ok(lua.push_owned_ref(&self.0))
|
||||
lua.push_owned_ref(&self.0);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,7 +132,8 @@ impl<'lua> IntoLua<'lua> for &Table<'lua> {
|
||||
|
||||
#[inline]
|
||||
unsafe fn push_into_stack(self, lua: &'lua Lua) -> Result<()> {
|
||||
Ok(lua.push_ref(&self.0))
|
||||
lua.push_ref(&self.0);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -168,7 +170,8 @@ impl<'lua> IntoLua<'lua> for &OwnedTable {
|
||||
|
||||
#[inline]
|
||||
unsafe fn push_into_stack(self, lua: &'lua Lua) -> Result<()> {
|
||||
Ok(lua.push_owned_ref(&self.0))
|
||||
lua.push_owned_ref(&self.0);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -196,7 +199,8 @@ impl<'lua> IntoLua<'lua> for &Function<'lua> {
|
||||
|
||||
#[inline]
|
||||
unsafe fn push_into_stack(self, lua: &'lua Lua) -> Result<()> {
|
||||
Ok(lua.push_ref(&self.0))
|
||||
lua.push_ref(&self.0);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -233,7 +237,8 @@ impl<'lua> IntoLua<'lua> for &OwnedFunction {
|
||||
|
||||
#[inline]
|
||||
unsafe fn push_into_stack(self, lua: &'lua Lua) -> Result<()> {
|
||||
Ok(lua.push_owned_ref(&self.0))
|
||||
lua.push_owned_ref(&self.0);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -261,7 +266,8 @@ impl<'lua> IntoLua<'lua> for &Thread<'lua> {
|
||||
|
||||
#[inline]
|
||||
unsafe fn push_into_stack(self, lua: &'lua Lua) -> Result<()> {
|
||||
Ok(lua.push_ref(&self.0))
|
||||
lua.push_ref(&self.0);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -298,7 +304,8 @@ impl<'lua> IntoLua<'lua> for &OwnedThread {
|
||||
|
||||
#[inline]
|
||||
unsafe fn push_into_stack(self, lua: &'lua Lua) -> Result<()> {
|
||||
Ok(lua.push_owned_ref(&self.0))
|
||||
lua.push_owned_ref(&self.0);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -326,7 +333,8 @@ impl<'lua> IntoLua<'lua> for &AnyUserData<'lua> {
|
||||
|
||||
#[inline]
|
||||
unsafe fn push_into_stack(self, lua: &'lua Lua) -> Result<()> {
|
||||
Ok(lua.push_ref(&self.0))
|
||||
lua.push_ref(&self.0);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -366,7 +374,8 @@ impl<'lua> IntoLua<'lua> for &OwnedAnyUserData {
|
||||
|
||||
#[inline]
|
||||
unsafe fn push_into_stack(self, lua: &'lua Lua) -> Result<()> {
|
||||
Ok(lua.push_owned_ref(&self.0))
|
||||
lua.push_owned_ref(&self.0);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -421,6 +430,18 @@ impl<'lua> FromLua<'lua> for Error {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> IntoLua<'lua> for RegistryKey {
|
||||
#[inline]
|
||||
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
lua.registry_value(&self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn push_into_stack(self, lua: &'lua Lua) -> Result<()> {
|
||||
<&RegistryKey>::push_into_stack(&self, lua)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> IntoLua<'lua> for &RegistryKey {
|
||||
#[inline]
|
||||
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
@@ -663,19 +684,49 @@ impl<'lua> IntoLua<'lua> for BString {
|
||||
}
|
||||
|
||||
impl<'lua> FromLua<'lua> for BString {
|
||||
#[inline]
|
||||
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<Self> {
|
||||
let ty = value.type_name();
|
||||
Ok(BString::from(
|
||||
lua.coerce_string(value)?
|
||||
match value {
|
||||
Value::String(s) => Ok(s.as_bytes().into()),
|
||||
#[cfg(feature = "luau")]
|
||||
Value::UserData(ud) if ud.1 == crate::types::SubtypeId::Buffer => unsafe {
|
||||
let mut size = 0usize;
|
||||
let buf = ffi::lua_tobuffer(ud.0.lua.ref_thread(), ud.0.index, &mut size);
|
||||
mlua_assert!(!buf.is_null(), "invalid Luau buffer");
|
||||
Ok(slice::from_raw_parts(buf as *const u8, size).into())
|
||||
},
|
||||
_ => Ok(lua
|
||||
.coerce_string(value)?
|
||||
.ok_or_else(|| Error::FromLuaConversionError {
|
||||
from: ty,
|
||||
to: "BString",
|
||||
message: Some("expected string or number".to_string()),
|
||||
})?
|
||||
.as_bytes()
|
||||
.to_vec(),
|
||||
))
|
||||
.into()),
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn from_stack(idx: c_int, lua: &'lua Lua) -> Result<Self> {
|
||||
let state = lua.state();
|
||||
match ffi::lua_type(state, idx) {
|
||||
ffi::LUA_TSTRING => {
|
||||
let mut size = 0;
|
||||
let data = ffi::lua_tolstring(state, idx, &mut size);
|
||||
Ok(slice::from_raw_parts(data as *const u8, size).into())
|
||||
}
|
||||
#[cfg(feature = "luau")]
|
||||
ffi::LUA_TBUFFER => {
|
||||
let mut size = 0;
|
||||
let buf = ffi::lua_tobuffer(state, idx, &mut size);
|
||||
mlua_assert!(!buf.is_null(), "invalid Luau buffer");
|
||||
Ok(slice::from_raw_parts(buf as *const u8, size).into())
|
||||
}
|
||||
_ => {
|
||||
// Fallback to default
|
||||
Self::from_lua(lua.stack_value(idx), lua)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1017,6 +1068,15 @@ impl<'lua, T: IntoLua<'lua>> IntoLua<'lua> for Option<T> {
|
||||
None => Ok(Nil),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn push_into_stack(self, lua: &'lua Lua) -> Result<()> {
|
||||
match self {
|
||||
Some(val) => val.push_into_stack(lua)?,
|
||||
None => ffi::lua_pushnil(lua.state()),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua, T: FromLua<'lua>> FromLua<'lua> for Option<T> {
|
||||
@@ -1027,4 +1087,13 @@ impl<'lua, T: FromLua<'lua>> FromLua<'lua> for Option<T> {
|
||||
value => Ok(Some(T::from_lua(value, lua)?)),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn from_stack(idx: c_int, lua: &'lua Lua) -> Result<Self> {
|
||||
if ffi::lua_isnil(lua.state(), idx) != 0 {
|
||||
Ok(None)
|
||||
} else {
|
||||
Ok(Some(T::from_stack(idx, lua)?))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -504,6 +504,27 @@ impl<'lua> Function<'lua> {
|
||||
self.0.to_pointer()
|
||||
}
|
||||
|
||||
/// Creates a deep clone of the Lua function.
|
||||
///
|
||||
/// Copies the function prototype and all its upvalues to the
|
||||
/// newly created function.
|
||||
///
|
||||
/// This function returns shallow clone (same handle) for Rust/C functions.
|
||||
/// Requires `feature = "luau"`
|
||||
#[cfg(feature = "luau")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
pub fn deep_clone(&self) -> Self {
|
||||
let ref_thread = self.0.lua.ref_thread();
|
||||
unsafe {
|
||||
if ffi::lua_iscfunction(ref_thread, self.0.index) != 0 {
|
||||
return self.clone();
|
||||
}
|
||||
|
||||
ffi::lua_clonefunction(ref_thread, self.0.index);
|
||||
Function(self.0.lua.pop_ref_thread())
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert this handle to owned version.
|
||||
#[cfg(all(feature = "unstable", any(not(feature = "send"), doc)))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all(feature = "unstable", not(feature = "send")))))]
|
||||
|
||||
+51
@@ -1288,6 +1288,21 @@ impl Lua {
|
||||
unsafe { (*self.extra.get()).enable_jit = enable };
|
||||
}
|
||||
|
||||
/// Sets Luau feature flag (global setting).
|
||||
///
|
||||
/// See https://github.com/luau-lang/luau/blob/master/CONTRIBUTING.md#feature-flags for details.
|
||||
#[cfg(feature = "luau")]
|
||||
#[doc(hidden)]
|
||||
#[allow(clippy::result_unit_err)]
|
||||
pub fn set_fflag(name: &str, enabled: bool) -> StdResult<(), ()> {
|
||||
if let Ok(name) = CString::new(name) {
|
||||
if unsafe { ffi::luau_setfflag(name.as_ptr(), enabled as c_int) != 0 } {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
Err(())
|
||||
}
|
||||
|
||||
/// Returns Lua source code as a `Chunk` builder type.
|
||||
///
|
||||
/// In order to actually compile or run the resulting code, you must call [`Chunk::exec`] or
|
||||
@@ -1373,6 +1388,27 @@ impl Lua {
|
||||
}
|
||||
}
|
||||
|
||||
/// Create and return a Luau [buffer] object from a byte slice of data.
|
||||
///
|
||||
/// Requires `feature = "luau"`
|
||||
///
|
||||
/// [buffer]: https://luau-lang.org/library#buffer-library
|
||||
#[cfg(feature = "luau")]
|
||||
pub fn create_buffer(&self, buf: impl AsRef<[u8]>) -> Result<AnyUserData> {
|
||||
let state = self.state();
|
||||
unsafe {
|
||||
if self.unlikely_memory_error() {
|
||||
crate::util::push_buffer(self.ref_thread(), buf.as_ref(), false)?;
|
||||
return Ok(AnyUserData(self.pop_ref_thread(), SubtypeId::Buffer));
|
||||
}
|
||||
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 4)?;
|
||||
crate::util::push_buffer(state, buf.as_ref(), true)?;
|
||||
Ok(AnyUserData(self.pop_ref(), SubtypeId::Buffer))
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates and returns a new empty table.
|
||||
pub fn create_table(&self) -> Result<Table> {
|
||||
self.create_table_with_capacity(0, 0)
|
||||
@@ -1725,6 +1761,21 @@ impl Lua {
|
||||
unsafe { self.make_any_userdata(UserDataCell::new(data)) }
|
||||
}
|
||||
|
||||
/// Creates a Lua userdata object from a custom serializable Rust type.
|
||||
///
|
||||
/// See [`Lua::create_any_userdata()`] for more details.
|
||||
///
|
||||
/// Requires `feature = "serialize"`
|
||||
#[cfg(feature = "serialize")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
|
||||
#[inline]
|
||||
pub fn create_ser_any_userdata<T>(&self, data: T) -> Result<AnyUserData>
|
||||
where
|
||||
T: Serialize + MaybeSend + 'static,
|
||||
{
|
||||
unsafe { self.make_any_userdata(UserDataCell::new_ser(data)) }
|
||||
}
|
||||
|
||||
/// Registers a custom Rust type in Lua to use in userdata objects.
|
||||
///
|
||||
/// This methods provides a way to add fields or methods to userdata objects of a type `T`.
|
||||
|
||||
@@ -103,6 +103,7 @@ pub(crate) fn register_package_module(lua: &Lua) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[allow(unused_variables)]
|
||||
pub(crate) fn disable_dylibs(lua: &Lua) {
|
||||
// Presence of `LoadedDylibs` in app data is used as a flag
|
||||
// to check whether binary modules are enabled
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
use std::iter::FromIterator;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::os::raw::c_int;
|
||||
use std::result::Result as StdResult;
|
||||
|
||||
+11
-6
@@ -1,5 +1,4 @@
|
||||
use std::cell::RefCell;
|
||||
use std::convert::TryInto;
|
||||
use std::os::raw::c_void;
|
||||
use std::rc::Rc;
|
||||
use std::result::Result as StdResult;
|
||||
@@ -134,9 +133,7 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
|
||||
Value::Nil => visitor.visit_unit(),
|
||||
Value::Boolean(b) => visitor.visit_bool(b),
|
||||
#[allow(clippy::useless_conversion)]
|
||||
Value::Integer(i) => {
|
||||
visitor.visit_i64(i.try_into().expect("cannot convert lua_Integer to i64"))
|
||||
}
|
||||
Value::Integer(i) => visitor.visit_i64(i.into()),
|
||||
#[allow(clippy::useless_conversion)]
|
||||
Value::Number(n) => visitor.visit_f64(n.into()),
|
||||
#[cfg(feature = "luau")]
|
||||
@@ -151,6 +148,14 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
|
||||
Value::UserData(ud) if ud.is_serializable() => {
|
||||
serde_userdata(ud, |value| value.deserialize_any(visitor))
|
||||
}
|
||||
#[cfg(feature = "luau")]
|
||||
Value::UserData(ud) if ud.1 == crate::types::SubtypeId::Buffer => unsafe {
|
||||
let mut size = 0usize;
|
||||
let buf = ffi::lua_tobuffer(ud.0.lua.ref_thread(), ud.0.index, &mut size);
|
||||
mlua_assert!(!buf.is_null(), "invalid Luau buffer");
|
||||
let buf = std::slice::from_raw_parts(buf as *const u8, size);
|
||||
visitor.visit_bytes(buf)
|
||||
},
|
||||
Value::Function(_)
|
||||
| Value::Thread(_)
|
||||
| Value::UserData(_)
|
||||
@@ -655,14 +660,14 @@ impl<'lua, 'de> de::VariantAccess<'de> for VariantDeserializer<'lua> {
|
||||
|
||||
// Adds `ptr` to the `visited` map and removes on drop
|
||||
// Used to track recursive tables but allow to traverse same tables multiple times
|
||||
struct RecursionGuard {
|
||||
pub(crate) struct RecursionGuard {
|
||||
ptr: *const c_void,
|
||||
visited: Rc<RefCell<FxHashSet<*const c_void>>>,
|
||||
}
|
||||
|
||||
impl RecursionGuard {
|
||||
#[inline]
|
||||
fn new(table: &Table, visited: &Rc<RefCell<FxHashSet<*const c_void>>>) -> Self {
|
||||
pub(crate) fn new(table: &Table, visited: &Rc<RefCell<FxHashSet<*const c_void>>>) -> Self {
|
||||
let visited = Rc::clone(visited);
|
||||
let ptr = table.to_pointer();
|
||||
visited.borrow_mut().insert(ptr);
|
||||
|
||||
+83
-15
@@ -3,7 +3,6 @@ use serde::{ser, Serialize};
|
||||
use super::LuaSerdeExt;
|
||||
use crate::error::{Error, Result};
|
||||
use crate::lua::Lua;
|
||||
use crate::string::String;
|
||||
use crate::table::Table;
|
||||
use crate::value::{IntoLua, Value};
|
||||
|
||||
@@ -43,6 +42,12 @@ pub struct Options {
|
||||
/// [`null`]: crate::LuaSerdeExt::null
|
||||
/// [`Nil`]: crate::Value::Nil
|
||||
pub serialize_unit_to_null: bool,
|
||||
|
||||
/// If true, serialize `serde_json::Number` with arbitrary_precision to a Lua number.
|
||||
/// Otherwise it will be serialized as an object (what serde does).
|
||||
///
|
||||
/// Default: **false**
|
||||
pub detect_serde_json_arbitrary_precision: bool,
|
||||
}
|
||||
|
||||
impl Default for Options {
|
||||
@@ -58,6 +63,7 @@ impl Options {
|
||||
set_array_metatable: true,
|
||||
serialize_none_to_null: true,
|
||||
serialize_unit_to_null: true,
|
||||
detect_serde_json_arbitrary_precision: false,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,6 +93,20 @@ impl Options {
|
||||
self.serialize_unit_to_null = enabled;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets [`detect_serde_json_arbitrary_precision`] option.
|
||||
///
|
||||
/// This option is used to serialize `serde_json::Number` with arbitrary precision to a Lua number.
|
||||
/// Otherwise it will be serialized as an object (what serde does).
|
||||
///
|
||||
/// This option is disabled by default.
|
||||
///
|
||||
/// [`detect_serde_json_arbitrary_precision`]: #structfield.detect_serde_json_arbitrary_precision
|
||||
#[must_use]
|
||||
pub const fn detect_serde_json_arbitrary_precision(mut self, enabled: bool) -> Self {
|
||||
self.detect_serde_json_arbitrary_precision = enabled;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> Serializer<'lua> {
|
||||
@@ -121,7 +141,7 @@ impl<'lua> ser::Serializer for Serializer<'lua> {
|
||||
type SerializeTupleStruct = SerializeSeq<'lua>;
|
||||
type SerializeTupleVariant = SerializeTupleVariant<'lua>;
|
||||
type SerializeMap = SerializeMap<'lua>;
|
||||
type SerializeStruct = SerializeMap<'lua>;
|
||||
type SerializeStruct = SerializeStruct<'lua>;
|
||||
type SerializeStructVariant = SerializeStructVariant<'lua>;
|
||||
|
||||
#[inline]
|
||||
@@ -266,7 +286,7 @@ impl<'lua> ser::Serializer for Serializer<'lua> {
|
||||
_len: usize,
|
||||
) -> Result<Self::SerializeTupleVariant> {
|
||||
Ok(SerializeTupleVariant {
|
||||
name: self.lua.create_string(variant)?,
|
||||
variant,
|
||||
table: self.lua.create_table()?,
|
||||
options: self.options,
|
||||
})
|
||||
@@ -282,8 +302,23 @@ impl<'lua> ser::Serializer for Serializer<'lua> {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_struct(self, _name: &'static str, len: usize) -> Result<Self::SerializeStruct> {
|
||||
self.serialize_map(Some(len))
|
||||
fn serialize_struct(self, name: &'static str, len: usize) -> Result<Self::SerializeStruct> {
|
||||
if self.options.detect_serde_json_arbitrary_precision
|
||||
&& name == "$serde_json::private::Number"
|
||||
&& len == 1
|
||||
{
|
||||
return Ok(SerializeStruct {
|
||||
lua: self.lua,
|
||||
inner: None,
|
||||
options: self.options,
|
||||
});
|
||||
}
|
||||
|
||||
Ok(SerializeStruct {
|
||||
lua: self.lua,
|
||||
inner: Some(Value::Table(self.lua.create_table_with_capacity(0, len)?)),
|
||||
options: self.options,
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -295,7 +330,7 @@ impl<'lua> ser::Serializer for Serializer<'lua> {
|
||||
len: usize,
|
||||
) -> Result<Self::SerializeStructVariant> {
|
||||
Ok(SerializeStructVariant {
|
||||
name: self.lua.create_string(variant)?,
|
||||
variant,
|
||||
table: self.lua.create_table_with_capacity(0, len)?,
|
||||
options: self.options,
|
||||
})
|
||||
@@ -402,7 +437,7 @@ impl<'lua> ser::SerializeTupleStruct for SerializeSeq<'lua> {
|
||||
|
||||
#[doc(hidden)]
|
||||
pub struct SerializeTupleVariant<'lua> {
|
||||
name: String<'lua>,
|
||||
variant: &'static str,
|
||||
table: Table<'lua>,
|
||||
options: Options,
|
||||
}
|
||||
@@ -422,7 +457,7 @@ impl<'lua> ser::SerializeTupleVariant for SerializeTupleVariant<'lua> {
|
||||
fn end(self) -> Result<Value<'lua>> {
|
||||
let lua = self.table.0.lua;
|
||||
let table = lua.create_table()?;
|
||||
table.raw_set(self.name, self.table)?;
|
||||
table.raw_set(self.variant, self.table)?;
|
||||
Ok(Value::Table(table))
|
||||
}
|
||||
}
|
||||
@@ -465,7 +500,14 @@ impl<'lua> ser::SerializeMap for SerializeMap<'lua> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> ser::SerializeStruct for SerializeMap<'lua> {
|
||||
#[doc(hidden)]
|
||||
pub struct SerializeStruct<'lua> {
|
||||
lua: &'lua Lua,
|
||||
inner: Option<Value<'lua>>,
|
||||
options: Options,
|
||||
}
|
||||
|
||||
impl<'lua> ser::SerializeStruct for SerializeStruct<'lua> {
|
||||
type Ok = Value<'lua>;
|
||||
type Error = Error;
|
||||
|
||||
@@ -473,18 +515,44 @@ impl<'lua> ser::SerializeStruct for SerializeMap<'lua> {
|
||||
where
|
||||
T: Serialize + ?Sized,
|
||||
{
|
||||
ser::SerializeMap::serialize_key(self, key)?;
|
||||
ser::SerializeMap::serialize_value(self, value)
|
||||
match self.inner {
|
||||
Some(Value::Table(ref table)) => {
|
||||
table.raw_set(key, self.lua.to_value_with(value, self.options)?)?;
|
||||
}
|
||||
None if self.options.detect_serde_json_arbitrary_precision => {
|
||||
// A special case for `serde_json::Number` with arbitrary precision.
|
||||
assert_eq!(key, "$serde_json::private::Number");
|
||||
self.inner = Some(self.lua.to_value_with(value, self.options)?);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn end(self) -> Result<Value<'lua>> {
|
||||
ser::SerializeMap::end(self)
|
||||
match self.inner {
|
||||
Some(table @ Value::Table(_)) => Ok(table),
|
||||
Some(value) if self.options.detect_serde_json_arbitrary_precision => {
|
||||
let number_s = value.as_str().expect("not an arbitrary precision number");
|
||||
if number_s.contains(['.', 'e', 'E']) {
|
||||
if let Ok(number) = number_s.parse().map(Value::Number) {
|
||||
return Ok(number);
|
||||
}
|
||||
}
|
||||
Ok(number_s
|
||||
.parse()
|
||||
.map(Value::Integer)
|
||||
.or_else(|_| number_s.parse().map(Value::Number))
|
||||
.unwrap_or(value))
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub struct SerializeStructVariant<'lua> {
|
||||
name: String<'lua>,
|
||||
variant: &'static str,
|
||||
table: Table<'lua>,
|
||||
options: Options,
|
||||
}
|
||||
@@ -505,8 +573,8 @@ impl<'lua> ser::SerializeStructVariant for SerializeStructVariant<'lua> {
|
||||
|
||||
fn end(self) -> Result<Value<'lua>> {
|
||||
let lua = self.table.0.lua;
|
||||
let table = lua.create_table()?;
|
||||
table.raw_set(self.name, self.table)?;
|
||||
let table = lua.create_table_with_capacity(0, 1)?;
|
||||
table.raw_set(self.variant, self.table)?;
|
||||
Ok(Value::Table(table))
|
||||
}
|
||||
}
|
||||
|
||||
+5
-5
@@ -1089,7 +1089,7 @@ impl<'a, 'lua> Serialize for SerializableTable<'a, 'lua> {
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
use crate::serde::de::{check_value_for_skip, MapPairs};
|
||||
use crate::serde::de::{check_value_for_skip, MapPairs, RecursionGuard};
|
||||
use crate::value::SerializableValue;
|
||||
|
||||
let convert_result = |res: Result<()>, serialize_err: Option<S::Error>| match res {
|
||||
@@ -1101,7 +1101,7 @@ impl<'a, 'lua> Serialize for SerializableTable<'a, 'lua> {
|
||||
|
||||
let options = self.options;
|
||||
let visited = &self.visited;
|
||||
visited.borrow_mut().insert(self.table.to_pointer());
|
||||
let _guard = RecursionGuard::new(self.table, visited);
|
||||
|
||||
// Array
|
||||
let len = self.table.raw_len();
|
||||
@@ -1109,7 +1109,7 @@ impl<'a, 'lua> Serialize for SerializableTable<'a, 'lua> {
|
||||
let mut seq = serializer.serialize_seq(Some(len))?;
|
||||
let mut serialize_err = None;
|
||||
let res = self.table.for_each_value::<Value>(|value| {
|
||||
let skip = check_value_for_skip(&value, self.options, &self.visited)
|
||||
let skip = check_value_for_skip(&value, self.options, visited)
|
||||
.map_err(|err| Error::SerializeError(err.to_string()))?;
|
||||
if skip {
|
||||
// continue iteration
|
||||
@@ -1129,9 +1129,9 @@ impl<'a, 'lua> Serialize for SerializableTable<'a, 'lua> {
|
||||
let mut map = serializer.serialize_map(None)?;
|
||||
let mut serialize_err = None;
|
||||
let mut process_pair = |key, value| {
|
||||
let skip_key = check_value_for_skip(&key, self.options, &self.visited)
|
||||
let skip_key = check_value_for_skip(&key, self.options, visited)
|
||||
.map_err(|err| Error::SerializeError(err.to_string()))?;
|
||||
let skip_value = check_value_for_skip(&value, self.options, &self.visited)
|
||||
let skip_value = check_value_for_skip(&value, self.options, visited)
|
||||
.map_err(|err| Error::SerializeError(err.to_string()))?;
|
||||
if skip_key || skip_value {
|
||||
// continue iteration
|
||||
|
||||
+19
-10
@@ -54,8 +54,8 @@ pub enum MetaMethod {
|
||||
/// The unary minus (`-`) operator.
|
||||
Unm,
|
||||
/// The floor division (//) operator.
|
||||
/// Requires `feature = "lua54/lua53"`
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
/// Requires `feature = "lua54/lua53/luau"`
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "luau"))]
|
||||
IDiv,
|
||||
/// The bitwise AND (&) operator.
|
||||
/// Requires `feature = "lua54/lua53"`
|
||||
@@ -180,7 +180,7 @@ impl MetaMethod {
|
||||
MetaMethod::Pow => "__pow",
|
||||
MetaMethod::Unm => "__unm",
|
||||
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "luau"))]
|
||||
MetaMethod::IDiv => "__idiv",
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
MetaMethod::BAnd => "__band",
|
||||
@@ -815,11 +815,7 @@ impl OwnedAnyUserData {
|
||||
impl<'lua> AnyUserData<'lua> {
|
||||
/// Checks whether the type of this userdata is `T`.
|
||||
pub fn is<T: 'static>(&self) -> bool {
|
||||
match self.inspect(|_: &UserDataCell<T>| Ok(())) {
|
||||
Ok(()) => true,
|
||||
Err(Error::UserDataTypeMismatch) => false,
|
||||
Err(_) => unreachable!(),
|
||||
}
|
||||
self.inspect(|_: &UserDataCell<T>| Ok(())).is_ok()
|
||||
}
|
||||
|
||||
/// Borrow this userdata immutably if it is of type `T`.
|
||||
@@ -1344,6 +1340,19 @@ impl<'lua> Serialize for AnyUserData<'lua> {
|
||||
S: Serializer,
|
||||
{
|
||||
let lua = self.0.lua;
|
||||
|
||||
// Special case for Luau buffer type
|
||||
#[cfg(feature = "luau")]
|
||||
if self.1 == SubtypeId::Buffer {
|
||||
let buf = unsafe {
|
||||
let mut size = 0usize;
|
||||
let buf = ffi::lua_tobuffer(lua.ref_thread(), self.0.index, &mut size);
|
||||
mlua_assert!(!buf.is_null(), "invalid Luau buffer");
|
||||
std::slice::from_raw_parts(buf as *const u8, size)
|
||||
};
|
||||
return serializer.serialize_bytes(buf);
|
||||
}
|
||||
|
||||
let data = unsafe {
|
||||
let _ = lua
|
||||
.get_userdata_ref_type_id(&self.0)
|
||||
@@ -1362,7 +1371,7 @@ impl<'lua> Serialize for AnyUserData<'lua> {
|
||||
/// A wrapper type for an immutably borrowed value from a `AnyUserData`.
|
||||
///
|
||||
/// It implements [`FromLua`] and can be used to receive a typed userdata from Lua.
|
||||
pub struct UserDataRef<'lua, T: 'static>(AnyUserData<'lua>, Ref<'lua, T>);
|
||||
pub struct UserDataRef<'lua, T: 'static>(#[allow(unused)] AnyUserData<'lua>, Ref<'lua, T>);
|
||||
|
||||
impl<'lua, T: 'static> Deref for UserDataRef<'lua, T> {
|
||||
type Target = T;
|
||||
@@ -1384,7 +1393,7 @@ impl<'lua, T: 'static> UserDataRef<'lua, T> {
|
||||
/// A wrapper type for a mutably borrowed value from a `AnyUserData`.
|
||||
///
|
||||
/// It implements [`FromLua`] and can be used to receive a typed userdata from Lua.
|
||||
pub struct UserDataRefMut<'lua, T: 'static>(AnyUserData<'lua>, RefMut<'lua, T>);
|
||||
pub struct UserDataRefMut<'lua, T: 'static>(#[allow(unused)] AnyUserData<'lua>, RefMut<'lua, T>);
|
||||
|
||||
impl<'lua, T: 'static> Deref for UserDataRefMut<'lua, T> {
|
||||
type Target = T;
|
||||
|
||||
+15
-1
@@ -253,6 +253,20 @@ pub unsafe fn push_string(state: *mut ffi::lua_State, s: &[u8], protect: bool) -
|
||||
}
|
||||
}
|
||||
|
||||
// Uses 3 stack spaces (when protect), does not call checkstack.
|
||||
#[cfg(feature = "luau")]
|
||||
#[inline(always)]
|
||||
pub unsafe fn push_buffer(state: *mut ffi::lua_State, b: &[u8], protect: bool) -> Result<()> {
|
||||
let data = if protect {
|
||||
protect_lua!(state, 0, 1, |state| ffi::lua_newbuffer(state, b.len()))?
|
||||
} else {
|
||||
ffi::lua_newbuffer(state, b.len())
|
||||
};
|
||||
let buf = slice::from_raw_parts_mut(data as *mut u8, b.len());
|
||||
buf.copy_from_slice(b);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Uses 3 stack spaces, does not call checkstack.
|
||||
#[inline]
|
||||
pub unsafe fn push_table(
|
||||
@@ -928,7 +942,7 @@ pub unsafe fn init_error_registry(state: *mut ffi::lua_State) -> Result<()> {
|
||||
"__mod",
|
||||
"__pow",
|
||||
"__unm",
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "luau"))]
|
||||
"__idiv",
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
"__band",
|
||||
|
||||
+4
-6
@@ -1,7 +1,7 @@
|
||||
use std::borrow::Cow;
|
||||
use std::cmp::Ordering;
|
||||
use std::collections::HashSet;
|
||||
use std::iter::{self, FromIterator};
|
||||
use std::iter;
|
||||
use std::ops::Index;
|
||||
use std::os::raw::{c_int, c_void};
|
||||
use std::string::String as StdString;
|
||||
@@ -15,7 +15,7 @@ use {
|
||||
crate::table::SerializableTable,
|
||||
rustc_hash::FxHashSet,
|
||||
serde::ser::{self, Serialize, Serializer},
|
||||
std::{cell::RefCell, convert::TryInto, rc::Rc, result::Result as StdResult},
|
||||
std::{cell::RefCell, rc::Rc, result::Result as StdResult},
|
||||
};
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
@@ -252,8 +252,7 @@ impl<'lua> Value<'lua> {
|
||||
/// If the value is a Lua [`Integer`], try to convert it to `i64` or return `None` otherwise.
|
||||
#[inline]
|
||||
pub fn as_i64(&self) -> Option<i64> {
|
||||
#[allow(clippy::useless_conversion)]
|
||||
self.as_integer().and_then(|i| i64::try_from(i).ok())
|
||||
self.as_integer().map(i64::from)
|
||||
}
|
||||
|
||||
/// Cast the value to `u64`.
|
||||
@@ -659,8 +658,7 @@ impl<'a, 'lua> Serialize for SerializableValue<'a, 'lua> {
|
||||
Value::Nil => serializer.serialize_unit(),
|
||||
Value::Boolean(b) => serializer.serialize_bool(*b),
|
||||
#[allow(clippy::useless_conversion)]
|
||||
Value::Integer(i) => serializer
|
||||
.serialize_i64((*i).try_into().expect("cannot convert Lua Integer to i64")),
|
||||
Value::Integer(i) => serializer.serialize_i64((*i).into()),
|
||||
Value::Number(n) => serializer.serialize_f64(*n),
|
||||
#[cfg(feature = "luau")]
|
||||
Value::Vector(v) => v.serialize(serializer),
|
||||
|
||||
+76
-6
@@ -2,6 +2,7 @@ use std::borrow::Cow;
|
||||
use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
|
||||
use std::ffi::{CStr, CString};
|
||||
|
||||
use bstr::BString;
|
||||
use maplit::{btreemap, btreeset, hashmap, hashset};
|
||||
use mlua::{
|
||||
AnyUserData, Error, Function, IntoLua, Lua, RegistryKey, Result, Table, Thread, UserDataRef,
|
||||
@@ -232,14 +233,22 @@ fn test_owned_anyuserdata_into_lua() -> Result<()> {
|
||||
fn test_registry_value_into_lua() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let t = lua.create_table()?;
|
||||
let r = lua.create_registry_value(t)?;
|
||||
let f = lua.create_function(|_, t: Table| t.raw_set("hello", "world"))?;
|
||||
// Direct conversion
|
||||
let s = lua.create_string("hello, world")?;
|
||||
let r = lua.create_registry_value(&s)?;
|
||||
let value1 = lua.pack(&r)?;
|
||||
let value2 = lua.pack(r)?;
|
||||
assert_eq!(value1.as_str(), Some("hello, world"));
|
||||
assert_eq!(value2.to_pointer(), value2.to_pointer());
|
||||
|
||||
f.call(&r)?;
|
||||
let v = r.into_lua(&lua)?;
|
||||
let t = v.as_table().unwrap();
|
||||
// Push into stack
|
||||
let t = lua.create_table()?;
|
||||
let r = lua.create_registry_value(&t)?;
|
||||
let f = lua.create_function(|_, (t, k, v): (Table, Value, Value)| t.set(k, v))?;
|
||||
f.call((&r, "hello", "world"))?;
|
||||
f.call((r, "welcome", "to the jungle"))?;
|
||||
assert_eq!(t.get::<_, String>("hello")?, "world");
|
||||
assert_eq!(t.get::<_, String>("welcome")?, "to the jungle");
|
||||
|
||||
// Try to set nil registry key
|
||||
let r_nil = lua.create_registry_value(Value::Nil)?;
|
||||
@@ -401,3 +410,64 @@ fn test_conv_array() -> Result<()> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_bstring_from_lua() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let s = lua.create_string("hello, world")?;
|
||||
let bstr = lua.unpack::<BString>(Value::String(s))?;
|
||||
assert_eq!(bstr, "hello, world");
|
||||
|
||||
let bstr = lua.unpack::<BString>(Value::Integer(123))?;
|
||||
assert_eq!(bstr, "123");
|
||||
|
||||
let bstr = lua.unpack::<BString>(Value::Number(-123.55))?;
|
||||
assert_eq!(bstr, "-123.55");
|
||||
|
||||
// Test from stack
|
||||
let f = lua.create_function(|_, bstr: BString| Ok(bstr))?;
|
||||
let bstr = f.call::<_, BString>("hello, world")?;
|
||||
assert_eq!(bstr, "hello, world");
|
||||
|
||||
let bstr = f.call::<_, BString>(-43.22)?;
|
||||
assert_eq!(bstr, "-43.22");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
#[test]
|
||||
fn test_bstring_from_lua_buffer() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let b = lua.create_buffer("hello, world")?;
|
||||
let bstr = lua.unpack::<BString>(Value::UserData(b))?;
|
||||
assert_eq!(bstr, "hello, world");
|
||||
|
||||
// Test from stack
|
||||
let f = lua.create_function(|_, bstr: BString| Ok(bstr))?;
|
||||
let buf = lua.create_buffer("hello, world")?;
|
||||
let bstr = f.call::<_, BString>(buf)?;
|
||||
assert_eq!(bstr, "hello, world");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_option_into_from_lua() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
// Direct conversion
|
||||
let v = Some(42);
|
||||
let v2 = v.into_lua(&lua)?;
|
||||
assert_eq!(v, v2.as_i32());
|
||||
|
||||
// Push into stack / get from stack
|
||||
let f = lua.create_function(|_, v: Option<i32>| Ok(v))?;
|
||||
assert_eq!(f.call::<_, Option<i32>>(Some(42))?, Some(42));
|
||||
assert_eq!(f.call::<_, Option<i32>>(Option::<i32>::None)?, None);
|
||||
assert_eq!(f.call::<_, Option<i32>>(())?, None);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -244,6 +244,27 @@ fn test_function_pointer() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
#[test]
|
||||
fn test_function_deep_clone() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
lua.globals().set("a", 1)?;
|
||||
let func1 = lua.load("a += 1; return a").into_function()?;
|
||||
let func2 = func1.deep_clone();
|
||||
|
||||
assert_ne!(func1.to_pointer(), func2.to_pointer());
|
||||
assert_eq!(func1.call::<_, i32>(())?, 2);
|
||||
assert_eq!(func2.call::<_, i32>(())?, 3);
|
||||
|
||||
// Check that for Rust functions deep_clone is just a clone
|
||||
let rust_func = lua.create_function(|_, ()| Ok(42))?;
|
||||
let rust_func2 = rust_func.deep_clone();
|
||||
assert_eq!(rust_func.to_pointer(), rust_func2.to_pointer());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_function_wrap() -> Result<()> {
|
||||
use mlua::Error;
|
||||
|
||||
@@ -484,3 +484,9 @@ fn test_buffer() -> Result<()> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fflags() {
|
||||
// We cannot really on any particular feature flag to be present
|
||||
assert!(Lua::set_fflag("UnknownFlag", true).is_err());
|
||||
}
|
||||
|
||||
+1
-1
@@ -84,7 +84,7 @@ fn test_gc_control() -> Result<()> {
|
||||
|
||||
assert_eq!(lua.gc_inc(200, 100, 13), GCMode::Incremental);
|
||||
|
||||
struct MyUserdata(Arc<()>);
|
||||
struct MyUserdata(#[allow(unused)] Arc<()>);
|
||||
impl UserData for MyUserdata {}
|
||||
|
||||
let rc = Arc::new(());
|
||||
|
||||
+10
-10
@@ -155,14 +155,14 @@ fn test_scope_userdata_functions() -> Result<()> {
|
||||
|
||||
impl<'a> UserData for MyUserData<'a> {
|
||||
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
methods.add_meta_function(MetaMethod::Add, |lua, ()| {
|
||||
methods.add_meta_method(MetaMethod::Add, |lua, this, ()| {
|
||||
let globals = lua.globals();
|
||||
globals.set("i", globals.get::<_, i64>("i")? + 1)?;
|
||||
globals.set("i", globals.get::<_, i64>("i")? + this.0)?;
|
||||
Ok(())
|
||||
});
|
||||
methods.add_meta_function(MetaMethod::Sub, |lua, ()| {
|
||||
methods.add_meta_method(MetaMethod::Sub, |lua, this, ()| {
|
||||
let globals = lua.globals();
|
||||
globals.set("i", globals.get::<_, i64>("i")? + 1)?;
|
||||
globals.set("i", globals.get::<_, i64>("i")? + this.0)?;
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
@@ -170,7 +170,7 @@ fn test_scope_userdata_functions() -> Result<()> {
|
||||
|
||||
let lua = Lua::new();
|
||||
|
||||
let dummy = 0;
|
||||
let dummy = 1;
|
||||
let f = lua
|
||||
.load(
|
||||
r#"
|
||||
@@ -178,7 +178,7 @@ fn test_scope_userdata_functions() -> Result<()> {
|
||||
return function(u)
|
||||
_ = u + u
|
||||
_ = u - 1
|
||||
_ = 1 + u
|
||||
_ = u + 1
|
||||
end
|
||||
"#,
|
||||
)
|
||||
@@ -257,7 +257,7 @@ fn test_scope_userdata_mismatch() -> Result<()> {
|
||||
fn test_scope_userdata_drop() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
struct MyUserData(Rc<()>);
|
||||
struct MyUserData(#[allow(unused)] Rc<()>);
|
||||
|
||||
impl UserData for MyUserData {
|
||||
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
@@ -265,7 +265,7 @@ fn test_scope_userdata_drop() -> Result<()> {
|
||||
}
|
||||
}
|
||||
|
||||
struct MyUserDataArc(Arc<()>);
|
||||
struct MyUserDataArc(#[allow(unused)] Arc<()>);
|
||||
|
||||
impl UserData for MyUserDataArc {}
|
||||
|
||||
@@ -315,7 +315,7 @@ fn test_scope_userdata_drop() -> Result<()> {
|
||||
fn test_scope_nonstatic_userdata_drop() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
struct MyUserData<'a>(&'a Cell<i64>, Arc<()>);
|
||||
struct MyUserData<'a>(&'a Cell<i64>, #[allow(unused)] Arc<()>);
|
||||
|
||||
impl<'a> UserData for MyUserData<'a> {
|
||||
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
@@ -326,7 +326,7 @@ fn test_scope_nonstatic_userdata_drop() -> Result<()> {
|
||||
}
|
||||
}
|
||||
|
||||
struct MyUserDataArc(Arc<()>);
|
||||
struct MyUserDataArc(#[allow(unused)] Arc<()>);
|
||||
|
||||
impl UserData for MyUserDataArc {}
|
||||
|
||||
|
||||
+95
-1
@@ -99,7 +99,7 @@ fn test_serialize_in_scope() -> LuaResult<()> {
|
||||
Err(e) => panic!("expected destructed error, got {}", e),
|
||||
}
|
||||
|
||||
struct MyUserDataRef<'a>(&'a ());
|
||||
struct MyUserDataRef<'a>(#[allow(unused)] &'a ());
|
||||
|
||||
impl<'a> UserData for MyUserDataRef<'a> {}
|
||||
|
||||
@@ -115,6 +115,21 @@ fn test_serialize_in_scope() -> LuaResult<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_serialize_any_userdata() -> Result<(), Box<dyn StdError>> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let json_val = serde_json::json!({
|
||||
"a": 1,
|
||||
"b": "test",
|
||||
});
|
||||
let json_ud = lua.create_ser_any_userdata(json_val)?;
|
||||
let json_str = serde_json::to_string_pretty(&json_ud)?;
|
||||
assert_eq!(json_str, "{\n \"a\": 1,\n \"b\": \"test\"\n}");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_serialize_failure() -> Result<(), Box<dyn StdError>> {
|
||||
#[derive(Serialize)]
|
||||
@@ -254,6 +269,27 @@ fn test_serialize_globals() -> LuaResult<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_serialize_same_table_twice() -> LuaResult<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let value = lua
|
||||
.load(
|
||||
r#"
|
||||
local foo = {}
|
||||
return {
|
||||
a = foo,
|
||||
b = foo,
|
||||
}
|
||||
"#,
|
||||
)
|
||||
.eval::<Value>()?;
|
||||
let json = serde_json::to_string(&value.to_serializable().sort_keys(true)).unwrap();
|
||||
assert_eq!(json, r#"{"a":{},"b":{}}"#);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_to_value_struct() -> LuaResult<()> {
|
||||
let lua = Lua::new();
|
||||
@@ -697,3 +733,61 @@ fn test_from_value_sorted() -> Result<(), Box<dyn StdError>> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_arbitrary_precision() {
|
||||
let lua = Lua::new();
|
||||
|
||||
let opts = SerializeOptions::new().detect_serde_json_arbitrary_precision(true);
|
||||
|
||||
// Number
|
||||
let num = serde_json::Value::Number(serde_json::Number::from_f64(1.244e2).unwrap());
|
||||
let num = lua.to_value_with(&num, opts).unwrap();
|
||||
assert_eq!(num, Value::Number(1.244e2));
|
||||
|
||||
// Integer
|
||||
let num = serde_json::Value::Number(serde_json::Number::from_f64(123.0).unwrap());
|
||||
let num = lua.to_value_with(&num, opts).unwrap();
|
||||
assert_eq!(num, Value::Integer(123));
|
||||
|
||||
// Max u64
|
||||
let num = serde_json::Value::Number(serde_json::Number::from(i64::MAX));
|
||||
let num = lua.to_value_with(&num, opts).unwrap();
|
||||
assert_eq!(num, Value::Number(i64::MAX as f64));
|
||||
|
||||
// Check that the option is disabled by default
|
||||
let num = serde_json::Value::Number(serde_json::Number::from_f64(1.244e2).unwrap());
|
||||
let num = lua.to_value(&num).unwrap();
|
||||
assert_eq!(num.type_name(), "table");
|
||||
assert_eq!(
|
||||
format!("{:#?}", num),
|
||||
"{\n [\"$serde_json::private::Number\"] = \"124.4\",\n}"
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
#[test]
|
||||
fn test_buffer_serialize() {
|
||||
let lua = Lua::new();
|
||||
|
||||
let buf = lua.create_buffer(&[1, 2, 3, 4]).unwrap();
|
||||
let val = serde_value::to_value(&buf).unwrap();
|
||||
assert_eq!(val, serde_value::Value::Bytes(vec![1, 2, 3, 4]));
|
||||
|
||||
// Try empty buffer
|
||||
let buf = lua.create_buffer(&[]).unwrap();
|
||||
let val = serde_value::to_value(&buf).unwrap();
|
||||
assert_eq!(val, serde_value::Value::Bytes(vec![]));
|
||||
}
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
#[test]
|
||||
fn test_buffer_from_value() {
|
||||
let lua = Lua::new();
|
||||
|
||||
let buf = lua.create_buffer(&[1, 2, 3, 4]).unwrap();
|
||||
let val = lua
|
||||
.from_value::<serde_value::Value>(Value::UserData(buf))
|
||||
.unwrap();
|
||||
assert_eq!(val, serde_value::Value::Bytes(vec![1, 2, 3, 4]));
|
||||
}
|
||||
|
||||
+1
-1
@@ -743,7 +743,7 @@ fn test_registry_value() -> Result<()> {
|
||||
|
||||
#[test]
|
||||
fn test_drop_registry_value() -> Result<()> {
|
||||
struct MyUserdata(Arc<()>);
|
||||
struct MyUserdata(#[allow(unused)] Arc<()>);
|
||||
|
||||
impl UserData for MyUserdata {}
|
||||
|
||||
|
||||
+1
-1
@@ -101,7 +101,7 @@ fn test_thread_reset() -> Result<()> {
|
||||
|
||||
let lua = Lua::new();
|
||||
|
||||
struct MyUserData(Arc<()>);
|
||||
struct MyUserData(#[allow(unused)] Arc<()>);
|
||||
impl UserData for MyUserData {}
|
||||
|
||||
let arc = Arc::new(());
|
||||
|
||||
+3
-1
@@ -370,6 +370,8 @@ fn test_userdata_take() -> Result<()> {
|
||||
r => panic!("improper return for destructed userdata: {:?}", r),
|
||||
}
|
||||
|
||||
assert!(!userdata.is::<MyUserdata>());
|
||||
|
||||
drop(userdata);
|
||||
lua.globals().raw_remove("userdata")?;
|
||||
lua.gc_collect()?;
|
||||
@@ -400,7 +402,7 @@ fn test_userdata_take() -> Result<()> {
|
||||
|
||||
#[test]
|
||||
fn test_userdata_destroy() -> Result<()> {
|
||||
struct MyUserdata(Arc<()>);
|
||||
struct MyUserdata(#[allow(unused)] Arc<()>);
|
||||
|
||||
impl UserData for MyUserdata {}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user