use crate::frame::Frame;
use crate::reply::Out;
use mlua::{Lua, Table, Value};
pub(super) fn push(out: &mut Out, value: &Value) {
match value {
Value::Nil | Value::Boolean(false) => out.nil(),
Value::Boolean(true) => out.int(1),
Value::Integer(n) => out.int(*n),
Value::Number(d) => out.int(*d as i64),
Value::String(s) => out.bulk(&s.as_bytes()),
Value::Table(t) => table(out, t),
_ => out.nil(),
}
}
fn table(out: &mut Out, t: &Table) {
if let Some(msg) = text(t, "err") {
out.error(&scrubbed(&msg));
return;
}
if let Some(msg) = text(t, "ok") {
out.simple(&scrubbed(&msg));
return;
}
if let Ok(Value::Number(d)) = t.raw_get::<Value>("double") {
out.double(d);
return;
}
if let Ok(Value::Integer(n)) = t.raw_get::<Value>("double") {
out.double(n as f64);
return;
}
if let Some(digits) = text(t, "big_number") {
out.big_number(&digits);
return;
}
if let Ok(Value::Table(inner)) = t.raw_get::<Value>("map") {
pairs(out, &inner);
return;
}
if let Ok(Value::Table(inner)) = t.raw_get::<Value>("set") {
members(out, &inner);
return;
}
array(out, t);
}
fn array(out: &mut Out, t: &Table) {
let mut n = 0;
while !matches!(t.raw_get::<Value>(n + 1), Ok(Value::Nil) | Err(_)) {
n += 1;
}
out.array(n);
for i in 1..=n {
push(out, &t.raw_get::<Value>(i).unwrap_or(Value::Nil));
}
}
fn pairs(out: &mut Out, t: &Table) {
let held: Vec<(Value, Value)> = t.pairs::<Value, Value>().filter_map(Result::ok).collect();
out.map(held.len());
for (k, v) in &held {
push(out, k);
push(out, v);
}
}
fn members(out: &mut Out, t: &Table) {
let held: Vec<Value> = t
.pairs::<Value, Value>()
.filter_map(|p| p.ok())
.map(|(k, _)| k)
.collect();
out.set(held.len());
for k in &held {
push(out, k);
}
}
fn text(t: &Table, field: &str) -> Option<Vec<u8>> {
match t.raw_get::<Value>(field) {
Ok(Value::String(s)) => Some(s.as_bytes().to_vec()),
_ => None,
}
}
fn scrubbed(msg: &[u8]) -> Vec<u8> {
let mut out = msg.to_vec();
for b in &mut out {
if *b == b'\r' || *b == b'\n' {
*b = b' ';
}
}
out
}
pub(super) fn pull(lua: &Lua, frame: &Frame<'_>, resp3: bool) -> mlua::Result<Value> {
Ok(match frame {
Frame::Simple(s) => {
let t = lua.create_table()?;
t.raw_set("ok", lua.create_string(s)?)?;
Value::Table(t)
}
Frame::Error(e) | Frame::BlobError(e) => {
let t = lua.create_table()?;
t.raw_set("err", lua.create_string(e)?)?;
t.raw_set("ignore_error_stats_update", true)?;
Value::Table(t)
}
Frame::Int(n) => Value::Integer(*n),
Frame::Bulk(b) => Value::String(lua.create_string(b)?),
Frame::Null if resp3 => Value::Nil,
Frame::Null => Value::Boolean(false),
Frame::Double(d) => wrapped(lua, "double", Value::Number(*d))?,
Frame::Bool(b) => Value::Boolean(*b),
Frame::BigNumber(digits) => {
wrapped(lua, "big_number", Value::String(lua.create_string(digits)?))?
}
Frame::Verbatim { text, .. } => wrapped(
lua,
"verbatim_string",
Value::String(lua.create_string(text)?),
)?,
Frame::Array(items) | Frame::Push(items) => Value::Table(list(lua, items, resp3)?),
Frame::Set(items) => {
let t = lua.create_table()?;
for item in items {
t.raw_set(pull(lua, item, resp3)?, true)?;
}
wrapped(lua, "set", Value::Table(t))?
}
Frame::Map(items) | Frame::Attribute(items) => {
let t = lua.create_table()?;
for (k, v) in items {
t.raw_set(pull(lua, k, resp3)?, pull(lua, v, resp3)?)?;
}
wrapped(lua, "map", Value::Table(t))?
}
})
}
fn wrapped(lua: &Lua, field: &str, value: Value) -> mlua::Result<Value> {
let t = lua.create_table()?;
t.raw_set(field, value)?;
Ok(Value::Table(t))
}
fn list(lua: &Lua, items: &[Frame<'_>], resp3: bool) -> mlua::Result<Table> {
let t = lua.create_table_with_capacity(items.len(), 0)?;
for (i, item) in items.iter().enumerate() {
t.raw_set(i + 1, pull(lua, item, resp3)?)?;
}
Ok(t)
}