use std::ffi::c_void;
#[cfg(feature = "serde")]
use crate::Lua;
use crate::{FromLua, IntoLua, LuaAnyRef, LuaResult, LuaState, LuaValueKind, UserDataRef};
use crate::lua_api::{LuaFunction, LuaString, LuaTable};
#[derive(Clone, Debug)]
pub struct Value {
pub(crate) inner: LuaAnyRef,
}
impl Value {
pub(crate) fn new(inner: LuaAnyRef) -> Self {
Value { inner }
}
#[inline]
pub fn kind(&self) -> LuaValueKind {
self.inner.kind()
}
#[inline]
pub fn type_name(&self) -> &'static str {
self.to_value().type_name()
}
#[inline]
pub fn is_nil(&self) -> bool {
self.to_value().is_nil()
}
#[inline]
pub fn get<T: FromLua>(&self) -> LuaResult<T> {
self.inner.get_as()
}
#[inline]
pub fn as_table(&self) -> Option<LuaTable> {
self.inner.as_table().map(LuaTable::new)
}
#[inline]
pub fn as_function(&self) -> Option<LuaFunction> {
self.inner.as_function().map(LuaFunction::new)
}
#[inline]
pub fn as_userdata<T: 'static>(&self) -> Option<UserDataRef<T>> {
self.inner.as_userdata()
}
#[inline]
pub fn as_string_handle(&self) -> Option<LuaString> {
self.inner.as_string().map(LuaString::new)
}
#[inline]
pub fn as_string(&self) -> Option<String> {
self.to_value().as_str().map(str::to_owned)
}
#[inline]
pub fn is_lightuserdata(&self) -> bool {
self.to_value().is_lightuserdata()
}
#[inline]
pub fn as_lightuserdata(&self) -> Option<*mut c_void> {
self.to_value().as_lightuserdata()
}
#[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 to_pointer(&self) -> Option<*const c_void> {
let value = self.to_value();
if let Some(pointer) = value.as_lightuserdata() {
return Some(pointer.cast_const());
}
match value.kind() {
LuaValueKind::String
| LuaValueKind::Table
| LuaValueKind::Function
| LuaValueKind::CFunction
| LuaValueKind::CClosure
| LuaValueKind::RClosure
| LuaValueKind::Userdata
| LuaValueKind::Thread => Some(value.raw_ptr_repr().cast()),
LuaValueKind::Nil
| LuaValueKind::Boolean
| LuaValueKind::Integer
| LuaValueKind::Float => None,
}
}
#[inline]
pub fn to_string_lossy(&self) -> String {
self.to_value()
.as_str()
.map(str::to_owned)
.or_else(|| self.to_value().as_integer().map(|i| i.to_string()))
.or_else(|| self.to_value().as_float().map(|n| n.to_string()))
.unwrap_or_else(|| self.type_name().to_owned())
}
#[cfg(feature = "serde")]
pub fn to_json_value(&self) -> Result<serde_json::Value, String> {
crate::serde::lua_to_json(&self.to_value())
}
#[cfg(feature = "serde")]
pub fn to_json_string(&self, pretty: bool) -> Result<String, String> {
crate::serde::lua_to_json_string(&self.to_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 value: {}", err))
}
#[cfg(feature = "serde")]
pub fn from_json_value(lua: &mut 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))?;
Ok(Value::new(vm.to_ref(value)))
}
#[cfg(feature = "serde")]
pub fn from_json_str(lua: &mut 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))?;
Ok(Value::new(vm.to_ref(value)))
}
#[cfg(feature = "serde")]
pub fn from_serde<T: serde::Serialize>(lua: &mut 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 to_value(&self) -> luars::LuaValue {
self.inner.to_value()
}
}
#[cfg(feature = "serde")]
impl serde::Serialize for Value {
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 Value {
fn into_lua(self, state: &mut LuaState) -> Result<usize, String> {
state
.push_value(self.inner.to_value())
.map_err(|e| format!("{:?}", e))?;
Ok(1)
}
}
impl IntoLua for &Value {
fn into_lua(self, state: &mut LuaState) -> Result<usize, String> {
state
.push_value(self.inner.to_value())
.map_err(|e| format!("{:?}", e))?;
Ok(1)
}
}
impl FromLua for Value {
fn from_lua(value: luars::LuaValue, state: &mut LuaState) -> Result<Self, String> {
Ok(Value::new(state.to_any_ref(value)))
}
}