mlua/serde/
mod.rs

1//! (De)Serialization support using serde.
2
3use std::os::raw::c_void;
4
5use serde::{de::DeserializeOwned, ser::Serialize};
6
7use crate::error::Result;
8use crate::lua::Lua;
9use crate::private::Sealed;
10use crate::table::Table;
11use crate::util::check_stack;
12use crate::value::Value;
13
14/// Trait for serializing/deserializing Lua values using Serde.
15#[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
16pub trait LuaSerdeExt: Sealed {
17    /// A special value (lightuserdata) to encode/decode optional (none) values.
18    ///
19    /// Requires `feature = "serialize"`
20    ///
21    /// # Example
22    ///
23    /// ```
24    /// use std::collections::HashMap;
25    /// use mlua::{Lua, Result, LuaSerdeExt};
26    ///
27    /// fn main() -> Result<()> {
28    ///     let lua = Lua::new();
29    ///     lua.globals().set("null", lua.null())?;
30    ///
31    ///     let val = lua.load(r#"{a = null}"#).eval()?;
32    ///     let map: HashMap<String, Option<String>> = lua.from_value(val)?;
33    ///     assert_eq!(map["a"], None);
34    ///
35    ///     Ok(())
36    /// }
37    /// ```
38    fn null(&self) -> Value;
39
40    /// A metatable attachable to a Lua table to systematically encode it as Array (instead of Map).
41    /// As result, encoded Array will contain only sequence part of the table, with the same length
42    /// as the `#` operator on that table.
43    ///
44    /// Requires `feature = "serialize"`
45    ///
46    /// # Example
47    ///
48    /// ```
49    /// use mlua::{Lua, Result, LuaSerdeExt};
50    /// use serde_json::Value as JsonValue;
51    ///
52    /// fn main() -> Result<()> {
53    ///     let lua = Lua::new();
54    ///     lua.globals().set("array_mt", lua.array_metatable())?;
55    ///
56    ///     // Encode as an empty array (no sequence part in the lua table)
57    ///     let val = lua.load("setmetatable({a = 5}, array_mt)").eval()?;
58    ///     let j: JsonValue = lua.from_value(val)?;
59    ///     assert_eq!(j.to_string(), "[]");
60    ///
61    ///     // Encode as object
62    ///     let val = lua.load("{a = 5}").eval()?;
63    ///     let j: JsonValue = lua.from_value(val)?;
64    ///     assert_eq!(j.to_string(), r#"{"a":5}"#);
65    ///
66    ///     Ok(())
67    /// }
68    /// ```
69    fn array_metatable(&self) -> Table;
70
71    /// Converts `T` into a [`Value`] instance.
72    ///
73    /// Requires `feature = "serialize"`
74    ///
75    /// [`Value`]: crate::Value
76    ///
77    /// # Example
78    ///
79    /// ```
80    /// use mlua::{Lua, Result, LuaSerdeExt};
81    /// use serde::Serialize;
82    ///
83    /// #[derive(Serialize)]
84    /// struct User {
85    ///     name: String,
86    ///     age: u8,
87    /// }
88    ///
89    /// fn main() -> Result<()> {
90    ///     let lua = Lua::new();
91    ///     let u = User {
92    ///         name: "John Smith".into(),
93    ///         age: 20,
94    ///     };
95    ///     lua.globals().set("user", lua.to_value(&u)?)?;
96    ///     lua.load(r#"
97    ///         assert(user["name"] == "John Smith")
98    ///         assert(user["age"] == 20)
99    ///     "#).exec()
100    /// }
101    /// ```
102    fn to_value<'lua, T: Serialize + ?Sized>(&'lua self, t: &T) -> Result<Value<'lua>>;
103
104    /// Converts `T` into a [`Value`] instance with options.
105    ///
106    /// Requires `feature = "serialize"`
107    ///
108    /// [`Value`]: crate::Value
109    ///
110    /// # Example
111    ///
112    /// ```
113    /// use mlua::{Lua, Result, LuaSerdeExt, SerializeOptions};
114    ///
115    /// fn main() -> Result<()> {
116    ///     let lua = Lua::new();
117    ///     let v = vec![1, 2, 3];
118    ///     let options = SerializeOptions::new().set_array_metatable(false);
119    ///     lua.globals().set("v", lua.to_value_with(&v, options)?)?;
120    ///
121    ///     lua.load(r#"
122    ///         assert(#v == 3 and v[1] == 1 and v[2] == 2 and v[3] == 3)
123    ///         assert(getmetatable(v) == nil)
124    ///     "#).exec()
125    /// }
126    /// ```
127    fn to_value_with<'lua, T>(&'lua self, t: &T, options: ser::Options) -> Result<Value<'lua>>
128    where
129        T: Serialize + ?Sized;
130
131    /// Deserializes a [`Value`] into any serde deserializable object.
132    ///
133    /// Requires `feature = "serialize"`
134    ///
135    /// [`Value`]: crate::Value
136    ///
137    /// # Example
138    ///
139    /// ```
140    /// use mlua::{Lua, Result, LuaSerdeExt};
141    /// use serde::Deserialize;
142    ///
143    /// #[derive(Deserialize, Debug, PartialEq)]
144    /// struct User {
145    ///     name: String,
146    ///     age: u8,
147    /// }
148    ///
149    /// fn main() -> Result<()> {
150    ///     let lua = Lua::new();
151    ///     let val = lua.load(r#"{name = "John Smith", age = 20}"#).eval()?;
152    ///     let u: User = lua.from_value(val)?;
153    ///
154    ///     assert_eq!(u, User { name: "John Smith".into(), age: 20 });
155    ///
156    ///     Ok(())
157    /// }
158    /// ```
159    #[allow(clippy::wrong_self_convention)]
160    fn from_value<T: DeserializeOwned>(&self, value: Value) -> Result<T>;
161
162    /// Deserializes a [`Value`] into any serde deserializable object with options.
163    ///
164    /// Requires `feature = "serialize"`
165    ///
166    /// [`Value`]: crate::Value
167    ///
168    /// # Example
169    ///
170    /// ```
171    /// use mlua::{Lua, Result, LuaSerdeExt, DeserializeOptions};
172    /// use serde::Deserialize;
173    ///
174    /// #[derive(Deserialize, Debug, PartialEq)]
175    /// struct User {
176    ///     name: String,
177    ///     age: u8,
178    /// }
179    ///
180    /// fn main() -> Result<()> {
181    ///     let lua = Lua::new();
182    ///     let val = lua.load(r#"{name = "John Smith", age = 20, f = function() end}"#).eval()?;
183    ///     let options = DeserializeOptions::new().deny_unsupported_types(false);
184    ///     let u: User = lua.from_value_with(val, options)?;
185    ///
186    ///     assert_eq!(u, User { name: "John Smith".into(), age: 20 });
187    ///
188    ///     Ok(())
189    /// }
190    /// ```
191    #[allow(clippy::wrong_self_convention)]
192    fn from_value_with<T: DeserializeOwned>(&self, value: Value, options: de::Options)
193        -> Result<T>;
194}
195
196impl LuaSerdeExt for Lua {
197    fn null(&self) -> Value {
198        Value::NULL
199    }
200
201    fn array_metatable(&self) -> Table {
202        unsafe {
203            push_array_metatable(self.ref_thread());
204            Table(self.pop_ref_thread())
205        }
206    }
207
208    fn to_value<'lua, T>(&'lua self, t: &T) -> Result<Value<'lua>>
209    where
210        T: Serialize + ?Sized,
211    {
212        t.serialize(ser::Serializer::new(self))
213    }
214
215    fn to_value_with<'lua, T>(&'lua self, t: &T, options: ser::Options) -> Result<Value<'lua>>
216    where
217        T: Serialize + ?Sized,
218    {
219        t.serialize(ser::Serializer::new_with_options(self, options))
220    }
221
222    fn from_value<T>(&self, value: Value) -> Result<T>
223    where
224        T: DeserializeOwned,
225    {
226        T::deserialize(de::Deserializer::new(value))
227    }
228
229    fn from_value_with<T>(&self, value: Value, options: de::Options) -> Result<T>
230    where
231        T: DeserializeOwned,
232    {
233        T::deserialize(de::Deserializer::new_with_options(value, options))
234    }
235}
236
237// Uses 2 stack spaces and calls checkstack.
238pub(crate) unsafe fn init_metatables(state: *mut ffi::lua_State) -> Result<()> {
239    check_stack(state, 2)?;
240    protect_lua!(state, 0, 0, fn(state) {
241        ffi::lua_createtable(state, 0, 1);
242
243        ffi::lua_pushstring(state, cstr!("__metatable"));
244        ffi::lua_pushboolean(state, 0);
245        ffi::lua_rawset(state, -3);
246
247        let array_metatable_key = &ARRAY_METATABLE_REGISTRY_KEY as *const u8 as *const c_void;
248        ffi::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, array_metatable_key);
249    })
250}
251
252pub(crate) unsafe fn push_array_metatable(state: *mut ffi::lua_State) {
253    let array_metatable_key = &ARRAY_METATABLE_REGISTRY_KEY as *const u8 as *const c_void;
254    ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, array_metatable_key);
255}
256
257static ARRAY_METATABLE_REGISTRY_KEY: u8 = 0;
258
259pub mod de;
260pub mod ser;
261
262#[doc(inline)]
263pub use de::Deserializer;
264#[doc(inline)]
265pub use ser::Serializer;