Add serialization example & update others
This commit is contained in:
parent
c5d0ccc433
commit
afc41ab23c
10
Cargo.toml
10
Cargo.toml
|
@ -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"]
|
||||
|
|
11
README.md
11
README.md
|
@ -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.
|
||||
|
|
|
@ -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" {}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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(())
|
||||
})?;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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(())
|
||||
}
|
15
src/lua.rs
15
src/lua.rs
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
///
|
||||
/// ```
|
||||
|
|
Loading…
Reference in New Issue