use core::fmt;
use std::collections::HashSet;
use luau_vm::thread::StackGuard;
use super::Table;
use crate::error::Error;
use crate::value::Value;
impl<'lua> Table<'lua> {
pub(crate) fn fmt_pretty(
&self,
formatter: &mut fmt::Formatter<'_>,
indent: usize,
visited: &mut HashSet<*const ()>,
) -> fmt::Result {
let pointer = self.pointer();
if !visited.insert(pointer) {
return write!(formatter, "table: {pointer:p}");
}
let Ok(mut pairs) = self.pairs_for_debug() else {
return write!(formatter, "table: {pointer:p}");
};
if pairs.is_empty() {
return formatter.write_str("{}");
}
pairs.sort_by(|(left, _), (right, _)| left.sort_cmp(right));
let is_sequence = pairs.iter().enumerate().all(|(index, (key, _))| {
matches!(
key.as_integer(),
Some(key) if key == i64::try_from(index + 1).unwrap_or(i64::MAX)
)
});
writeln!(formatter, "{{")?;
if is_sequence {
for (_, value) in pairs {
write!(formatter, "{}", " ".repeat(indent + 2))?;
value.fmt_pretty(formatter, true, indent + 2, visited)?;
writeln!(formatter, ",")?;
}
} else {
for (key, value) in pairs {
match key {
Value::String(key) if is_simple_key(key.as_bytes().as_ref()) => {
write!(formatter, "{}{key}", " ".repeat(indent + 2))?;
formatter.write_str(" = ")?;
}
key => {
write!(formatter, "{}[", " ".repeat(indent + 2))?;
key.fmt_pretty(formatter, false, indent + 2, visited)?;
formatter.write_str("] = ")?;
}
}
value.fmt_pretty(formatter, true, indent + 2, visited)?;
writeln!(formatter, ",")?;
}
}
write!(formatter, "{}}}", " ".repeat(indent))
}
fn pairs_for_debug(&self) -> Result<Vec<(Value<'lua>, Value<'lua>)>, Error> {
unsafe {
let thread = self.thread();
let vm_thread = thread.as_vm();
let _stack = StackGuard::new(vm_thread);
let mut pairs = Vec::new();
self.push_to(&thread)?;
let table_index = vm_thread.get_top();
vm_thread
.push_nil()
.map_err(|exit| Error::from_thread_exit(vm_thread, exit))?;
while vm_thread
.next(table_index)
.map_err(|exit| Error::from_thread_exit(vm_thread, exit))?
!= 0
{
let value = Value::from_stack(&thread, -1)?;
let key = Value::from_stack(&thread, -2)?;
pairs.push((key, value));
vm_thread.pop(1);
}
Ok(pairs)
}
}
}
impl fmt::Debug for Table<'_> {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
if formatter.alternate() {
return self.fmt_pretty(formatter, 0, &mut HashSet::new());
}
formatter
.debug_tuple("Table")
.field(&self.reference)
.finish()
}
}
fn is_simple_key(key: &[u8]) -> bool {
key.first()
.is_some_and(|byte| byte.is_ascii_alphabetic() || *byte == b'_')
&& key
.iter()
.all(|byte| byte.is_ascii_alphanumeric() || *byte == b'_')
}