factorio_mlua/serde/
mod.rs

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