///|
/// Opaque handle to a Lua table stored in the registry.
///
/// Instances of this type are created when converting Lua values
/// into [`Value::Table`] and keep the underlying table alive via a
/// registry reference.
struct Table(Ref)
///|
pub impl Show for Table with output(self : Table, logger : &Logger) -> Unit {
let length = self.length()
logger.write_string("{")
let visit = for i in 1..=length {
guard self.get(Integer(i.to_int64())) is Some(value) else { break i }
logger.write_char(' ')
logger.write_string(value.to_string())
if i < length {
logger.write_char(',')
}
} else {
length + 1
}
for k, v in self {
if k is Integer(i) && i >= 1 && i < visit.to_int64() {
continue
}
logger.write_string(", [")
logger.write_string(k.to_string())
logger.write_string("] = ")
logger.write_string(v.to_string())
}
logger.write_string(" }")
}
///|
pub impl ToJson for Table with to_json(self : Table) -> Json {
let length = self.length()
let mut array : FixedArray[Json?]? = Some(FixedArray::make(length, None))
let mut count = 0
let object : Map[String, Json] = Map::new()
for k, v in self {
if array is Some(a) && k is Integer(i) && i >= 1 && i <= length.to_int64() {
let i = i.to_int() - 1
// Fill the array with the value
a[i] = Some(v.to_json())
count += 1
} else {
if array is Some(a) {
// We found that the table contains non-integer keys,
// so we switch to an object
for j, v in a {
if v is Some(v) {
object[(j + 1).to_string()] = v
}
}
array = None
}
// Use the key-value pair in the object
object[k.to_string()] = v.to_json()
}
}
if array is Some(array) && count == length {
let values = []
for v in array {
values.push(v.unwrap())
}
return Json::array(values)
}
Json::object(object)
}
///|
/// Returns an iterator over this table's key-value pairs.
///
/// The iterator traverses the table using `lua_next`, yielding
/// arbitrary keys and values as [`Value`] pairs until the table is
/// exhausted.
pub fn Table::iterator2(self : Table) -> Iterator2[Value, Value] {
let lua = self.0.lua()
let mut last_key = Nil
Iterator2::new(() => lua.with_state(state => {
// Push the table
@lua.raw_get_i(state, @lua.registry_index, self.0.int().to_int64())
|> ignore()
// Push the last key
state_push_value(state, last_key)
// Get the next key-value pair
if @lua.next(state, -2) {
// Get the key (-2) and value (-1)
let value = state_pop_top_value(lua, state).unwrap()
let key = state_pop_top_value(lua, state).unwrap()
// Pop the table
let _ = @lua.pop(state, 1)
// Update last_key for the next iteration
last_key = key
Some((key, value))
} else {
// No more elements
@lua.pop(state, 1) // Pop the table
None
}
}))
}
///|
/// Computes the length of this table using Lua's `#` operator.
///
/// This is equivalent to calling `@lua.len` on the underlying
/// table and converting the result to a MoonBit `Int`.
pub fn Table::length(self : Table) -> Int {
@c.with_unsafe_borrowed(self, _ => {
let lua = self.0.lua()
lua.with_state(state => {
// Push the table
@lua.raw_get_i(state, @lua.registry_index, self.0.int().to_int64())
|> ignore()
@lua.raw_len(state, -1).to_int()
})
})
}
///|
pub fn Table::to_array(self : Table) -> Array[Value] {
@c.with_unsafe_borrowed(self, _ => {
let lua = self.0.lua()
lua.with_state(state => {
// Push the table
@lua.raw_get_i(state, @lua.registry_index, self.0.int().to_int64())
|> ignore()
let len = @lua.raw_len(state, -1).to_int()
let result = Array::new(capacity=len)
for i in 1..=len {
@lua.raw_get_i(state, -1, i.to_int64()) |> ignore()
let value = state_pop_top_value(lua, state).unwrap()
result.push(value)
}
@lua.pop(state, 1) // Pop the table
result
})
})
}
///|
pub fn Table::get(self : Table, value : Value) -> Value? {
@c.with_unsafe_borrowed(self, _ => {
let lua = self.0.lua()
lua.with_state(state => {
// Push the table
@lua.raw_get_i(state, @lua.registry_index, self.0.int().to_int64())
|> ignore()
// Push the key
state_push_value(state, value)
// Get the value
@lua.get_table(state, -2) |> ignore()
let result = state_pop_top_value(lua, state)
@lua.pop(state, 1) // Pop the table
result
})
})
}