use crate::{Error, Result};
use mlua::{Lua, LuaSerdeExt as _};
pub fn serde_value_to_lua_value(lua: &Lua, val: serde_json::Value) -> Result<mlua::Value> {
let res = match val {
serde_json::Value::Null => mlua::Value::Nil,
other => lua.to_value(&other)?,
};
Ok(res)
}
pub fn serde_values_to_lua_values(lua: &Lua, values: Vec<serde_json::Value>) -> Result<Vec<mlua::Value>> {
values.into_iter().map(|val| serde_value_to_lua_value(lua, val)).collect()
}
pub fn lua_value_to_serde_value(lua_value: mlua::Value) -> Result<serde_json::Value> {
use mlua::Value;
fn number_from_f64(v: f64) -> Result<serde_json::Number> {
serde_json::Number::from_f64(v)
.ok_or_else(|| Error::custom("Cannot convert non-finite Lua number to JSON (NaN or Infinity)"))
}
fn convert_table(table: mlua::Table) -> Result<serde_json::Value> {
let mut max_idx: usize = 0;
let mut numeric_only = true;
for pair in table.clone().pairs::<mlua::Value, mlua::Value>() {
let (k, _) = pair?;
match k {
Value::Integer(i) if i > 0 => {
let i = i as usize;
if i > max_idx {
max_idx = i;
}
}
Value::Number(n) if n.is_finite() && n.fract() == 0.0 && n > 0.0 => {
let i = n as usize;
if i > max_idx {
max_idx = i;
}
}
_ => {
numeric_only = false;
break;
}
}
}
if numeric_only {
let mut vec: Vec<Option<serde_json::Value>> = vec![None; max_idx];
for pair in table.clone().pairs::<mlua::Value, mlua::Value>() {
let (k, v) = pair?;
let idx_opt = match k {
Value::Integer(i) if i > 0 => Some(i as usize),
Value::Number(n) if n.is_finite() && n.fract() == 0.0 && n > 0.0 => Some(n as usize),
_ => None,
};
if let Some(idx) = idx_opt {
if idx == 0 || idx > max_idx {
numeric_only = false;
break;
}
vec[idx - 1] = Some(lua_value_to_serde_value(v)?);
} else {
numeric_only = false;
break;
}
}
if numeric_only && vec.iter().all(|o| o.is_some()) {
let arr = vec.into_iter().flatten().collect();
return Ok(serde_json::Value::Array(arr));
}
}
let mut map = serde_json::Map::new();
for pair in table.pairs::<mlua::Value, mlua::Value>() {
let (k, v) = pair?;
let key = match k {
Value::String(s) => s.to_str()?.to_string(),
Value::Integer(i) => i.to_string(),
Value::Number(n) => n.to_string(),
Value::Boolean(b) => b.to_string(),
other => {
return Err(Error::custom(format!(
"Unsupported Lua table key type '{}' for JSON object",
other.type_name()
)));
}
};
map.insert(key, lua_value_to_serde_value(v)?);
}
Ok(serde_json::Value::Object(map))
}
let res = match lua_value {
Value::Nil => serde_json::Value::Null,
Value::Boolean(b) => serde_json::Value::Bool(b),
Value::Integer(i) => serde_json::Value::Number(serde_json::Number::from(i)),
Value::Number(n) => serde_json::Value::Number(number_from_f64(n)?),
Value::String(s) => serde_json::Value::String(s.to_str()?.to_string()),
Value::Table(t) => convert_table(t)?,
Value::LightUserData(ldata) => {
if Value::LightUserData(ldata) == Value::NULL {
serde_json::Value::Null
} else {
serde_json::Value::Null
}
}
Value::Function(_) | Value::Thread(_) | Value::UserData(_) => {
return Err(Error::custom(
"Cannot serialize Lua value to JSON: unsupported type (Function/LigthUserData/UserData)",
));
}
Value::Error(_) => {
return Err(Error::custom(
"Cannot serialize Lua value to JSON: unsupported type (error)",
));
}
Value::Other(_) => {
return Err(Error::custom(
"Cannot serialize Lua value to JSON: unsupported type (other)",
));
}
};
Ok(res)
}
pub fn lua_value_list_to_serde_values(lua_value: mlua::Value) -> Result<Vec<serde_json::Value>> {
let list = match lua_value {
mlua::Value::Table(table) => table,
other => {
return Err(Error::custom(format!(
"Lua Value is not a List. Expected a Lua table (list) as the second argument, but got {}",
other.type_name()
)));
}
};
let iter = list.sequence_values::<mlua::Value>();
let json_values: Vec<serde_json::Value> = iter
.into_iter()
.map(|val| lua_value_to_serde_value(val?))
.collect::<Result<Vec<_>>>()
.map_err(|e| format!("A mlua Value cannot be serialize to Json.\nCause: {e}"))?;
Ok(json_values)
}