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