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;