use crate::{FromLua, FromLuaMulti, IntoLua, LuaFunction, LuaResult, LuaTableRef, LuaValue};
#[derive(Clone, Debug)]
pub struct LuaTable {
pub(crate) inner: LuaTableRef,
}
impl LuaTable {
pub(crate) fn new(inner: LuaTableRef) -> Self {
LuaTable { inner }
}
#[inline]
pub fn get<T: FromLua>(&self, key: impl IntoLua) -> LuaResult<T> {
self.inner.get_typed(key)
}
pub fn get_path<T: FromLua>(&self, path: &[&str]) -> LuaResult<T> {
let mut current = self.clone();
let Some((last, prefix)) = path.split_last() else {
return Err(luars::LuaError::RuntimeError);
};
for key in prefix {
current = current.get::<LuaTable>(*key)?;
}
current.get(*last)
}
#[inline]
pub fn contains_key(&self, key: impl IntoLua) -> LuaResult<bool> {
self.inner.contains_key(key)
}
#[inline]
pub fn has_metatable(&self) -> bool {
self.inner.has_metatable()
}
#[inline]
pub fn get_metatable(&self) -> Option<LuaTable> {
self.inner.get_metatable().map(LuaTable::new)
}
#[inline]
pub fn set_metatable(&self, metatable: Option<&LuaTable>) -> LuaResult<()> {
self.inner
.set_metatable(metatable.map(|table| &table.inner))
}
#[inline]
pub fn set(&self, key: impl IntoLua, value: impl IntoLua) -> LuaResult<()> {
self.inner.set_typed(key, value)
}
#[inline]
pub fn call_function<A: IntoLua, R: FromLuaMulti>(&self, name: &str, args: A) -> LuaResult<R> {
self.get::<LuaFunction>(name)?.call(args)
}
#[inline]
pub fn call_method<A: IntoLua, R: FromLuaMulti>(&self, name: &str, args: A) -> LuaResult<R> {
self.get::<LuaFunction>(name)?.call((self.clone(), args))
}
#[inline]
pub fn call_method1<A: IntoLua, R: FromLua>(&self, name: &str, args: A) -> LuaResult<R> {
self.get::<LuaFunction>(name)?.call1((self.clone(), args))
}
#[inline]
pub fn raw_get<T: FromLua>(&self, key: impl IntoLua) -> LuaResult<T> {
self.inner.rawget_typed(key)
}
#[inline]
pub fn raw_set(&self, key: impl IntoLua, value: impl IntoLua) -> LuaResult<()> {
self.inner.rawset_typed(key, value)
}
#[inline]
pub fn raw_seti(&self, idx: i64, value: impl IntoLua) -> LuaResult<()> {
self.inner.rawseti_typed(idx, value)
}
#[inline]
pub fn raw_geti<T: FromLua>(&self, idx: i64) -> LuaResult<T> {
self.inner.rawgeti_typed(idx)
}
#[inline]
pub fn len(&self) -> LuaResult<usize> {
self.inner.len()
}
#[inline]
pub fn raw_len(&self) -> LuaResult<usize> {
self.inner.len()
}
#[inline]
pub fn is_empty(&self) -> LuaResult<bool> {
self.len().map(|len| len == 0)
}
#[inline]
pub fn pairs_raw(&self) -> LuaResult<Vec<(LuaValue, LuaValue)>> {
self.inner.pairs()
}
#[inline]
pub fn pairs<K: FromLua, V: FromLua>(&self) -> LuaResult<Vec<(K, V)>> {
self.inner.pairs_typed()
}
#[inline]
pub fn sequence_values<T: FromLua>(&self) -> LuaResult<Vec<T>> {
self.inner.sequence_values()
}
#[inline]
pub fn push(&self, value: impl IntoLua) -> LuaResult<()> {
self.inner.push_typed(value)
}
#[cfg(feature = "serde")]
pub fn to_json_value(&self) -> Result<serde_json::Value, String> {
crate::serde::lua_to_json(&self.value())
}
#[cfg(feature = "serde")]
pub fn to_json_string(&self, pretty: bool) -> Result<String, String> {
crate::serde::lua_to_json_string(&self.value(), pretty)
}
#[cfg(feature = "serde")]
pub fn to_serde<T: serde::de::DeserializeOwned>(&self) -> Result<T, String> {
let json = self.to_json_value()?;
serde_json::from_value(json).map_err(|err| format!("Failed to deserialize table: {}", err))
}
#[cfg(feature = "serde")]
pub fn from_json_value(lua: &mut crate::Lua, json: &serde_json::Value) -> LuaResult<Self> {
let vm = lua.global_state_mut();
let value = vm
.deserialize_from_json(json)
.map_err(|msg| vm.error(msg))?;
LuaTable::from_lua(value, vm.main_state()).map_err(|msg| vm.error(msg))
}
#[cfg(feature = "serde")]
pub fn from_json_str(lua: &mut crate::Lua, json: &str) -> LuaResult<Self> {
let vm = lua.global_state_mut();
let value = vm
.deserialize_from_json_string(json)
.map_err(|msg| vm.error(msg))?;
LuaTable::from_lua(value, vm.main_state()).map_err(|msg| vm.error(msg))
}
#[cfg(feature = "serde")]
pub fn from_serde<T: serde::Serialize>(lua: &mut crate::Lua, value: &T) -> LuaResult<Self> {
let json = match serde_json::to_value(value) {
Ok(json) => json,
Err(err) => {
let vm = lua.global_state_mut();
return Err(vm.error(err.to_string()));
}
};
Self::from_json_value(lua, &json)
}
pub(crate) fn value(&self) -> luars::LuaValue {
self.inner.to_value()
}
pub unsafe fn to_value(&self) -> LuaValue {
self.inner.to_value()
}
}
#[cfg(feature = "serde")]
impl serde::Serialize for LuaTable {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serde::Serialize::serialize(
&self.to_json_value().map_err(serde::ser::Error::custom)?,
serializer,
)
}
}
impl IntoLua for LuaTable {
#[inline]
fn into_lua(self, state: &mut luars::LuaState) -> Result<usize, String> {
state
.push_value(self.inner.to_value())
.map_err(|e| format!("{:?}", e))?;
Ok(1)
}
}
impl IntoLua for &LuaTable {
#[inline]
fn into_lua(self, state: &mut luars::LuaState) -> Result<usize, String> {
state
.push_value(self.inner.to_value())
.map_err(|e| format!("{:?}", e))?;
Ok(1)
}
}
impl FromLua for LuaTable {
fn from_lua(value: LuaValue, state: &mut luars::LuaState) -> Result<Self, String> {
let actual = value.type_name();
let table = state
.to_table_ref(value)
.ok_or_else(|| format!("expected table, got {}", actual))?;
Ok(LuaTable::new(table))
}
}