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