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;