use std::cell::RefCell;
use std::collections::HashMap;
use serde::de::DeserializeOwned;
use serde::ser::Serialize;
use crate::error::Result;
use crate::state::Lua;
use crate::sys::lua_State;
use crate::table::Table;
use crate::value::Value;
pub mod de;
pub mod ser;
mod value_serialize;
pub use de::{Deserializer, Options as DeserializeOptions};
pub use ser::{Options as SerializeOptions, Serializer};
pub use value_serialize::{SerializableTable, SerializableValue};
struct StateSentinels {
null: Table,
array_metatable: Table,
}
thread_local! {
static SENTINELS: RefCell<HashMap<*mut lua_State, StateSentinels>> =
RefCell::new(HashMap::new());
}
pub(crate) fn null_table(lua: &Lua) -> Table {
let key = lua.state();
SENTINELS.with(|cell| {
cell.borrow_mut()
.entry(key)
.or_insert_with(|| StateSentinels {
null: lua.create_table(),
array_metatable: build_array_metatable(lua),
})
.null
.clone()
})
}
pub(crate) fn array_metatable_table(lua: &Lua) -> Table {
let key = lua.state();
SENTINELS.with(|cell| {
cell.borrow_mut()
.entry(key)
.or_insert_with(|| StateSentinels {
null: lua.create_table(),
array_metatable: build_array_metatable(lua),
})
.array_metatable
.clone()
})
}
fn build_array_metatable(lua: &Lua) -> Table {
let mt = lua.create_table();
let _ = mt.raw_set("__metatable", false);
mt
}
pub(crate) fn is_null(value: &Value) -> bool {
if let Value::Table(t) = value {
let key = t.lua().state();
return SENTINELS.with(|cell| {
cell.borrow()
.get(&key)
.map(|s| s.null.to_pointer() == t.to_pointer())
.unwrap_or(false)
});
}
false
}
pub(crate) fn has_array_metatable(table: &Table) -> bool {
let key = table.lua().state();
let array_ptr = SENTINELS.with(|cell| {
cell.borrow()
.get(&key)
.map(|s| s.array_metatable.to_pointer())
});
match array_ptr {
Some(ptr) => table
.metatable()
.map(|mt| mt.to_pointer() == ptr)
.unwrap_or(false),
None => false,
}
}
pub trait LuaSerdeExt {
fn null(&self) -> Value;
fn array_metatable(&self) -> Table;
fn to_value<T: Serialize + ?Sized>(&self, t: &T) -> Result<Value>;
fn to_value_with<T>(&self, t: &T, options: ser::Options) -> Result<Value>
where
T: Serialize + ?Sized;
#[allow(clippy::wrong_self_convention)]
fn from_value<T: DeserializeOwned>(&self, value: Value) -> Result<T>;
#[allow(clippy::wrong_self_convention)]
fn from_value_with<T: DeserializeOwned>(&self, value: Value, options: de::Options)
-> Result<T>;
}
impl LuaSerdeExt for Lua {
fn null(&self) -> Value {
Value::Table(null_table(self))
}
fn array_metatable(&self) -> Table {
array_metatable_table(self)
}
fn to_value<T>(&self, t: &T) -> Result<Value>
where
T: Serialize + ?Sized,
{
t.serialize(ser::Serializer::new(self))
}
fn to_value_with<T>(&self, t: &T, options: ser::Options) -> Result<Value>
where
T: Serialize + ?Sized,
{
t.serialize(ser::Serializer::new_with_options(self, options))
}
fn from_value<T>(&self, value: Value) -> Result<T>
where
T: DeserializeOwned,
{
T::deserialize(de::Deserializer::new(value))
}
fn from_value_with<T>(&self, value: Value, options: de::Options) -> Result<T>
where
T: DeserializeOwned,
{
T::deserialize(de::Deserializer::new_with_options(value, options))
}
}