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 a result, encoded Array will contain only sequence part of the table, with the same
41 /// length 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::serde::SerializeOptions;
105 /// use mlua::{Lua, Result, LuaSerdeExt};
106 ///
107 /// fn main() -> Result<()> {
108 /// let lua = Lua::new();
109 /// let v = vec![1, 2, 3];
110 /// let options = SerializeOptions::new().set_array_metatable(false);
111 /// lua.globals().set("v", lua.to_value_with(&v, options)?)?;
112 ///
113 /// lua.load(r#"
114 /// assert(#v == 3 and v[1] == 1 and v[2] == 2 and v[3] == 3)
115 /// assert(getmetatable(v) == nil)
116 /// "#).exec()
117 /// }
118 /// ```
119 fn to_value_with<T>(&self, t: &T, options: ser::Options) -> Result<Value>
120 where
121 T: Serialize + ?Sized;
122
123 /// Deserializes a [`Value`] into any serde deserializable object.
124 ///
125 /// # Example
126 ///
127 /// ```
128 /// use mlua::{Lua, Result, LuaSerdeExt};
129 /// use serde::Deserialize;
130 ///
131 /// #[derive(Deserialize, Debug, PartialEq)]
132 /// struct User {
133 /// name: String,
134 /// age: u8,
135 /// }
136 ///
137 /// fn main() -> Result<()> {
138 /// let lua = Lua::new();
139 /// let val = lua.load(r#"{name = "John Smith", age = 20}"#).eval()?;
140 /// let u: User = lua.from_value(val)?;
141 ///
142 /// assert_eq!(u, User { name: "John Smith".into(), age: 20 });
143 ///
144 /// Ok(())
145 /// }
146 /// ```
147 #[allow(clippy::wrong_self_convention)]
148 fn from_value<T: DeserializeOwned>(&self, value: Value) -> Result<T>;
149
150 /// Deserializes a [`Value`] into any serde deserializable object with options.
151 ///
152 /// # Example
153 ///
154 /// ```
155 /// use mlua::serde::DeserializeOptions;
156 /// use mlua::{Lua, Result, LuaSerdeExt};
157 /// use serde::Deserialize;
158 ///
159 /// #[derive(Deserialize, Debug, PartialEq)]
160 /// struct User {
161 /// name: String,
162 /// age: u8,
163 /// }
164 ///
165 /// fn main() -> Result<()> {
166 /// let lua = Lua::new();
167 /// let val = lua.load(r#"{name = "John Smith", age = 20, f = function() end}"#).eval()?;
168 /// let options = DeserializeOptions::new().deny_unsupported_types(false);
169 /// let u: User = lua.from_value_with(val, options)?;
170 ///
171 /// assert_eq!(u, User { name: "John Smith".into(), age: 20 });
172 ///
173 /// Ok(())
174 /// }
175 /// ```
176 #[allow(clippy::wrong_self_convention)]
177 fn from_value_with<T: DeserializeOwned>(&self, value: Value, options: de::Options) -> Result<T>;
178}
179
180impl LuaSerdeExt for Lua {
181 fn null(&self) -> Value {
182 Value::NULL
183 }
184
185 fn array_metatable(&self) -> Table {
186 let lua = self.lock();
187 unsafe {
188 push_array_metatable(lua.ref_thread());
189 Table(lua.pop_ref_thread())
190 }
191 }
192
193 fn to_value<T>(&self, t: &T) -> Result<Value>
194 where
195 T: Serialize + ?Sized,
196 {
197 t.serialize(ser::Serializer::new(self))
198 }
199
200 fn to_value_with<T>(&self, t: &T, options: ser::Options) -> Result<Value>
201 where
202 T: Serialize + ?Sized,
203 {
204 t.serialize(ser::Serializer::new_with_options(self, options))
205 }
206
207 fn from_value<T>(&self, value: Value) -> Result<T>
208 where
209 T: DeserializeOwned,
210 {
211 T::deserialize(de::Deserializer::new(value))
212 }
213
214 fn from_value_with<T>(&self, value: Value, options: de::Options) -> Result<T>
215 where
216 T: DeserializeOwned,
217 {
218 T::deserialize(de::Deserializer::new_with_options(value, options))
219 }
220}
221
222// Uses 2 stack spaces and calls checkstack.
223pub(crate) unsafe fn init_metatables(state: *mut ffi::lua_State) -> Result<()> {
224 check_stack(state, 2)?;
225 protect_lua!(state, 0, 0, fn(state) {
226 ffi::lua_createtable(state, 0, 1);
227
228 ffi::lua_pushstring(state, cstr!("__metatable"));
229 ffi::lua_pushboolean(state, 0);
230 ffi::lua_rawset(state, -3);
231
232 let array_metatable_key = &ARRAY_METATABLE_REGISTRY_KEY as *const u8 as *const c_void;
233 ffi::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, array_metatable_key);
234 })
235}
236
237pub(crate) unsafe fn push_array_metatable(state: *mut ffi::lua_State) {
238 let array_metatable_key = &ARRAY_METATABLE_REGISTRY_KEY as *const u8 as *const c_void;
239 ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, array_metatable_key);
240}
241
242static ARRAY_METATABLE_REGISTRY_KEY: u8 = 0;
243
244pub mod de;
245pub mod ser;
246
247pub use de::{Deserializer, Options as DeserializeOptions};
248pub use ser::{Options as SerializeOptions, Serializer};