Skip to content

Commit afc41ab

Browse files
committed
Add serialization example & update others
1 parent c5d0ccc commit afc41ab

File tree

9 files changed

+126
-18
lines changed

9 files changed

+126
-18
lines changed

Cargo.toml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,8 @@ High level bindings to Lua 5.4/5.3/5.2/5.1 (including LuaJIT)
1616
with async/await features and support of writing native lua modules in Rust.
1717
"""
1818

19-
[badges]
20-
# github-actions = { repository = "khvzak/mlua", workflow = "CI" }
21-
maintenance = { status = "actively-developed" }
22-
2319
[package.metadata.docs.rs]
24-
features = ["async", "send", "lua53"]
20+
features = ["lua53", "async", "send", "serialize"]
2521

2622
[workspace]
2723
members = [
@@ -88,3 +84,7 @@ required-features = ["async", "send"]
8884
[[example]]
8985
name = "async_tcp_server"
9086
required-features = ["async"]
87+
88+
[[example]]
89+
name = "serialize"
90+
required-features = ["serialize"]

README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,20 @@ Starting from v0.3, `mlua` supports async/await for all Lua versions. This works
3333

3434
**Examples**:
3535
- [HTTP Client](examples/async_http_client.rs)
36+
- [HTTP Client (json)](examples/async_http_reqwest.rs)
3637
- [HTTP Server](examples/async_http_server.rs)
3738
- [TCP Server](examples/async_tcp_server.rs)
3839

40+
### Serialization (serde) support
41+
42+
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 user data support).
43+
44+
[Example](examples/serialize.rs)
45+
46+
[`serde::Serialize`]: https://docs.serde.rs/serde/ser/trait.Serialize.html
47+
[`serde::Deserialize`]: https://docs.serde.rs/serde/de/trait.Deserialize.html
48+
[`mlua::Value`]: https://docs.rs/mlua/latest/mlua/enum.Value.html
49+
3950
### Compiling
4051

4152
You have to enable one of the features `lua54`, `lua53`, `lua52`, `lua51` or `luajit`, according to the choosen Lua version.

benches/benchmark.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55

66
#[cfg_attr(
77
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
8-
link_args = "-pagezero_size 10000 -image_base 100000000"
8+
link_args = "-pagezero_size 10000 -image_base 100000000",
9+
allow(unused_attributes)
910
)]
1011
extern "system" {}
1112

examples/async_http_client.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ async fn main() -> Result<()> {
6161
let f = lua
6262
.load(
6363
r#"
64-
local res = fetch_url(...);
64+
local res = fetch_url(...)
6565
print(res.status)
6666
for key, vals in pairs(res.headers) do
6767
for _, val in ipairs(vals) do

examples/async_tcp_server.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ impl UserData for LuaTcpStream {
6363

6464
async fn run_server(lua: &'static Lua) -> Result<()> {
6565
let spawn = lua.create_function(move |_, func: Function| {
66-
task::spawn_local(async move { func.call_async::<_, ()>(()).await.unwrap() });
66+
task::spawn_local(async move { func.call_async::<_, ()>(()).await });
6767
Ok(())
6868
})?;
6969

examples/guided_tour.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,33 @@ fn main() -> Result<()> {
164164
< f32::EPSILON
165165
);
166166

167+
// Normally, Rust types passed to `Lua` must be `'static`, because there is no way to be
168+
// sure of their lifetime inside the Lua state. There is, however, a limited way to lift this
169+
// requirement. You can call `Lua::scope` to create userdata and callbacks types that only live
170+
// for as long as the call to scope, but do not have to be `'static` (and `Send`).
171+
172+
{
173+
let mut rust_val = 0;
174+
175+
lua.scope(|scope| {
176+
// We create a 'sketchy' Lua callback that holds a mutable reference to the variable
177+
// `rust_val`. Outside of a `Lua::scope` call, this would not be allowed
178+
// because it could be unsafe.
179+
180+
lua.globals().set(
181+
"sketchy",
182+
scope.create_function_mut(|_, ()| {
183+
rust_val = 42;
184+
Ok(())
185+
})?,
186+
)?;
187+
188+
lua.load("sketchy()").exec()
189+
})?;
190+
191+
assert_eq!(rust_val, 42);
192+
}
193+
167194
// We were able to run our 'sketchy' function inside the scope just fine. However, if we
168195
// try to run our 'sketchy' function outside of the scope, the function we created will have
169196
// been invalidated and we will generate an error. If our function wasn't invalidated, we

examples/serialize.rs

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
use mlua::{Error, Lua, LuaSerdeExt, Result, UserData, Value};
2+
use serde::{Deserialize, Serialize};
3+
4+
#[derive(Serialize, Deserialize)]
5+
enum Transmission {
6+
Manual,
7+
Automatic,
8+
}
9+
10+
#[derive(Serialize, Deserialize)]
11+
struct Engine {
12+
v: u32,
13+
kw: u32,
14+
}
15+
16+
#[derive(Serialize, Deserialize)]
17+
struct Car {
18+
active: bool,
19+
model: String,
20+
transmission: Transmission,
21+
engine: Engine,
22+
}
23+
24+
impl UserData for Car {}
25+
26+
fn main() -> Result<()> {
27+
let lua = Lua::new();
28+
let globals = lua.globals();
29+
30+
// Create Car struct from a Lua table
31+
let car: Car = lua.from_value(lua.load(r#"
32+
{active = true, model = "Volkswagen Golf", transmission = "Automatic", engine = {v = 1499, kw = 90}}
33+
"#).eval()?)?;
34+
35+
// Set it as (serializable) userdata
36+
globals.set("null", lua.null()?)?;
37+
globals.set("array_mt", lua.array_metatable()?)?;
38+
globals.set("car", lua.create_ser_userdata(car)?)?;
39+
40+
// Create a Lua table with multiple data types
41+
let val: Value = lua
42+
.load(r#"{driver = "Boris", car = car, price = null, points = setmetatable({}, array_mt)}"#)
43+
.eval()?;
44+
45+
// Serialize the table above to JSON
46+
let json_str = serde_json::to_string(&val).map_err(Error::external)?;
47+
println!("{}", json_str);
48+
49+
// Create Lua Value from JSON (or any serializable type)
50+
let json = serde_json::json!({
51+
"key": "value",
52+
"null": null,
53+
"array": [],
54+
});
55+
globals.set("json_value", lua.to_value(&json)?)?;
56+
lua.load(
57+
r#"
58+
assert(json_value["key"] == "value")
59+
assert(json_value["null"] == null)
60+
assert(#(json_value["array"]) == 0)
61+
"#,
62+
)
63+
.exec()?;
64+
65+
Ok(())
66+
}

src/lua.rs

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,6 @@ use std::os::raw::{c_char, c_int, c_void};
77
use std::sync::{Arc, Mutex, Weak};
88
use std::{mem, ptr, str};
99

10-
#[cfg(feature = "serialize")]
11-
use serde::Serialize;
12-
1310
use crate::error::{Error, Result};
1411
use crate::ffi;
1512
use crate::function::Function;
@@ -25,11 +22,10 @@ use crate::types::{
2522
};
2623
use crate::userdata::{AnyUserData, MetaMethod, UserData, UserDataMethods, UserDataWrapped};
2724
use crate::util::{
28-
assert_stack, callback_error, check_stack, get_destructed_userdata_metatable, get_gc_userdata,
29-
get_main_state, get_meta_gc_userdata, get_wrapped_error, init_error_registry,
30-
init_gc_metatable_for, init_userdata_metatable, pop_error, protect_lua, protect_lua_closure,
31-
push_gc_userdata, push_meta_gc_userdata, push_string, push_userdata, push_wrapped_error,
32-
StackGuard,
25+
assert_stack, callback_error, check_stack, get_gc_userdata, get_main_state,
26+
get_meta_gc_userdata, get_wrapped_error, init_error_registry, init_gc_metatable_for,
27+
init_userdata_metatable, pop_error, protect_lua, protect_lua_closure, push_gc_userdata,
28+
push_meta_gc_userdata, push_string, push_userdata, push_wrapped_error, StackGuard,
3329
};
3430
use crate::value::{FromLua, FromLuaMulti, MultiValue, Nil, ToLua, ToLuaMulti, Value};
3531

@@ -44,6 +40,9 @@ use {
4440
futures_util::future::{self, TryFutureExt},
4541
};
4642

43+
#[cfg(feature = "serialize")]
44+
use {crate::util::get_destructed_userdata_metatable, serde::Serialize};
45+
4746
/// Top level Lua struct which holds the Lua state itself.
4847
pub struct Lua {
4948
pub(crate) state: *mut ffi::lua_State,

src/serde/mod.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,10 +65,12 @@ pub trait LuaSerdeExt<'lua> {
6565
/// ```
6666
fn array_metatable(&'lua self) -> Result<Table<'lua>>;
6767

68-
/// Convert a `T` into Lua `Value` instance.
68+
/// Converts `T` into a `Value` instance.
6969
///
7070
/// Requires `feature = "serialize"`
7171
///
72+
/// [`Value`]: enum.Value.html
73+
///
7274
/// # Example
7375
///
7476
/// ```
@@ -96,10 +98,12 @@ pub trait LuaSerdeExt<'lua> {
9698
/// ```
9799
fn to_value<T: Serialize + ?Sized>(&'lua self, t: &T) -> Result<Value<'lua>>;
98100

99-
/// Deserialize a Lua `Value` into any serde deserializable object.
101+
/// Deserializes a `Value` into any serde deserializable object.
100102
///
101103
/// Requires `feature = "serialize"`
102104
///
105+
/// [`Value`]: enum.Value.html
106+
///
103107
/// # Example
104108
///
105109
/// ```

0 commit comments

Comments
 (0)