Compare commits

...

20 Commits

Author SHA1 Message Date
Alex Orlenko 806bd202d6 v0.9.7 2024-04-05 12:46:12 +01:00
Alex Orlenko a644049087 Cosmetic changes for clippy 2024-04-05 12:44:18 +01:00
Alex Orlenko ad31bed1db Minor improvements in serializing:
Use `&str` instead of creating Lua string when serializing tuple variant and struct variant
2024-04-05 12:44:08 +01:00
Alex Orlenko 62f0bb97b0 Add Lua::create_ser_any_userdata() function 2024-04-05 12:03:40 +01:00
Alex Orlenko f67f8646ae Implement push_into_stack/from_stack for Option<T> 2024-03-28 18:18:08 +00:00
Alex Orlenko fa217d3706 Better Luau buffer type support.
- Add `Lua::create_buffer()` function
- Support serializing buffer type as a byte slice
- Support accessing copy of underlying bytes using `BString`
2024-03-28 13:05:01 +00:00
Yiyu Lin b62f2ee0f7 chore: make clippy happy (#388)
Co-authored-by: hzlinyiyu <hzlinyiyu@corp.netease.com>
2024-03-25 13:26:06 +00:00
Alex Orlenko 6e6c73e4c7 Add deserialize json benchmark 2024-03-23 22:31:42 +00:00
Alex Orlenko 508517c45e Do not use dependencies as implicit features.
Add `dep:` prefix instead.
2024-03-23 19:47:42 +00:00
Alex Orlenko a79840afc9 Suppress Rust 1.77 dead_code false warnings. 2024-03-23 11:42:46 +00:00
Alex Orlenko 39afe4c6f7 Add SerializeOptions::detect_serde_json_arbitrary_precision to detect serde_json::Number with arbitrary_precision and convert it to Lua number.
By default the option is disabled and such numbers represented as Lua objects with `$serde_json::private::Number` key.
Fixes #385
2024-03-23 11:27:46 +00:00
Alex Orlenko 038cc5f974 Bump rustyline to 14.0 2024-03-22 00:40:21 +00:00
Alex Orlenko 59b14000f3 Update hyper examples 2024-03-22 00:35:37 +00:00
19年梦醒 849206ef9d update hyper to v1, and add shell command example (#384)
Co-authored-by: Alex Orlenko <zxteam@protonmail.com>
2024-03-21 23:06:00 +00:00
Alex Orlenko 80fff4f2e7 Update reqwest to 0.12 2024-03-21 22:29:34 +00:00
Alex Orlenko 9734146313 Add Function::deep_clone() (Luau only) 2024-03-21 22:14:00 +00:00
Alex Orlenko 58be624222 Remove redundant "match" when checking userdata type via AnyUserData::is.
Fixes #386
2024-03-20 19:29:47 +00:00
Alex Orlenko 3d43103431 Make __idiv metamethod available for luau
Closes #383
2024-03-20 19:20:05 +00:00
Alex Orlenko 5a22437d5f Assert that luau_compile returns non-null pointer.
Fixes #381
2024-03-16 23:39:10 +00:00
Alex Orlenko 83c075c72b Implement IntoLua for RegistryKey 2024-02-29 22:45:32 +00:00
30 changed files with 632 additions and 169 deletions
+11
View File
@@ -1,3 +1,14 @@
## 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`
+11 -9
View File
@@ -1,6 +1,6 @@
[package]
name = "mlua"
version = "0.9.6" # remember to update mlua_derive
version = "0.9.7" # remember to update mlua_derive
authors = ["Aleksandr Orlenko <zxteam@pm.me>", "kyren <catherine@kyju.org>"]
rust-version = "1.71"
edition = "2021"
@@ -32,14 +32,14 @@ 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 = []
@@ -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]]
+16 -2
View File
@@ -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.7", 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.7", features = ["lua54", "module"] }
```
`lib.rs` :
+2 -2
View File
@@ -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
View File
@@ -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);
+15 -11
View File
@@ -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()?;
+7 -13
View File
@@ -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()?;
+74 -54
View File
@@ -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);
-1
View File
@@ -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;
-1
View File
@@ -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;
+1
View File
@@ -36,6 +36,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
View File
@@ -1,7 +1,6 @@
use std::{
cmp::{Eq, PartialEq},
fmt::{self, Display, Formatter},
iter::IntoIterator,
vec::IntoIter,
};
+85 -16
View File
@@ -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)?))
}
}
}
+21
View File
@@ -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")))))]
+36
View File
@@ -1373,6 +1373,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 +1746,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`.
+1
View File
@@ -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
View File
@@ -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;
+9 -4
View File
@@ -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(_)
+83 -15
View File
@@ -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))
}
}
+19 -10
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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(())
}
+21
View File
@@ -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;
+1 -1
View File
@@ -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
View File
@@ -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 {}
+74 -1
View File
@@ -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)]
@@ -697,3 +712,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
View File
@@ -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
View File
@@ -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
View File
@@ -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 {}