use std::cell::RefCell;
use std::collections::HashSet;
use std::ffi::c_void;
use std::rc::Rc;
use std::result::Result as StdResult;
use serde::ser::{self, Serialize, SerializeMap, SerializeSeq, Serializer};
use super::de::{check_value_for_skip, encode_as_array, MapPairs, Options, RecursionGuard};
use crate::table::Table;
use crate::value::Value;
type Visited = Rc<RefCell<HashSet<*const c_void>>>;
pub struct SerializableValue<'a> {
value: &'a Value,
options: Options,
visited: Option<Visited>,
}
impl Value {
pub fn to_serializable(&self) -> SerializableValue<'_> {
SerializableValue::new(self, Options::default(), None)
}
}
impl Serialize for Value {
#[inline]
fn serialize<S: Serializer>(&self, serializer: S) -> StdResult<S::Ok, S::Error> {
SerializableValue::new(self, Options::default(), None).serialize(serializer)
}
}
impl<'a> SerializableValue<'a> {
#[inline]
pub(crate) fn new(value: &'a Value, options: Options, visited: Option<&Visited>) -> Self {
if let Value::Table(_) = value {
return Self {
value,
options,
visited: visited.cloned().or_else(|| Some(Default::default())),
};
}
Self {
value,
options,
visited: None,
}
}
#[must_use]
pub fn deny_unsupported_types(mut self, enabled: bool) -> Self {
self.options.deny_unsupported_types = enabled;
self
}
#[must_use]
pub fn deny_recursive_tables(mut self, enabled: bool) -> Self {
self.options.deny_recursive_tables = enabled;
self
}
#[must_use]
pub fn sort_keys(mut self, enabled: bool) -> Self {
self.options.sort_keys = enabled;
self
}
#[must_use]
pub fn encode_empty_tables_as_array(mut self, enabled: bool) -> Self {
self.options.encode_empty_tables_as_array = enabled;
self
}
#[must_use]
pub fn detect_mixed_tables(mut self, enabled: bool) -> Self {
self.options.detect_mixed_tables = enabled;
self
}
}
impl Serialize for SerializableValue<'_> {
fn serialize<S>(&self, serializer: S) -> StdResult<S::Ok, S::Error>
where
S: Serializer,
{
if super::is_null(self.value) {
return serializer.serialize_none();
}
match self.value {
Value::Nil => serializer.serialize_unit(),
Value::Boolean(b) => serializer.serialize_bool(*b),
Value::Integer(i) => serializer.serialize_i64(*i),
Value::Number(n) => serializer.serialize_f64(*n),
Value::Vector(v) => {
use ser::SerializeTupleStruct;
let mut ts = serializer.serialize_tuple_struct("Vector", crate::Vector::SIZE)?;
ts.serialize_field(&v.x())?;
ts.serialize_field(&v.y())?;
ts.serialize_field(&v.z())?;
ts.end()
}
Value::String(s) => serialize_lua_string(s, serializer),
Value::Table(t) => {
let visited = self.visited.as_ref().unwrap().clone();
SerializableTable::new(t, self.options, visited).serialize(serializer)
}
Value::Buffer(buf) => serializer.serialize_bytes(buf.as_slice()),
Value::Function(_)
| Value::Thread(_)
| Value::LightUserData(_)
| Value::UserData(_)
| Value::Error(_) => {
if self.options.deny_unsupported_types {
let msg = format!("cannot serialize <{}>", self.value.type_name());
Err(ser::Error::custom(msg))
} else {
serializer.serialize_unit()
}
}
}
}
}
fn serialize_lua_string<S>(s: &crate::LuaString, serializer: S) -> StdResult<S::Ok, S::Error>
where
S: Serializer,
{
match s.to_str() {
Ok(s) => serializer.serialize_str(&s),
Err(_) => serializer.serialize_bytes(&s.as_bytes()),
}
}
pub struct SerializableTable<'a> {
table: &'a Table,
options: Options,
visited: Visited,
}
impl Serialize for Table {
#[inline]
fn serialize<S: Serializer>(&self, serializer: S) -> StdResult<S::Ok, S::Error> {
SerializableTable::new(self, Options::default(), Default::default()).serialize(serializer)
}
}
impl<'a> SerializableTable<'a> {
#[inline]
pub(crate) fn new(table: &'a Table, options: Options, visited: Visited) -> Self {
Self {
table,
options,
visited,
}
}
}
impl Serialize for SerializableTable<'_> {
fn serialize<S>(&self, serializer: S) -> StdResult<S::Ok, S::Error>
where
S: Serializer,
{
let options = self.options;
let visited = &self.visited;
let _guard = RecursionGuard::new(self.table, visited);
if let Some(len) = encode_as_array(self.table, self.options) {
let mut seq = serializer.serialize_seq(Some(len))?;
let mut idx = 1usize;
while idx <= len {
let value: Value = self
.table
.raw_get(idx as i64)
.map_err(serde::ser::Error::custom)?;
let skip = check_value_for_skip(&value, options, visited)
.map_err(serde::ser::Error::custom)?;
if !skip {
seq.serialize_element(&SerializableValue::new(&value, options, Some(visited)))?;
}
idx += 1;
}
return seq.end();
}
let mut map = serializer.serialize_map(None)?;
let pairs =
MapPairs::new(self.table, options.sort_keys).map_err(serde::ser::Error::custom)?;
for kv in pairs {
let (key, value) = kv.map_err(serde::ser::Error::custom)?;
let skip_key =
check_value_for_skip(&key, options, visited).map_err(serde::ser::Error::custom)?;
let skip_value = check_value_for_skip(&value, options, visited)
.map_err(serde::ser::Error::custom)?;
if skip_key || skip_value {
continue;
}
map.serialize_entry(
&SerializableValue::new(&key, options, Some(visited)),
&SerializableValue::new(&value, options, Some(visited)),
)?;
}
map.end()
}
}