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};