use std::os::raw::c_int; use serde::{ser, Serialize}; use super::LuaSerdeExt; use crate::error::{Error, Result}; use crate::ffi; use crate::lua::Lua; use crate::string::String; use crate::table::Table; use crate::types::Integer; use crate::util::{check_stack, StackGuard}; use crate::value::{ToLua, Value}; /// A struct for serializing Rust values into Lua values. #[derive(Debug)] pub struct Serializer<'lua> { lua: &'lua Lua, options: Options, } /// A struct with options to change default serializer behaviour. #[derive(Debug, Clone, Copy)] #[non_exhaustive] pub struct Options { /// If true, sequence serialization to a Lua table will create table /// with the [`array_metatable`] attached. /// /// Default: **true** /// /// [`array_metatable`]: ../trait.LuaSerdeExt.html#tymethod.array_metatable pub set_array_metatable: bool, /// If true, serialize `None` (part of `Option` type) to [`null`]. /// Otherwise it will be set to Lua [`Nil`]. /// /// Default: **true** /// /// [`null`]: ../trait.LuaSerdeExt.html#tymethod.null /// [`Nil`]: ../../enum.Value.html#variant.Nil pub serialize_none_to_null: bool, /// If true, serialize `Unit` (type of `()` in Rust) and Unit structs to [`null`]. /// Otherwise it will be set to Lua [`Nil`]. /// /// Default: **true** /// /// [`null`]: ../trait.LuaSerdeExt.html#tymethod.null /// [`Nil`]: ../../enum.Value.html#variant.Nil pub serialize_unit_to_null: bool, } impl Default for Options { fn default() -> Self { Options { set_array_metatable: true, serialize_none_to_null: true, serialize_unit_to_null: true, } } } impl Options { /// Retruns a new instance of `Options` with default parameters. pub fn new() -> Self { Self::default() } /// Sets [`set_array_metatable`] option. /// /// [`set_array_metatable`]: #structfield.set_array_metatable pub fn set_array_metatable(mut self, enabled: bool) -> Self { self.set_array_metatable = enabled; self } /// Sets [`serialize_none_to_null`] option. /// /// [`serialize_none_to_null`]: #structfield.serialize_none_to_null pub fn serialize_none_to_null(mut self, enabled: bool) -> Self { self.serialize_none_to_null = enabled; self } /// Sets [`serialize_unit_to_null`] option. /// /// [`serialize_unit_to_null`]: #structfield.serialize_unit_to_null pub fn serialize_unit_to_null(mut self, enabled: bool) -> Self { self.serialize_unit_to_null = enabled; self } } impl<'lua> Serializer<'lua> { /// Creates a new Lua Serializer with default options. pub fn new(lua: &'lua Lua) -> Self { Self::new_with_options(lua, Options::default()) } /// Creates a new Lua Serializer with custom options. pub fn new_with_options(lua: &'lua Lua, options: Options) -> Self { Serializer { lua, options } } } macro_rules! lua_serialize_number { ($name:ident, $t:ty) => { #[inline] fn $name(self, value: $t) -> Result> { value.to_lua(self.lua) } }; } impl<'lua> ser::Serializer for Serializer<'lua> { type Ok = Value<'lua>; type Error = Error; // Associated types for keeping track of additional state while serializing // compound data structures like sequences and maps. type SerializeSeq = SerializeVec<'lua>; type SerializeTuple = SerializeVec<'lua>; type SerializeTupleStruct = SerializeVec<'lua>; type SerializeTupleVariant = SerializeTupleVariant<'lua>; type SerializeMap = SerializeMap<'lua>; type SerializeStruct = SerializeMap<'lua>; type SerializeStructVariant = SerializeStructVariant<'lua>; #[inline] fn serialize_bool(self, value: bool) -> Result> { Ok(Value::Boolean(value)) } lua_serialize_number!(serialize_i8, i8); lua_serialize_number!(serialize_u8, u8); lua_serialize_number!(serialize_i16, i16); lua_serialize_number!(serialize_u16, u16); lua_serialize_number!(serialize_i32, i32); lua_serialize_number!(serialize_u32, u32); lua_serialize_number!(serialize_i64, i64); lua_serialize_number!(serialize_u64, u64); lua_serialize_number!(serialize_f32, f32); lua_serialize_number!(serialize_f64, f64); #[inline] fn serialize_char(self, value: char) -> Result> { self.serialize_str(&value.to_string()) } #[inline] fn serialize_str(self, value: &str) -> Result> { self.lua.create_string(value).map(Value::String) } #[inline] fn serialize_bytes(self, value: &[u8]) -> Result> { self.lua.create_string(value).map(Value::String) } #[inline] fn serialize_none(self) -> Result> { if self.options.serialize_none_to_null { Ok(self.lua.null()) } else { Ok(Value::Nil) } } #[inline] fn serialize_some(self, value: &T) -> Result> where T: Serialize + ?Sized, { value.serialize(self) } #[inline] fn serialize_unit(self) -> Result> { if self.options.serialize_unit_to_null { Ok(self.lua.null()) } else { Ok(Value::Nil) } } #[inline] fn serialize_unit_struct(self, _name: &'static str) -> Result> { if self.options.serialize_unit_to_null { Ok(self.lua.null()) } else { Ok(Value::Nil) } } #[inline] fn serialize_unit_variant( self, _name: &'static str, _variant_index: u32, variant: &'static str, ) -> Result> { self.serialize_str(variant) } #[inline] fn serialize_newtype_struct(self, _name: &'static str, value: &T) -> Result> where T: Serialize + ?Sized, { value.serialize(self) } #[inline] fn serialize_newtype_variant( self, _name: &'static str, _variant_index: u32, variant: &'static str, value: &T, ) -> Result> where T: Serialize + ?Sized, { let table = self.lua.create_table()?; let variant = self.lua.create_string(variant)?; let value = self.lua.to_value_with(value, self.options)?; table.raw_set(variant, value)?; Ok(Value::Table(table)) } #[inline] fn serialize_seq(self, len: Option) -> Result { let len = len.unwrap_or(0) as c_int; let table = self.lua.create_table_with_capacity(len, 0)?; if self.options.set_array_metatable { table.set_metatable(Some(self.lua.array_metatable())); } let options = self.options; Ok(SerializeVec { table, options }) } #[inline] fn serialize_tuple(self, len: usize) -> Result { self.serialize_seq(Some(len)) } #[inline] fn serialize_tuple_struct( self, _name: &'static str, len: usize, ) -> Result { self.serialize_seq(Some(len)) } #[inline] fn serialize_tuple_variant( self, _name: &'static str, _variant_index: u32, variant: &'static str, _len: usize, ) -> Result { Ok(SerializeTupleVariant { name: self.lua.create_string(variant)?, table: self.lua.create_table()?, options: self.options, }) } #[inline] fn serialize_map(self, len: Option) -> Result { let len = len.unwrap_or(0) as c_int; Ok(SerializeMap { key: None, table: self.lua.create_table_with_capacity(0, len)?, options: self.options, }) } #[inline] fn serialize_struct(self, _name: &'static str, len: usize) -> Result { self.serialize_map(Some(len)) } #[inline] fn serialize_struct_variant( self, _name: &'static str, _variant_index: u32, variant: &'static str, len: usize, ) -> Result { Ok(SerializeStructVariant { name: self.lua.create_string(variant)?, table: self.lua.create_table_with_capacity(0, len as c_int)?, options: self.options, }) } } #[doc(hidden)] pub struct SerializeVec<'lua> { table: Table<'lua>, options: Options, } impl<'lua> ser::SerializeSeq for SerializeVec<'lua> { type Ok = Value<'lua>; type Error = Error; fn serialize_element(&mut self, value: &T) -> Result<()> where T: Serialize + ?Sized, { let lua = self.table.0.lua; let value = lua.to_value_with(value, self.options)?; unsafe { let _sg = StackGuard::new(lua.state); check_stack(lua.state, 6)?; lua.push_ref(&self.table.0); lua.push_value(value)?; let len = ffi::lua_rawlen(lua.state, -2) as Integer; ffi::safe::lua_rawseti(lua.state, -2, len + 1) } } fn end(self) -> Result> { Ok(Value::Table(self.table)) } } impl<'lua> ser::SerializeTuple for SerializeVec<'lua> { type Ok = Value<'lua>; type Error = Error; fn serialize_element(&mut self, value: &T) -> Result<()> where T: Serialize + ?Sized, { ser::SerializeSeq::serialize_element(self, value) } fn end(self) -> Result> { ser::SerializeSeq::end(self) } } impl<'lua> ser::SerializeTupleStruct for SerializeVec<'lua> { type Ok = Value<'lua>; type Error = Error; fn serialize_field(&mut self, value: &T) -> Result<()> where T: Serialize + ?Sized, { ser::SerializeSeq::serialize_element(self, value) } fn end(self) -> Result> { ser::SerializeSeq::end(self) } } #[doc(hidden)] pub struct SerializeTupleVariant<'lua> { name: String<'lua>, table: Table<'lua>, options: Options, } impl<'lua> ser::SerializeTupleVariant for SerializeTupleVariant<'lua> { type Ok = Value<'lua>; type Error = Error; fn serialize_field(&mut self, value: &T) -> Result<()> where T: Serialize + ?Sized, { let lua = self.table.0.lua; let idx = self.table.raw_len() + 1; self.table .raw_insert(idx, lua.to_value_with(value, self.options)?) } fn end(self) -> Result> { let lua = self.table.0.lua; let table = lua.create_table()?; table.raw_set(self.name, self.table)?; Ok(Value::Table(table)) } } #[doc(hidden)] pub struct SerializeMap<'lua> { table: Table<'lua>, key: Option>, options: Options, } impl<'lua> ser::SerializeMap for SerializeMap<'lua> { type Ok = Value<'lua>; type Error = Error; fn serialize_key(&mut self, key: &T) -> Result<()> where T: Serialize + ?Sized, { let lua = self.table.0.lua; self.key = Some(lua.to_value_with(key, self.options)?); Ok(()) } fn serialize_value(&mut self, value: &T) -> Result<()> where T: Serialize + ?Sized, { let lua = self.table.0.lua; let key = mlua_expect!( self.key.take(), "serialize_value called before serialize_key" ); let value = lua.to_value_with(value, self.options)?; self.table.raw_set(key, value) } fn end(self) -> Result> { Ok(Value::Table(self.table)) } } impl<'lua> ser::SerializeStruct for SerializeMap<'lua> { type Ok = Value<'lua>; type Error = Error; fn serialize_field(&mut self, key: &'static str, value: &T) -> Result<()> where T: Serialize + ?Sized, { ser::SerializeMap::serialize_key(self, key)?; ser::SerializeMap::serialize_value(self, value) } fn end(self) -> Result> { ser::SerializeMap::end(self) } } #[doc(hidden)] pub struct SerializeStructVariant<'lua> { name: String<'lua>, table: Table<'lua>, options: Options, } impl<'lua> ser::SerializeStructVariant for SerializeStructVariant<'lua> { type Ok = Value<'lua>; type Error = Error; fn serialize_field(&mut self, key: &'static str, value: &T) -> Result<()> where T: Serialize + ?Sized, { let lua = self.table.0.lua; self.table .raw_set(key, lua.to_value_with(value, self.options)?)?; Ok(()) } fn end(self) -> Result> { let lua = self.table.0.lua; let table = lua.create_table()?; table.raw_set(self.name, self.table)?; Ok(Value::Table(table)) } }