Skip to main content

mlua/serde/
mod.rs

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