Add serialization example & update others

This commit is contained in:
Alex Orlenko 2020-12-29 20:02:03 +00:00
parent c5d0ccc433
commit afc41ab23c
9 changed files with 126 additions and 18 deletions

View File

@ -16,12 +16,8 @@ High level bindings to Lua 5.4/5.3/5.2/5.1 (including LuaJIT)
with async/await features and support of writing native lua modules in Rust.
"""
[badges]
# github-actions = { repository = "khvzak/mlua", workflow = "CI" }
maintenance = { status = "actively-developed" }
[package.metadata.docs.rs]
features = ["async", "send", "lua53"]
features = ["lua53", "async", "send", "serialize"]
[workspace]
members = [
@ -88,3 +84,7 @@ required-features = ["async", "send"]
[[example]]
name = "async_tcp_server"
required-features = ["async"]
[[example]]
name = "serialize"
required-features = ["serialize"]

View File

@ -33,9 +33,20 @@ Starting from v0.3, `mlua` supports async/await for all Lua versions. This works
**Examples**:
- [HTTP Client](examples/async_http_client.rs)
- [HTTP Client (json)](examples/async_http_reqwest.rs)
- [HTTP Server](examples/async_http_server.rs)
- [TCP Server](examples/async_tcp_server.rs)
### 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 user data support).
[Example](examples/serialize.rs)
[`serde::Serialize`]: https://docs.serde.rs/serde/ser/trait.Serialize.html
[`serde::Deserialize`]: https://docs.serde.rs/serde/de/trait.Deserialize.html
[`mlua::Value`]: https://docs.rs/mlua/latest/mlua/enum.Value.html
### Compiling
You have to enable one of the features `lua54`, `lua53`, `lua52`, `lua51` or `luajit`, according to the choosen Lua version.

View File

@ -5,7 +5,8 @@
#[cfg_attr(
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
link_args = "-pagezero_size 10000 -image_base 100000000"
link_args = "-pagezero_size 10000 -image_base 100000000",
allow(unused_attributes)
)]
extern "system" {}

View File

@ -61,7 +61,7 @@ async fn main() -> Result<()> {
let f = lua
.load(
r#"
local res = fetch_url(...);
local res = fetch_url(...)
print(res.status)
for key, vals in pairs(res.headers) do
for _, val in ipairs(vals) do

View File

@ -63,7 +63,7 @@ impl UserData for LuaTcpStream {
async fn run_server(lua: &'static Lua) -> Result<()> {
let spawn = lua.create_function(move |_, func: Function| {
task::spawn_local(async move { func.call_async::<_, ()>(()).await.unwrap() });
task::spawn_local(async move { func.call_async::<_, ()>(()).await });
Ok(())
})?;

View File

@ -164,6 +164,33 @@ fn main() -> Result<()> {
< f32::EPSILON
);
// Normally, Rust types passed to `Lua` must be `'static`, because there is no way to be
// sure of their lifetime inside the Lua state. There is, however, a limited way to lift this
// requirement. You can call `Lua::scope` to create userdata and callbacks types that only live
// for as long as the call to scope, but do not have to be `'static` (and `Send`).
{
let mut rust_val = 0;
lua.scope(|scope| {
// We create a 'sketchy' Lua callback that holds a mutable reference to the variable
// `rust_val`. Outside of a `Lua::scope` call, this would not be allowed
// because it could be unsafe.
lua.globals().set(
"sketchy",
scope.create_function_mut(|_, ()| {
rust_val = 42;
Ok(())
})?,
)?;
lua.load("sketchy()").exec()
})?;
assert_eq!(rust_val, 42);
}
// We were able to run our 'sketchy' function inside the scope just fine. However, if we
// try to run our 'sketchy' function outside of the scope, the function we created will have
// been invalidated and we will generate an error. If our function wasn't invalidated, we

66
examples/serialize.rs Normal file
View File

@ -0,0 +1,66 @@
use mlua::{Error, Lua, LuaSerdeExt, Result, UserData, Value};
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize)]
enum Transmission {
Manual,
Automatic,
}
#[derive(Serialize, Deserialize)]
struct Engine {
v: u32,
kw: u32,
}
#[derive(Serialize, Deserialize)]
struct Car {
active: bool,
model: String,
transmission: Transmission,
engine: Engine,
}
impl UserData for Car {}
fn main() -> Result<()> {
let lua = Lua::new();
let globals = lua.globals();
// Create Car struct from a Lua table
let car: Car = lua.from_value(lua.load(r#"
{active = true, model = "Volkswagen Golf", transmission = "Automatic", engine = {v = 1499, kw = 90}}
"#).eval()?)?;
// Set it as (serializable) userdata
globals.set("null", lua.null()?)?;
globals.set("array_mt", lua.array_metatable()?)?;
globals.set("car", lua.create_ser_userdata(car)?)?;
// Create a Lua table with multiple data types
let val: Value = lua
.load(r#"{driver = "Boris", car = car, price = null, points = setmetatable({}, array_mt)}"#)
.eval()?;
// Serialize the table above to JSON
let json_str = serde_json::to_string(&val).map_err(Error::external)?;
println!("{}", json_str);
// Create Lua Value from JSON (or any serializable type)
let json = serde_json::json!({
"key": "value",
"null": null,
"array": [],
});
globals.set("json_value", lua.to_value(&json)?)?;
lua.load(
r#"
assert(json_value["key"] == "value")
assert(json_value["null"] == null)
assert(#(json_value["array"]) == 0)
"#,
)
.exec()?;
Ok(())
}

View File

@ -7,9 +7,6 @@ use std::os::raw::{c_char, c_int, c_void};
use std::sync::{Arc, Mutex, Weak};
use std::{mem, ptr, str};
#[cfg(feature = "serialize")]
use serde::Serialize;
use crate::error::{Error, Result};
use crate::ffi;
use crate::function::Function;
@ -25,11 +22,10 @@ use crate::types::{
};
use crate::userdata::{AnyUserData, MetaMethod, UserData, UserDataMethods, UserDataWrapped};
use crate::util::{
assert_stack, callback_error, check_stack, get_destructed_userdata_metatable, get_gc_userdata,
get_main_state, get_meta_gc_userdata, get_wrapped_error, init_error_registry,
init_gc_metatable_for, init_userdata_metatable, pop_error, protect_lua, protect_lua_closure,
push_gc_userdata, push_meta_gc_userdata, push_string, push_userdata, push_wrapped_error,
StackGuard,
assert_stack, callback_error, check_stack, get_gc_userdata, get_main_state,
get_meta_gc_userdata, get_wrapped_error, init_error_registry, init_gc_metatable_for,
init_userdata_metatable, pop_error, protect_lua, protect_lua_closure, push_gc_userdata,
push_meta_gc_userdata, push_string, push_userdata, push_wrapped_error, StackGuard,
};
use crate::value::{FromLua, FromLuaMulti, MultiValue, Nil, ToLua, ToLuaMulti, Value};
@ -44,6 +40,9 @@ use {
futures_util::future::{self, TryFutureExt},
};
#[cfg(feature = "serialize")]
use {crate::util::get_destructed_userdata_metatable, serde::Serialize};
/// Top level Lua struct which holds the Lua state itself.
pub struct Lua {
pub(crate) state: *mut ffi::lua_State,

View File

@ -65,10 +65,12 @@ pub trait LuaSerdeExt<'lua> {
/// ```
fn array_metatable(&'lua self) -> Result<Table<'lua>>;
/// Convert a `T` into Lua `Value` instance.
/// Converts `T` into a `Value` instance.
///
/// Requires `feature = "serialize"`
///
/// [`Value`]: enum.Value.html
///
/// # Example
///
/// ```
@ -96,10 +98,12 @@ pub trait LuaSerdeExt<'lua> {
/// ```
fn to_value<T: Serialize + ?Sized>(&'lua self, t: &T) -> Result<Value<'lua>>;
/// Deserialize a Lua `Value` into any serde deserializable object.
/// Deserializes a `Value` into any serde deserializable object.
///
/// Requires `feature = "serialize"`
///
/// [`Value`]: enum.Value.html
///
/// # Example
///
/// ```