fix table as global mod
This commit is contained in:
parent
2d43c457e1
commit
ae4af71822
@ -1,4 +1,3 @@
|
||||
local tbl = require("table")
|
||||
local mysql = {}
|
||||
|
||||
local Connection = {}
|
||||
@ -138,14 +137,14 @@ function Connection:insert(table_name, data)
|
||||
error("Table name cannot be empty")
|
||||
end
|
||||
|
||||
local keys = tbl.keys(data)
|
||||
local values = tbl.values(data)
|
||||
local placeholders = tbl.map(keys, function() return "?" end)
|
||||
local keys = table.keys(data)
|
||||
local values = table.values(data)
|
||||
local placeholders = table.map(keys, function() return "?" end)
|
||||
|
||||
local query = string.template("INSERT INTO ${table} (${columns}) VALUES (${placeholders})", {
|
||||
table = table_name,
|
||||
columns = tbl.concat(keys, ", "),
|
||||
placeholders = tbl.concat(placeholders, ", ")
|
||||
columns = table.concat(keys, ", "),
|
||||
placeholders = table.concat(placeholders, ", ")
|
||||
})
|
||||
|
||||
return self:exec(query, unpack(values))
|
||||
@ -156,21 +155,21 @@ function Connection:upsert(table_name, data, update_data)
|
||||
error("Table name cannot be empty")
|
||||
end
|
||||
|
||||
local keys = tbl.keys(data)
|
||||
local values = tbl.values(data)
|
||||
local placeholders = tbl.map(keys, function() return "?" end)
|
||||
local keys = table.keys(data)
|
||||
local values = table.values(data)
|
||||
local placeholders = table.map(keys, function() return "?" end)
|
||||
|
||||
-- Use update_data if provided, otherwise update with same data
|
||||
local update_source = update_data or data
|
||||
local updates = tbl.map(tbl.keys(update_source), function(key)
|
||||
local updates = table.map(table.keys(update_source), function(key)
|
||||
return string.template("${key} = VALUES(${key})", {key = key})
|
||||
end)
|
||||
|
||||
local query = string.template("INSERT INTO ${table} (${columns}) VALUES (${placeholders}) ON DUPLICATE KEY UPDATE ${updates}", {
|
||||
table = table_name,
|
||||
columns = tbl.concat(keys, ", "),
|
||||
placeholders = tbl.concat(placeholders, ", "),
|
||||
updates = tbl.concat(updates, ", ")
|
||||
columns = table.concat(keys, ", "),
|
||||
placeholders = table.concat(placeholders, ", "),
|
||||
updates = table.concat(updates, ", ")
|
||||
})
|
||||
|
||||
return self:exec(query, unpack(values))
|
||||
@ -181,14 +180,14 @@ function Connection:replace(table_name, data)
|
||||
error("Table name cannot be empty")
|
||||
end
|
||||
|
||||
local keys = tbl.keys(data)
|
||||
local values = tbl.values(data)
|
||||
local placeholders = tbl.map(keys, function() return "?" end)
|
||||
local keys = table.keys(data)
|
||||
local values = table.values(data)
|
||||
local placeholders = table.map(keys, function() return "?" end)
|
||||
|
||||
local query = string.template("REPLACE INTO ${table} (${columns}) VALUES (${placeholders})", {
|
||||
table = table_name,
|
||||
columns = tbl.concat(keys, ", "),
|
||||
placeholders = tbl.concat(placeholders, ", ")
|
||||
columns = table.concat(keys, ", "),
|
||||
placeholders = table.concat(placeholders, ", ")
|
||||
})
|
||||
|
||||
return self:exec(query, unpack(values))
|
||||
@ -202,21 +201,21 @@ function Connection:update(table_name, data, where_clause, ...)
|
||||
error("WHERE clause cannot be empty for UPDATE")
|
||||
end
|
||||
|
||||
local keys = tbl.keys(data)
|
||||
local values = tbl.values(data)
|
||||
local sets = tbl.map(keys, function(key)
|
||||
local keys = table.keys(data)
|
||||
local values = table.values(data)
|
||||
local sets = table.map(keys, function(key)
|
||||
return string.template("${key} = ?", {key = key})
|
||||
end)
|
||||
|
||||
local query = string.template("UPDATE ${table} SET ${sets} WHERE ${where}", {
|
||||
table = table_name,
|
||||
sets = tbl.concat(sets, ", "),
|
||||
sets = table.concat(sets, ", "),
|
||||
where = where_clause
|
||||
})
|
||||
|
||||
-- Add WHERE clause parameters
|
||||
local where_args = {...}
|
||||
tbl.extend(values, where_args)
|
||||
table.extend(values, where_args)
|
||||
|
||||
return self:exec(query, unpack(values))
|
||||
end
|
||||
@ -243,7 +242,7 @@ function Connection:select(table_name, columns, where_clause, ...)
|
||||
|
||||
columns = columns or "*"
|
||||
if type(columns) == "table" then
|
||||
columns = tbl.concat(columns, ", ")
|
||||
columns = table.concat(columns, ", ")
|
||||
end
|
||||
|
||||
local query
|
||||
@ -418,7 +417,7 @@ function Connection:create_index(index_name, table_name, columns, unique, type)
|
||||
|
||||
local unique_clause = unique and "UNIQUE " or ""
|
||||
local type_clause = type and string.template(" USING ${type}", {type = string.upper(type)}) or ""
|
||||
local columns_str = type(columns) == "table" and tbl.concat(columns, ", ") or tostring(columns)
|
||||
local columns_str = type(columns) == "table" and table.concat(columns, ", ") or tostring(columns)
|
||||
|
||||
local query = string.template("CREATE ${unique}INDEX ${index} ON ${table} (${columns})${type}", {
|
||||
unique = unique_clause,
|
||||
@ -465,7 +464,7 @@ function Connection:check_table(table_name, options)
|
||||
local valid_options = {"QUICK", "FAST", "MEDIUM", "EXTENDED", "CHANGED"}
|
||||
local options_upper = string.upper(options)
|
||||
|
||||
if tbl.contains(valid_options, options_upper) then
|
||||
if table.contains(valid_options, options_upper) then
|
||||
options_clause = string.template(" ${options}", {options = options_upper})
|
||||
end
|
||||
end
|
||||
@ -784,7 +783,7 @@ end
|
||||
|
||||
-- Simplified result processing utilities
|
||||
function mysql.to_array(results, column_name)
|
||||
if not results or tbl.is_empty(results) then
|
||||
if not results or table.is_empty(results) then
|
||||
return {}
|
||||
end
|
||||
|
||||
@ -792,11 +791,11 @@ function mysql.to_array(results, column_name)
|
||||
error("Column name cannot be empty")
|
||||
end
|
||||
|
||||
return tbl.map(results, function(row) return row[column_name] end)
|
||||
return table.map(results, function(row) return row[column_name] end)
|
||||
end
|
||||
|
||||
function mysql.to_map(results, key_column, value_column)
|
||||
if not results or tbl.is_empty(results) then
|
||||
if not results or table.is_empty(results) then
|
||||
return {}
|
||||
end
|
||||
|
||||
@ -813,7 +812,7 @@ function mysql.to_map(results, key_column, value_column)
|
||||
end
|
||||
|
||||
function mysql.group_by(results, column_name)
|
||||
if not results or tbl.is_empty(results) then
|
||||
if not results or table.is_empty(results) then
|
||||
return {}
|
||||
end
|
||||
|
||||
@ -821,18 +820,18 @@ function mysql.group_by(results, column_name)
|
||||
error("Column name cannot be empty")
|
||||
end
|
||||
|
||||
return tbl.group_by(results, function(row) return row[column_name] end)
|
||||
return table.group_by(results, function(row) return row[column_name] end)
|
||||
end
|
||||
|
||||
-- Simplified debug helper
|
||||
function mysql.print_results(results)
|
||||
if not results or tbl.is_empty(results) then
|
||||
if not results or table.is_empty(results) then
|
||||
print("No results")
|
||||
return
|
||||
end
|
||||
|
||||
local columns = tbl.keys(results[1])
|
||||
tbl.sort(columns)
|
||||
local columns = table.keys(results[1])
|
||||
table.sort(columns)
|
||||
|
||||
-- Calculate column widths
|
||||
local widths = {}
|
||||
@ -848,19 +847,19 @@ function mysql.print_results(results)
|
||||
end
|
||||
|
||||
-- Print header
|
||||
local header_parts = tbl.map(columns, function(col) return string.pad_right(col, widths[col]) end)
|
||||
local separator_parts = tbl.map(columns, function(col) return string.repeat_("-", widths[col]) end)
|
||||
local header_parts = table.map(columns, function(col) return string.pad_right(col, widths[col]) end)
|
||||
local separator_parts = table.map(columns, function(col) return string.repeat_("-", widths[col]) end)
|
||||
|
||||
print(tbl.concat(header_parts, " | "))
|
||||
print(tbl.concat(separator_parts, "-+-"))
|
||||
print(table.concat(header_parts, " | "))
|
||||
print(table.concat(separator_parts, "-+-"))
|
||||
|
||||
-- Print rows
|
||||
for _, row in ipairs(results) do
|
||||
local value_parts = tbl.map(columns, function(col)
|
||||
local value_parts = table.map(columns, function(col)
|
||||
local value = tostring(row[col] or "")
|
||||
return string.pad_right(value, widths[col])
|
||||
end)
|
||||
print(tbl.concat(value_parts, " | "))
|
||||
print(table.concat(value_parts, " | "))
|
||||
end
|
||||
end
|
||||
|
||||
@ -888,7 +887,7 @@ function mysql.build_dsn(options)
|
||||
local parts = {}
|
||||
|
||||
if options.username and not string.is_blank(options.username) then
|
||||
tbl.insert(parts, options.username)
|
||||
table.insert(parts, options.username)
|
||||
if options.password and not string.is_blank(options.password) then
|
||||
parts[#parts] = string.template("${user}:${pass}", {
|
||||
user = parts[#parts],
|
||||
@ -899,9 +898,9 @@ function mysql.build_dsn(options)
|
||||
end
|
||||
|
||||
if options.protocol and not string.is_blank(options.protocol) then
|
||||
tbl.insert(parts, string.template("${protocol}(", {protocol = options.protocol}))
|
||||
table.insert(parts, string.template("${protocol}(", {protocol = options.protocol}))
|
||||
if options.host and not string.is_blank(options.host) then
|
||||
tbl.insert(parts, options.host)
|
||||
table.insert(parts, options.host)
|
||||
if options.port then
|
||||
parts[#parts] = string.template("${host}:${port}", {
|
||||
host = parts[#parts],
|
||||
@ -918,33 +917,33 @@ function mysql.build_dsn(options)
|
||||
port = tostring(options.port)
|
||||
})
|
||||
end
|
||||
tbl.insert(parts, host_part .. ")")
|
||||
table.insert(parts, host_part .. ")")
|
||||
end
|
||||
|
||||
if options.database and not string.is_blank(options.database) then
|
||||
tbl.insert(parts, string.template("/${database}", {database = options.database}))
|
||||
table.insert(parts, string.template("/${database}", {database = options.database}))
|
||||
end
|
||||
|
||||
-- Add parameters
|
||||
local params = {}
|
||||
if options.charset and not string.is_blank(options.charset) then
|
||||
tbl.insert(params, string.template("charset=${charset}", {charset = options.charset}))
|
||||
table.insert(params, string.template("charset=${charset}", {charset = options.charset}))
|
||||
end
|
||||
if options.parseTime ~= nil then
|
||||
tbl.insert(params, string.template("parseTime=${parse}", {parse = tostring(options.parseTime)}))
|
||||
table.insert(params, string.template("parseTime=${parse}", {parse = tostring(options.parseTime)}))
|
||||
end
|
||||
if options.timeout and not string.is_blank(options.timeout) then
|
||||
tbl.insert(params, string.template("timeout=${timeout}", {timeout = options.timeout}))
|
||||
table.insert(params, string.template("timeout=${timeout}", {timeout = options.timeout}))
|
||||
end
|
||||
if options.tls and not string.is_blank(options.tls) then
|
||||
tbl.insert(params, string.template("tls=${tls}", {tls = options.tls}))
|
||||
table.insert(params, string.template("tls=${tls}", {tls = options.tls}))
|
||||
end
|
||||
|
||||
if #params > 0 then
|
||||
tbl.insert(parts, string.template("?${params}", {params = tbl.concat(params, "&")}))
|
||||
table.insert(parts, string.template("?${params}", {params = table.concat(params, "&")}))
|
||||
end
|
||||
|
||||
return tbl.concat(parts, "")
|
||||
return table.concat(parts, "")
|
||||
end
|
||||
|
||||
return mysql
|
||||
|
@ -1,4 +1,3 @@
|
||||
local tbl = require("table")
|
||||
local postgres = {}
|
||||
|
||||
local Connection = {}
|
||||
@ -134,12 +133,12 @@ end
|
||||
|
||||
-- Simplified PostgreSQL parameter builder
|
||||
local function build_postgres_params(data)
|
||||
local keys = tbl.keys(data)
|
||||
local values = tbl.values(data)
|
||||
local keys = table.keys(data)
|
||||
local values = table.values(data)
|
||||
local placeholders = {}
|
||||
|
||||
for i = 1, #keys do
|
||||
tbl.insert(placeholders, string.template("$${num}", {num = tostring(i)}))
|
||||
table.insert(placeholders, string.template("$${num}", {num = tostring(i)}))
|
||||
end
|
||||
|
||||
return keys, values, placeholders, #keys
|
||||
@ -155,8 +154,8 @@ function Connection:insert(table_name, data, returning)
|
||||
|
||||
local query = string.template("INSERT INTO ${table} (${columns}) VALUES (${placeholders})", {
|
||||
table = table_name,
|
||||
columns = tbl.concat(keys, ", "),
|
||||
placeholders = tbl.concat(placeholders, ", ")
|
||||
columns = table.concat(keys, ", "),
|
||||
placeholders = table.concat(placeholders, ", ")
|
||||
})
|
||||
|
||||
if returning and not string.is_blank(returning) then
|
||||
@ -176,7 +175,7 @@ function Connection:upsert(table_name, data, conflict_columns, returning)
|
||||
end
|
||||
|
||||
local keys, values, placeholders = build_postgres_params(data)
|
||||
local updates = tbl.map(keys, function(key)
|
||||
local updates = table.map(keys, function(key)
|
||||
return string.template("${key} = EXCLUDED.${key}", {key = key})
|
||||
end)
|
||||
|
||||
@ -185,16 +184,16 @@ function Connection:upsert(table_name, data, conflict_columns, returning)
|
||||
if type(conflict_columns) == "string" then
|
||||
conflict_clause = string.template("(${columns})", {columns = conflict_columns})
|
||||
else
|
||||
conflict_clause = string.template("(${columns})", {columns = tbl.concat(conflict_columns, ", ")})
|
||||
conflict_clause = string.template("(${columns})", {columns = table.concat(conflict_columns, ", ")})
|
||||
end
|
||||
end
|
||||
|
||||
local query = string.template("INSERT INTO ${table} (${columns}) VALUES (${placeholders}) ON CONFLICT ${conflict} DO UPDATE SET ${updates}", {
|
||||
table = table_name,
|
||||
columns = tbl.concat(keys, ", "),
|
||||
placeholders = tbl.concat(placeholders, ", "),
|
||||
columns = table.concat(keys, ", "),
|
||||
placeholders = table.concat(placeholders, ", "),
|
||||
conflict = conflict_clause,
|
||||
updates = tbl.concat(updates, ", ")
|
||||
updates = table.concat(updates, ", ")
|
||||
})
|
||||
|
||||
if returning and not string.is_blank(returning) then
|
||||
@ -216,13 +215,13 @@ function Connection:update(table_name, data, where_clause, returning, ...)
|
||||
error("WHERE clause cannot be empty for UPDATE")
|
||||
end
|
||||
|
||||
local keys = tbl.keys(data)
|
||||
local values = tbl.values(data)
|
||||
local keys = table.keys(data)
|
||||
local values = table.values(data)
|
||||
local param_count = #keys
|
||||
|
||||
local sets = {}
|
||||
for i, key in ipairs(keys) do
|
||||
tbl.insert(sets, string.template("${key} = $${num}", {
|
||||
table.insert(sets, string.template("${key} = $${num}", {
|
||||
key = key,
|
||||
num = tostring(i)
|
||||
}))
|
||||
@ -233,14 +232,14 @@ function Connection:update(table_name, data, where_clause, returning, ...)
|
||||
local where_clause_with_params = where_clause
|
||||
for i = 1, #where_args do
|
||||
param_count = param_count + 1
|
||||
tbl.insert(values, where_args[i])
|
||||
table.insert(values, where_args[i])
|
||||
where_clause_with_params = string.replace(where_clause_with_params, "?",
|
||||
string.template("$${num}", {num = tostring(param_count)}), 1)
|
||||
end
|
||||
|
||||
local query = string.template("UPDATE ${table} SET ${sets} WHERE ${where}", {
|
||||
table = table_name,
|
||||
sets = tbl.concat(sets, ", "),
|
||||
sets = table.concat(sets, ", "),
|
||||
where = where_clause_with_params
|
||||
})
|
||||
|
||||
@ -268,7 +267,7 @@ function Connection:delete(table_name, where_clause, returning, ...)
|
||||
local values = {}
|
||||
local where_clause_with_params = where_clause
|
||||
for i = 1, #where_args do
|
||||
tbl.insert(values, where_args[i])
|
||||
table.insert(values, where_args[i])
|
||||
where_clause_with_params = string.replace(where_clause_with_params, "?",
|
||||
string.template("$${num}", {num = tostring(i)}), 1)
|
||||
end
|
||||
@ -296,7 +295,7 @@ function Connection:select(table_name, columns, where_clause, ...)
|
||||
|
||||
columns = columns or "*"
|
||||
if type(columns) == "table" then
|
||||
columns = tbl.concat(columns, ", ")
|
||||
columns = table.concat(columns, ", ")
|
||||
end
|
||||
|
||||
local query
|
||||
@ -306,7 +305,7 @@ function Connection:select(table_name, columns, where_clause, ...)
|
||||
local values = {}
|
||||
local where_clause_with_params = where_clause
|
||||
for i = 1, #where_args do
|
||||
tbl.insert(values, where_args[i])
|
||||
table.insert(values, where_args[i])
|
||||
where_clause_with_params = string.replace(where_clause_with_params, "?",
|
||||
string.template("$${num}", {num = tostring(i)}), 1)
|
||||
end
|
||||
@ -411,7 +410,7 @@ function Connection:create_index(index_name, table_name, columns, unique, method
|
||||
|
||||
local unique_clause = unique and "UNIQUE " or ""
|
||||
local method_clause = method and string.template(" USING ${method}", {method = string.upper(method)}) or ""
|
||||
local columns_str = type(columns) == "table" and tbl.concat(columns, ", ") or tostring(columns)
|
||||
local columns_str = type(columns) == "table" and table.concat(columns, ", ") or tostring(columns)
|
||||
|
||||
local query = string.template("CREATE ${unique}INDEX IF NOT EXISTS ${index} ON ${table}${method} (${columns})", {
|
||||
unique = unique_clause,
|
||||
@ -460,7 +459,7 @@ function Connection:reindex(name, type)
|
||||
local valid_types = {"INDEX", "TABLE", "SCHEMA", "DATABASE", "SYSTEM"}
|
||||
local type_upper = string.upper(type)
|
||||
|
||||
if not tbl.contains(valid_types, type_upper) then
|
||||
if not table.contains(valid_types, type_upper) then
|
||||
error(string.template("Invalid REINDEX type: ${type}", {type = type}))
|
||||
end
|
||||
|
||||
@ -710,7 +709,7 @@ end
|
||||
|
||||
-- Simplified result processing utilities
|
||||
function postgres.to_array(results, column_name)
|
||||
if not results or tbl.is_empty(results) then
|
||||
if not results or table.is_empty(results) then
|
||||
return {}
|
||||
end
|
||||
|
||||
@ -718,11 +717,11 @@ function postgres.to_array(results, column_name)
|
||||
error("Column name cannot be empty")
|
||||
end
|
||||
|
||||
return tbl.map(results, function(row) return row[column_name] end)
|
||||
return table.map(results, function(row) return row[column_name] end)
|
||||
end
|
||||
|
||||
function postgres.to_map(results, key_column, value_column)
|
||||
if not results or tbl.is_empty(results) then
|
||||
if not results or table.is_empty(results) then
|
||||
return {}
|
||||
end
|
||||
|
||||
@ -739,7 +738,7 @@ function postgres.to_map(results, key_column, value_column)
|
||||
end
|
||||
|
||||
function postgres.group_by(results, column_name)
|
||||
if not results or tbl.is_empty(results) then
|
||||
if not results or table.is_empty(results) then
|
||||
return {}
|
||||
end
|
||||
|
||||
@ -747,18 +746,18 @@ function postgres.group_by(results, column_name)
|
||||
error("Column name cannot be empty")
|
||||
end
|
||||
|
||||
return tbl.group_by(results, function(row) return row[column_name] end)
|
||||
return table.group_by(results, function(row) return row[column_name] end)
|
||||
end
|
||||
|
||||
-- Simplified debug helper
|
||||
function postgres.print_results(results)
|
||||
if not results or tbl.is_empty(results) then
|
||||
if not results or table.is_empty(results) then
|
||||
print("No results")
|
||||
return
|
||||
end
|
||||
|
||||
local columns = tbl.keys(results[1])
|
||||
tbl.sort(columns)
|
||||
local columns = table.keys(results[1])
|
||||
table.sort(columns)
|
||||
|
||||
-- Calculate column widths
|
||||
local widths = {}
|
||||
@ -774,19 +773,19 @@ function postgres.print_results(results)
|
||||
end
|
||||
|
||||
-- Print header
|
||||
local header_parts = tbl.map(columns, function(col) return string.pad_right(col, widths[col]) end)
|
||||
local separator_parts = tbl.map(columns, function(col) return string.repeat_("-", widths[col]) end)
|
||||
local header_parts = table.map(columns, function(col) return string.pad_right(col, widths[col]) end)
|
||||
local separator_parts = table.map(columns, function(col) return string.repeat_("-", widths[col]) end)
|
||||
|
||||
print(tbl.concat(header_parts, " | "))
|
||||
print(tbl.concat(separator_parts, "-+-"))
|
||||
print(table.concat(header_parts, " | "))
|
||||
print(table.concat(separator_parts, "-+-"))
|
||||
|
||||
-- Print rows
|
||||
for _, row in ipairs(results) do
|
||||
local value_parts = tbl.map(columns, function(col)
|
||||
local value_parts = table.map(columns, function(col)
|
||||
local value = tostring(row[col] or "")
|
||||
return string.pad_right(value, widths[col])
|
||||
end)
|
||||
print(tbl.concat(value_parts, " | "))
|
||||
print(table.concat(value_parts, " | "))
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -1,4 +1,3 @@
|
||||
local tbl = require("table")
|
||||
local sqlite = {}
|
||||
|
||||
local Connection = {}
|
||||
@ -118,14 +117,14 @@ function Connection:insert(table_name, data)
|
||||
error("Table name cannot be empty")
|
||||
end
|
||||
|
||||
local keys = tbl.keys(data)
|
||||
local values = tbl.values(data)
|
||||
local placeholders = tbl.map(keys, function() return "?" end)
|
||||
local keys = table.keys(data)
|
||||
local values = table.values(data)
|
||||
local placeholders = table.map(keys, function() return "?" end)
|
||||
|
||||
local query = string.template("INSERT INTO ${table} (${columns}) VALUES (${placeholders})", {
|
||||
table = table_name,
|
||||
columns = tbl.concat(keys, ", "),
|
||||
placeholders = tbl.concat(placeholders, ", ")
|
||||
columns = table.concat(keys, ", "),
|
||||
placeholders = table.concat(placeholders, ", ")
|
||||
})
|
||||
|
||||
return self:exec(query, unpack(values))
|
||||
@ -136,10 +135,10 @@ function Connection:upsert(table_name, data, conflict_columns)
|
||||
error("Table name cannot be empty")
|
||||
end
|
||||
|
||||
local keys = tbl.keys(data)
|
||||
local values = tbl.values(data)
|
||||
local placeholders = tbl.map(keys, function() return "?" end)
|
||||
local updates = tbl.map(keys, function(key)
|
||||
local keys = table.keys(data)
|
||||
local values = table.values(data)
|
||||
local placeholders = table.map(keys, function() return "?" end)
|
||||
local updates = table.map(keys, function(key)
|
||||
return string.template("${key} = excluded.${key}", {key = key})
|
||||
end)
|
||||
|
||||
@ -148,16 +147,16 @@ function Connection:upsert(table_name, data, conflict_columns)
|
||||
if type(conflict_columns) == "string" then
|
||||
conflict_clause = string.template("(${columns})", {columns = conflict_columns})
|
||||
else
|
||||
conflict_clause = string.template("(${columns})", {columns = tbl.concat(conflict_columns, ", ")})
|
||||
conflict_clause = string.template("(${columns})", {columns = table.concat(conflict_columns, ", ")})
|
||||
end
|
||||
end
|
||||
|
||||
local query = string.template("INSERT INTO ${table} (${columns}) VALUES (${placeholders}) ON CONFLICT ${conflict} DO UPDATE SET ${updates}", {
|
||||
table = table_name,
|
||||
columns = tbl.concat(keys, ", "),
|
||||
placeholders = tbl.concat(placeholders, ", "),
|
||||
columns = table.concat(keys, ", "),
|
||||
placeholders = table.concat(placeholders, ", "),
|
||||
conflict = conflict_clause,
|
||||
updates = tbl.concat(updates, ", ")
|
||||
updates = table.concat(updates, ", ")
|
||||
})
|
||||
|
||||
return self:exec(query, unpack(values))
|
||||
@ -171,21 +170,21 @@ function Connection:update(table_name, data, where_clause, ...)
|
||||
error("WHERE clause cannot be empty for UPDATE")
|
||||
end
|
||||
|
||||
local keys = tbl.keys(data)
|
||||
local values = tbl.values(data)
|
||||
local sets = tbl.map(keys, function(key)
|
||||
local keys = table.keys(data)
|
||||
local values = table.values(data)
|
||||
local sets = table.map(keys, function(key)
|
||||
return string.template("${key} = ?", {key = key})
|
||||
end)
|
||||
|
||||
local query = string.template("UPDATE ${table} SET ${sets} WHERE ${where}", {
|
||||
table = table_name,
|
||||
sets = tbl.concat(sets, ", "),
|
||||
sets = table.concat(sets, ", "),
|
||||
where = where_clause
|
||||
})
|
||||
|
||||
-- Add WHERE clause parameters
|
||||
local where_args = {...}
|
||||
tbl.extend(values, where_args)
|
||||
table.extend(values, where_args)
|
||||
|
||||
return self:exec(query, unpack(values))
|
||||
end
|
||||
@ -212,7 +211,7 @@ function Connection:select(table_name, columns, where_clause, ...)
|
||||
|
||||
columns = columns or "*"
|
||||
if type(columns) == "table" then
|
||||
columns = tbl.concat(columns, ", ")
|
||||
columns = table.concat(columns, ", ")
|
||||
end
|
||||
|
||||
local query
|
||||
@ -252,7 +251,7 @@ function Connection:column_exists(table_name, column_name)
|
||||
|
||||
local result = self:query(string.template("PRAGMA table_info(${table})", {table = table_name}))
|
||||
if result then
|
||||
return tbl.any(result, function(row)
|
||||
return table.any(result, function(row)
|
||||
return string.iequals(row.name, string.trim(column_name))
|
||||
end)
|
||||
end
|
||||
@ -298,7 +297,7 @@ function Connection:create_index(index_name, table_name, columns, unique)
|
||||
end
|
||||
|
||||
local unique_clause = unique and "UNIQUE " or ""
|
||||
local columns_str = type(columns) == "table" and tbl.concat(columns, ", ") or tostring(columns)
|
||||
local columns_str = type(columns) == "table" and table.concat(columns, ", ") or tostring(columns)
|
||||
|
||||
local query = string.template("CREATE ${unique}INDEX IF NOT EXISTS ${index} ON ${table} (${columns})", {
|
||||
unique = unique_clause,
|
||||
@ -340,7 +339,7 @@ function Connection:journal_mode(mode)
|
||||
mode = mode or "WAL"
|
||||
local valid_modes = {"DELETE", "TRUNCATE", "PERSIST", "MEMORY", "WAL", "OFF"}
|
||||
|
||||
if not tbl.contains(tbl.map(valid_modes, string.upper), string.upper(mode)) then
|
||||
if not table.contains(table.map(valid_modes, string.upper), string.upper(mode)) then
|
||||
error("Invalid journal mode: " .. mode)
|
||||
end
|
||||
|
||||
@ -351,7 +350,7 @@ function Connection:synchronous(level)
|
||||
level = level or "NORMAL"
|
||||
local valid_levels = {"OFF", "NORMAL", "FULL", "EXTRA"}
|
||||
|
||||
if not tbl.contains(valid_levels, string.upper(level)) then
|
||||
if not table.contains(valid_levels, string.upper(level)) then
|
||||
error("Invalid synchronous level: " .. level)
|
||||
end
|
||||
|
||||
@ -370,7 +369,7 @@ function Connection:temp_store(mode)
|
||||
mode = mode or "MEMORY"
|
||||
local valid_modes = {"DEFAULT", "FILE", "MEMORY"}
|
||||
|
||||
if not tbl.contains(valid_modes, string.upper(mode)) then
|
||||
if not table.contains(valid_modes, string.upper(mode)) then
|
||||
error("Invalid temp_store mode: " .. mode)
|
||||
end
|
||||
|
||||
@ -511,7 +510,7 @@ end
|
||||
|
||||
-- Simplified result processing using table utilities
|
||||
function sqlite.to_array(results, column_name)
|
||||
if not results or tbl.is_empty(results) then
|
||||
if not results or table.is_empty(results) then
|
||||
return {}
|
||||
end
|
||||
|
||||
@ -519,11 +518,11 @@ function sqlite.to_array(results, column_name)
|
||||
error("Column name cannot be empty")
|
||||
end
|
||||
|
||||
return tbl.map(results, function(row) return row[column_name] end)
|
||||
return table.map(results, function(row) return row[column_name] end)
|
||||
end
|
||||
|
||||
function sqlite.to_map(results, key_column, value_column)
|
||||
if not results or tbl.is_empty(results) then
|
||||
if not results or table.is_empty(results) then
|
||||
return {}
|
||||
end
|
||||
|
||||
@ -540,7 +539,7 @@ function sqlite.to_map(results, key_column, value_column)
|
||||
end
|
||||
|
||||
function sqlite.group_by(results, column_name)
|
||||
if not results or tbl.is_empty(results) then
|
||||
if not results or table.is_empty(results) then
|
||||
return {}
|
||||
end
|
||||
|
||||
@ -548,21 +547,21 @@ function sqlite.group_by(results, column_name)
|
||||
error("Column name cannot be empty")
|
||||
end
|
||||
|
||||
return tbl.group_by(results, function(row) return row[column_name] end)
|
||||
return table.group_by(results, function(row) return row[column_name] end)
|
||||
end
|
||||
|
||||
-- Simplified debug helper
|
||||
function sqlite.print_results(results)
|
||||
if not results or tbl.is_empty(results) then
|
||||
if not results or table.is_empty(results) then
|
||||
print("No results")
|
||||
return
|
||||
end
|
||||
|
||||
local columns = tbl.keys(results[1])
|
||||
tbl.sort(columns)
|
||||
local columns = table.keys(results[1])
|
||||
table.sort(columns)
|
||||
|
||||
-- Calculate column widths
|
||||
local widths = tbl.map_values(tbl.to_map(columns, function(col) return col end, function(col) return string.length(col) end), function(width) return width end)
|
||||
local widths = table.map_values(table.to_map(columns, function(col) return col end, function(col) return string.length(col) end), function(width) return width end)
|
||||
|
||||
for _, row in ipairs(results) do
|
||||
for _, col in ipairs(columns) do
|
||||
@ -572,19 +571,19 @@ function sqlite.print_results(results)
|
||||
end
|
||||
|
||||
-- Print header
|
||||
local header_parts = tbl.map(columns, function(col) return string.pad_right(col, widths[col]) end)
|
||||
local separator_parts = tbl.map(columns, function(col) return string.repeat_("-", widths[col]) end)
|
||||
local header_parts = table.map(columns, function(col) return string.pad_right(col, widths[col]) end)
|
||||
local separator_parts = table.map(columns, function(col) return string.repeat_("-", widths[col]) end)
|
||||
|
||||
print(tbl.concat(header_parts, " | "))
|
||||
print(tbl.concat(separator_parts, "-+-"))
|
||||
print(table.concat(header_parts, " | "))
|
||||
print(table.concat(separator_parts, "-+-"))
|
||||
|
||||
-- Print rows
|
||||
for _, row in ipairs(results) do
|
||||
local value_parts = tbl.map(columns, function(col)
|
||||
local value_parts = table.map(columns, function(col)
|
||||
local value = tostring(row[col] or "")
|
||||
return string.pad_right(value, widths[col])
|
||||
end)
|
||||
print(tbl.concat(value_parts, " | "))
|
||||
print(table.concat(value_parts, " | "))
|
||||
end
|
||||
end
|
||||
|
||||
|
433
tests/table.lua
433
tests/table.lua
@ -1,5 +1,4 @@
|
||||
require("tests")
|
||||
local tbl = require("table")
|
||||
|
||||
-- Test data
|
||||
local simple_array = {1, 2, 3, 4, 5}
|
||||
@ -18,11 +17,11 @@ local nested_table = {
|
||||
test("Table Insert Operations", function()
|
||||
local t = {1, 2, 3}
|
||||
|
||||
tbl.insert(t, 4)
|
||||
table.insert(t, 4)
|
||||
assert_equal(4, #t)
|
||||
assert_equal(4, t[4])
|
||||
|
||||
tbl.insert(t, 2, "inserted")
|
||||
table.insert(t, 2, "inserted")
|
||||
assert_equal(5, #t)
|
||||
assert_equal("inserted", t[2])
|
||||
assert_equal(2, t[3])
|
||||
@ -31,11 +30,11 @@ end)
|
||||
test("Table Remove Operations", function()
|
||||
local t = {1, 2, 3, 4, 5}
|
||||
|
||||
local removed = tbl.remove(t)
|
||||
local removed = table.remove(t)
|
||||
assert_equal(5, removed)
|
||||
assert_equal(4, #t)
|
||||
|
||||
removed = tbl.remove(t, 2)
|
||||
removed = table.remove(t, 2)
|
||||
assert_equal(2, removed)
|
||||
assert_equal(3, #t)
|
||||
assert_equal(3, t[2])
|
||||
@ -43,23 +42,23 @@ end)
|
||||
|
||||
test("Table Concat", function()
|
||||
local t = {"hello", "world", "test"}
|
||||
assert_equal("helloworldtest", tbl.concat(t))
|
||||
assert_equal("hello,world,test", tbl.concat(t, ","))
|
||||
assert_equal("world,test", tbl.concat(t, ",", 2))
|
||||
assert_equal("world", tbl.concat(t, ",", 2, 2))
|
||||
assert_equal("helloworldtest", table.concat(t))
|
||||
assert_equal("hello,world,test", table.concat(t, ","))
|
||||
assert_equal("world,test", table.concat(t, ",", 2))
|
||||
assert_equal("world", table.concat(t, ",", 2, 2))
|
||||
end)
|
||||
|
||||
test("Table Sort", function()
|
||||
local t = {3, 1, 4, 1, 5}
|
||||
tbl.sort(t)
|
||||
table.sort(t)
|
||||
assert_table_equal({1, 1, 3, 4, 5}, t)
|
||||
|
||||
local t2 = {"c", "a", "b"}
|
||||
tbl.sort(t2)
|
||||
table.sort(t2)
|
||||
assert_table_equal({"a", "b", "c"}, t2)
|
||||
|
||||
local t3 = {3, 1, 4, 1, 5}
|
||||
tbl.sort(t3, function(a, b) return a > b end)
|
||||
table.sort(t3, function(a, b) return a > b end)
|
||||
assert_table_equal({5, 4, 3, 1, 1}, t3)
|
||||
end)
|
||||
|
||||
@ -68,39 +67,39 @@ end)
|
||||
-- ======================================================================
|
||||
|
||||
test("Table Length and Size", function()
|
||||
assert_equal(5, tbl.length(simple_array))
|
||||
assert_equal(0, tbl.length({}))
|
||||
assert_equal(5, table.length(simple_array))
|
||||
assert_equal(0, table.length({}))
|
||||
|
||||
assert_equal(3, tbl.size(simple_table))
|
||||
assert_equal(4, tbl.size(mixed_table))
|
||||
assert_equal(0, tbl.size({}))
|
||||
assert_equal(3, table.size(simple_table))
|
||||
assert_equal(4, table.size(mixed_table))
|
||||
assert_equal(0, table.size({}))
|
||||
end)
|
||||
|
||||
test("Table Empty Check", function()
|
||||
assert_equal(true, tbl.is_empty({}))
|
||||
assert_equal(false, tbl.is_empty(simple_array))
|
||||
assert_equal(false, tbl.is_empty(simple_table))
|
||||
assert_equal(true, table.is_empty({}))
|
||||
assert_equal(false, table.is_empty(simple_array))
|
||||
assert_equal(false, table.is_empty(simple_table))
|
||||
end)
|
||||
|
||||
test("Table Array Check", function()
|
||||
assert_equal(true, tbl.is_array(simple_array))
|
||||
assert_equal(true, tbl.is_array({}))
|
||||
assert_equal(false, tbl.is_array(simple_table))
|
||||
assert_equal(false, tbl.is_array(mixed_table))
|
||||
assert_equal(true, table.is_array(simple_array))
|
||||
assert_equal(true, table.is_array({}))
|
||||
assert_equal(false, table.is_array(simple_table))
|
||||
assert_equal(false, table.is_array(mixed_table))
|
||||
|
||||
assert_equal(true, tbl.is_array({1, 2, 3}))
|
||||
assert_equal(false, tbl.is_array({1, 2, nil, 4}))
|
||||
assert_equal(false, tbl.is_array({[0] = 1, [1] = 2}))
|
||||
assert_equal(true, table.is_array({1, 2, 3}))
|
||||
assert_equal(false, table.is_array({1, 2, nil, 4}))
|
||||
assert_equal(false, table.is_array({[0] = 1, [1] = 2}))
|
||||
end)
|
||||
|
||||
test("Table Clear", function()
|
||||
local t = tbl.clone(simple_table)
|
||||
tbl.clear(t)
|
||||
assert_equal(true, tbl.is_empty(t))
|
||||
local t = table.clone(simple_table)
|
||||
table.clear(t)
|
||||
assert_equal(true, table.is_empty(t))
|
||||
end)
|
||||
|
||||
test("Table Clone", function()
|
||||
local cloned = tbl.clone(simple_table)
|
||||
local cloned = table.clone(simple_table)
|
||||
assert_table_equal(simple_table, cloned)
|
||||
|
||||
-- Modify original shouldn't affect clone
|
||||
@ -110,7 +109,7 @@ test("Table Clone", function()
|
||||
end)
|
||||
|
||||
test("Table Deep Copy", function()
|
||||
local copied = tbl.deep_copy(nested_table)
|
||||
local copied = table.deep_copy(nested_table)
|
||||
assert_table_equal(nested_table, copied)
|
||||
|
||||
-- Modify nested part shouldn't affect copy
|
||||
@ -124,50 +123,50 @@ end)
|
||||
-- ======================================================================
|
||||
|
||||
test("Table Contains", function()
|
||||
assert_equal(true, tbl.contains(simple_array, 3))
|
||||
assert_equal(false, tbl.contains(simple_array, 6))
|
||||
assert_equal(true, tbl.contains(simple_table, 2))
|
||||
assert_equal(false, tbl.contains(simple_table, "hello"))
|
||||
assert_equal(true, table.contains(simple_array, 3))
|
||||
assert_equal(false, table.contains(simple_array, 6))
|
||||
assert_equal(true, table.contains(simple_table, 2))
|
||||
assert_equal(false, table.contains(simple_table, "hello"))
|
||||
end)
|
||||
|
||||
test("Table Index Of", function()
|
||||
assert_equal(3, tbl.index_of(simple_array, 3))
|
||||
assert_equal(nil, tbl.index_of(simple_array, 6))
|
||||
assert_equal("b", tbl.index_of(simple_table, 2))
|
||||
assert_equal(nil, tbl.index_of(simple_table, "hello"))
|
||||
assert_equal(3, table.index_of(simple_array, 3))
|
||||
assert_equal(nil, table.index_of(simple_array, 6))
|
||||
assert_equal("b", table.index_of(simple_table, 2))
|
||||
assert_equal(nil, table.index_of(simple_table, "hello"))
|
||||
end)
|
||||
|
||||
test("Table Find", function()
|
||||
local value, key = tbl.find(simple_array, function(v) return v > 3 end)
|
||||
local value, key = table.find(simple_array, function(v) return v > 3 end)
|
||||
assert_equal(4, value)
|
||||
assert_equal(4, key)
|
||||
|
||||
local value2, key2 = tbl.find(simple_table, function(v, k) return k == "b" end)
|
||||
local value2, key2 = table.find(simple_table, function(v, k) return k == "b" end)
|
||||
assert_equal(2, value2)
|
||||
assert_equal("b", key2)
|
||||
|
||||
local value3 = tbl.find(simple_array, function(v) return v > 10 end)
|
||||
local value3 = table.find(simple_array, function(v) return v > 10 end)
|
||||
assert_equal(nil, value3)
|
||||
end)
|
||||
|
||||
test("Table Find Index", function()
|
||||
local idx = tbl.find_index(simple_array, function(v) return v > 3 end)
|
||||
local idx = table.find_index(simple_array, function(v) return v > 3 end)
|
||||
assert_equal(4, idx)
|
||||
|
||||
local idx2 = tbl.find_index(simple_table, function(v, k) return k == "c" end)
|
||||
local idx2 = table.find_index(simple_table, function(v, k) return k == "c" end)
|
||||
assert_equal("c", idx2)
|
||||
|
||||
local idx3 = tbl.find_index(simple_array, function(v) return v > 10 end)
|
||||
local idx3 = table.find_index(simple_array, function(v) return v > 10 end)
|
||||
assert_equal(nil, idx3)
|
||||
end)
|
||||
|
||||
test("Table Count", function()
|
||||
local arr = {1, 2, 3, 2, 4, 2}
|
||||
assert_equal(3, tbl.count(arr, 2))
|
||||
assert_equal(0, tbl.count(arr, 5))
|
||||
assert_equal(3, table.count(arr, 2))
|
||||
assert_equal(0, table.count(arr, 5))
|
||||
|
||||
assert_equal(2, tbl.count(arr, function(v) return v > 2 end))
|
||||
assert_equal(2, tbl.count(arr, function(v) return v == 1 or v == 4 end))
|
||||
assert_equal(2, table.count(arr, function(v) return v > 2 end))
|
||||
assert_equal(2, table.count(arr, function(v) return v == 1 or v == 4 end))
|
||||
end)
|
||||
|
||||
-- ======================================================================
|
||||
@ -175,39 +174,39 @@ end)
|
||||
-- ======================================================================
|
||||
|
||||
test("Table Filter", function()
|
||||
local evens = tbl.filter(simple_array, function(v) return v % 2 == 0 end)
|
||||
local evens = table.filter(simple_array, function(v) return v % 2 == 0 end)
|
||||
assert_table_equal({2, 4}, evens)
|
||||
|
||||
local filtered_table = tbl.filter(simple_table, function(v) return v > 1 end)
|
||||
assert_equal(2, tbl.size(filtered_table))
|
||||
local filtered_table = table.filter(simple_table, function(v) return v > 1 end)
|
||||
assert_equal(2, table.size(filtered_table))
|
||||
assert_equal(2, filtered_table.b)
|
||||
assert_equal(3, filtered_table.c)
|
||||
end)
|
||||
|
||||
test("Table Reject", function()
|
||||
local odds = tbl.reject(simple_array, function(v) return v % 2 == 0 end)
|
||||
local odds = table.reject(simple_array, function(v) return v % 2 == 0 end)
|
||||
assert_table_equal({1, 3, 5}, odds)
|
||||
end)
|
||||
|
||||
test("Table Map", function()
|
||||
local doubled = tbl.map(simple_array, function(v) return v * 2 end)
|
||||
local doubled = table.map(simple_array, function(v) return v * 2 end)
|
||||
assert_table_equal({2, 4, 6, 8, 10}, doubled)
|
||||
|
||||
local mapped_table = tbl.map(simple_table, function(v) return v + 10 end)
|
||||
local mapped_table = table.map(simple_table, function(v) return v + 10 end)
|
||||
assert_equal(11, mapped_table.a)
|
||||
assert_equal(12, mapped_table.b)
|
||||
assert_equal(13, mapped_table.c)
|
||||
end)
|
||||
|
||||
test("Table Map Values", function()
|
||||
local incremented = tbl.map_values(simple_table, function(v) return v + 1 end)
|
||||
local incremented = table.map_values(simple_table, function(v) return v + 1 end)
|
||||
assert_equal(2, incremented.a)
|
||||
assert_equal(3, incremented.b)
|
||||
assert_equal(4, incremented.c)
|
||||
end)
|
||||
|
||||
test("Table Map Keys", function()
|
||||
local prefixed = tbl.map_keys(simple_table, function(k) return "key_" .. k end)
|
||||
local prefixed = table.map_keys(simple_table, function(k) return "key_" .. k end)
|
||||
assert_equal(1, prefixed.key_a)
|
||||
assert_equal(2, prefixed.key_b)
|
||||
assert_equal(3, prefixed.key_c)
|
||||
@ -219,34 +218,34 @@ end)
|
||||
-- ======================================================================
|
||||
|
||||
test("Table Reduce", function()
|
||||
local sum = tbl.reduce(simple_array, function(acc, v) return acc + v end)
|
||||
local sum = table.reduce(simple_array, function(acc, v) return acc + v end)
|
||||
assert_equal(15, sum)
|
||||
|
||||
local sum_with_initial = tbl.reduce(simple_array, function(acc, v) return acc + v end, 10)
|
||||
local sum_with_initial = table.reduce(simple_array, function(acc, v) return acc + v end, 10)
|
||||
assert_equal(25, sum_with_initial)
|
||||
|
||||
local product = tbl.reduce({2, 3, 4}, function(acc, v) return acc * v end)
|
||||
local product = table.reduce({2, 3, 4}, function(acc, v) return acc * v end)
|
||||
assert_equal(24, product)
|
||||
end)
|
||||
|
||||
test("Table Fold", function()
|
||||
local sum = tbl.fold(simple_array, function(acc, v) return acc + v end, 0)
|
||||
local sum = table.fold(simple_array, function(acc, v) return acc + v end, 0)
|
||||
assert_equal(15, sum)
|
||||
|
||||
local concatenated = tbl.fold({"a", "b", "c"}, function(acc, v) return acc .. v end, "")
|
||||
local concatenated = table.fold({"a", "b", "c"}, function(acc, v) return acc .. v end, "")
|
||||
assert_equal("abc", concatenated)
|
||||
end)
|
||||
|
||||
test("Table Math Operations", function()
|
||||
assert_equal(15, tbl.sum(simple_array))
|
||||
assert_equal(120, tbl.product(simple_array))
|
||||
assert_equal(1, tbl.min(simple_array))
|
||||
assert_equal(5, tbl.max(simple_array))
|
||||
assert_equal(3, tbl.average(simple_array))
|
||||
assert_equal(15, table.sum(simple_array))
|
||||
assert_equal(120, table.product(simple_array))
|
||||
assert_equal(1, table.min(simple_array))
|
||||
assert_equal(5, table.max(simple_array))
|
||||
assert_equal(3, table.average(simple_array))
|
||||
|
||||
local floats = {1.5, 2.5, 3.0}
|
||||
assert_close(7.0, tbl.sum(floats))
|
||||
assert_close(2.33333, tbl.average(floats), 0.001)
|
||||
assert_close(7.0, table.sum(floats))
|
||||
assert_close(2.33333, table.average(floats), 0.001)
|
||||
end)
|
||||
|
||||
-- ======================================================================
|
||||
@ -254,30 +253,30 @@ end)
|
||||
-- ======================================================================
|
||||
|
||||
test("Table All", function()
|
||||
assert_equal(true, tbl.all({true, true, true}))
|
||||
assert_equal(false, tbl.all({true, false, true}))
|
||||
assert_equal(true, tbl.all({}))
|
||||
assert_equal(true, table.all({true, true, true}))
|
||||
assert_equal(false, table.all({true, false, true}))
|
||||
assert_equal(true, table.all({}))
|
||||
|
||||
assert_equal(true, tbl.all(simple_array, function(v) return v > 0 end))
|
||||
assert_equal(false, tbl.all(simple_array, function(v) return v > 3 end))
|
||||
assert_equal(true, table.all(simple_array, function(v) return v > 0 end))
|
||||
assert_equal(false, table.all(simple_array, function(v) return v > 3 end))
|
||||
end)
|
||||
|
||||
test("Table Any", function()
|
||||
assert_equal(true, tbl.any({false, true, false}))
|
||||
assert_equal(false, tbl.any({false, false, false}))
|
||||
assert_equal(false, tbl.any({}))
|
||||
assert_equal(true, table.any({false, true, false}))
|
||||
assert_equal(false, table.any({false, false, false}))
|
||||
assert_equal(false, table.any({}))
|
||||
|
||||
assert_equal(true, tbl.any(simple_array, function(v) return v > 3 end))
|
||||
assert_equal(false, tbl.any(simple_array, function(v) return v > 10 end))
|
||||
assert_equal(true, table.any(simple_array, function(v) return v > 3 end))
|
||||
assert_equal(false, table.any(simple_array, function(v) return v > 10 end))
|
||||
end)
|
||||
|
||||
test("Table None", function()
|
||||
assert_equal(true, tbl.none({false, false, false}))
|
||||
assert_equal(false, tbl.none({false, true, false}))
|
||||
assert_equal(true, tbl.none({}))
|
||||
assert_equal(true, table.none({false, false, false}))
|
||||
assert_equal(false, table.none({false, true, false}))
|
||||
assert_equal(true, table.none({}))
|
||||
|
||||
assert_equal(false, tbl.none(simple_array, function(v) return v > 3 end))
|
||||
assert_equal(true, tbl.none(simple_array, function(v) return v > 10 end))
|
||||
assert_equal(false, table.none(simple_array, function(v) return v > 3 end))
|
||||
assert_equal(true, table.none(simple_array, function(v) return v > 10 end))
|
||||
end)
|
||||
|
||||
-- ======================================================================
|
||||
@ -286,36 +285,36 @@ end)
|
||||
|
||||
test("Table Unique", function()
|
||||
local duplicates = {1, 2, 2, 3, 3, 3, 4}
|
||||
local unique = tbl.unique(duplicates)
|
||||
local unique = table.unique(duplicates)
|
||||
assert_table_equal({1, 2, 3, 4}, unique)
|
||||
|
||||
local empty_unique = tbl.unique({})
|
||||
local empty_unique = table.unique({})
|
||||
assert_table_equal({}, empty_unique)
|
||||
end)
|
||||
|
||||
test("Table Intersection", function()
|
||||
local arr1 = {1, 2, 3, 4}
|
||||
local arr2 = {3, 4, 5, 6}
|
||||
local intersect = tbl.intersection(arr1, arr2)
|
||||
local intersect = table.intersection(arr1, arr2)
|
||||
assert_equal(2, #intersect)
|
||||
assert_equal(true, tbl.contains(intersect, 3))
|
||||
assert_equal(true, tbl.contains(intersect, 4))
|
||||
assert_equal(true, table.contains(intersect, 3))
|
||||
assert_equal(true, table.contains(intersect, 4))
|
||||
end)
|
||||
|
||||
test("Table Union", function()
|
||||
local arr1 = {1, 2, 3}
|
||||
local arr2 = {3, 4, 5}
|
||||
local union = tbl.union(arr1, arr2)
|
||||
local union = table.union(arr1, arr2)
|
||||
assert_equal(5, #union)
|
||||
for i = 1, 5 do
|
||||
assert_equal(true, tbl.contains(union, i))
|
||||
assert_equal(true, table.contains(union, i))
|
||||
end
|
||||
end)
|
||||
|
||||
test("Table Difference", function()
|
||||
local arr1 = {1, 2, 3, 4, 5}
|
||||
local arr2 = {3, 4}
|
||||
local diff = tbl.difference(arr1, arr2)
|
||||
local diff = table.difference(arr1, arr2)
|
||||
assert_table_equal({1, 2, 5}, diff)
|
||||
end)
|
||||
|
||||
@ -324,23 +323,23 @@ end)
|
||||
-- ======================================================================
|
||||
|
||||
test("Table Reverse", function()
|
||||
local reversed = tbl.reverse(simple_array)
|
||||
local reversed = table.reverse(simple_array)
|
||||
assert_table_equal({5, 4, 3, 2, 1}, reversed)
|
||||
|
||||
local single = tbl.reverse({42})
|
||||
local single = table.reverse({42})
|
||||
assert_table_equal({42}, single)
|
||||
|
||||
local empty = tbl.reverse({})
|
||||
local empty = table.reverse({})
|
||||
assert_table_equal({}, empty)
|
||||
end)
|
||||
|
||||
test("Table Shuffle", function()
|
||||
local shuffled = tbl.shuffle(simple_array)
|
||||
local shuffled = table.shuffle(simple_array)
|
||||
assert_equal(5, #shuffled)
|
||||
|
||||
-- All original elements should still be present
|
||||
for _, v in ipairs(simple_array) do
|
||||
assert_equal(true, tbl.contains(shuffled, v))
|
||||
assert_equal(true, table.contains(shuffled, v))
|
||||
end
|
||||
|
||||
-- Should be same length
|
||||
@ -350,30 +349,30 @@ end)
|
||||
test("Table Rotate", function()
|
||||
local arr = {1, 2, 3, 4, 5}
|
||||
|
||||
local rotated_right = tbl.rotate(arr, 2)
|
||||
local rotated_right = table.rotate(arr, 2)
|
||||
assert_table_equal({4, 5, 1, 2, 3}, rotated_right)
|
||||
|
||||
local rotated_left = tbl.rotate(arr, -2)
|
||||
local rotated_left = table.rotate(arr, -2)
|
||||
assert_table_equal({3, 4, 5, 1, 2}, rotated_left)
|
||||
|
||||
local no_rotation = tbl.rotate(arr, 0)
|
||||
local no_rotation = table.rotate(arr, 0)
|
||||
assert_table_equal(arr, no_rotation)
|
||||
|
||||
local full_rotation = tbl.rotate(arr, 5)
|
||||
local full_rotation = table.rotate(arr, 5)
|
||||
assert_table_equal(arr, full_rotation)
|
||||
end)
|
||||
|
||||
test("Table Slice", function()
|
||||
local sliced = tbl.slice(simple_array, 2, 4)
|
||||
local sliced = table.slice(simple_array, 2, 4)
|
||||
assert_table_equal({2, 3, 4}, sliced)
|
||||
|
||||
local from_start = tbl.slice(simple_array, 1, 3)
|
||||
local from_start = table.slice(simple_array, 1, 3)
|
||||
assert_table_equal({1, 2, 3}, from_start)
|
||||
|
||||
local to_end = tbl.slice(simple_array, 3)
|
||||
local to_end = table.slice(simple_array, 3)
|
||||
assert_table_equal({3, 4, 5}, to_end)
|
||||
|
||||
local negative_indices = tbl.slice(simple_array, -3, -1)
|
||||
local negative_indices = table.slice(simple_array, -3, -1)
|
||||
assert_table_equal({3, 4, 5}, negative_indices)
|
||||
end)
|
||||
|
||||
@ -381,19 +380,19 @@ test("Table Splice", function()
|
||||
local arr = {1, 2, 3, 4, 5}
|
||||
|
||||
-- Remove elements
|
||||
local removed = tbl.splice(arr, 2, 2)
|
||||
local removed = table.splice(arr, 2, 2)
|
||||
assert_table_equal({2, 3}, removed)
|
||||
assert_table_equal({1, 4, 5}, arr)
|
||||
|
||||
-- Insert elements
|
||||
arr = {1, 2, 3, 4, 5}
|
||||
removed = tbl.splice(arr, 3, 0, "a", "b")
|
||||
removed = table.splice(arr, 3, 0, "a", "b")
|
||||
assert_table_equal({}, removed)
|
||||
assert_table_equal({1, 2, "a", "b", 3, 4, 5}, arr)
|
||||
|
||||
-- Replace elements
|
||||
arr = {1, 2, 3, 4, 5}
|
||||
removed = tbl.splice(arr, 2, 2, "x", "y", "z")
|
||||
removed = table.splice(arr, 2, 2, "x", "y", "z")
|
||||
assert_table_equal({2, 3}, removed)
|
||||
assert_table_equal({1, "x", "y", "z", 4, 5}, arr)
|
||||
end)
|
||||
@ -409,23 +408,23 @@ test("Table Sort By", function()
|
||||
{name = "Charlie", age = 35}
|
||||
}
|
||||
|
||||
local sorted_by_age = tbl.sort_by(people, function(p) return p.age end)
|
||||
local sorted_by_age = table.sort_by(people, function(p) return p.age end)
|
||||
assert_equal("Bob", sorted_by_age[1].name)
|
||||
assert_equal("Charlie", sorted_by_age[3].name)
|
||||
|
||||
local sorted_by_name = tbl.sort_by(people, function(p) return p.name end)
|
||||
local sorted_by_name = table.sort_by(people, function(p) return p.name end)
|
||||
assert_equal("Alice", sorted_by_name[1].name)
|
||||
assert_equal("Charlie", sorted_by_name[3].name)
|
||||
end)
|
||||
|
||||
test("Table Is Sorted", function()
|
||||
assert_equal(true, tbl.is_sorted({1, 2, 3, 4, 5}))
|
||||
assert_equal(false, tbl.is_sorted({1, 3, 2, 4, 5}))
|
||||
assert_equal(true, tbl.is_sorted({}))
|
||||
assert_equal(true, tbl.is_sorted({42}))
|
||||
assert_equal(true, table.is_sorted({1, 2, 3, 4, 5}))
|
||||
assert_equal(false, table.is_sorted({1, 3, 2, 4, 5}))
|
||||
assert_equal(true, table.is_sorted({}))
|
||||
assert_equal(true, table.is_sorted({42}))
|
||||
|
||||
assert_equal(true, tbl.is_sorted({5, 4, 3, 2, 1}, function(a, b) return a > b end))
|
||||
assert_equal(false, tbl.is_sorted({1, 2, 3, 4, 5}, function(a, b) return a > b end))
|
||||
assert_equal(true, table.is_sorted({5, 4, 3, 2, 1}, function(a, b) return a > b end))
|
||||
assert_equal(false, table.is_sorted({1, 2, 3, 4, 5}, function(a, b) return a > b end))
|
||||
end)
|
||||
|
||||
-- ======================================================================
|
||||
@ -433,21 +432,21 @@ end)
|
||||
-- ======================================================================
|
||||
|
||||
test("Table Keys and Values", function()
|
||||
local keys = tbl.keys(simple_table)
|
||||
local keys = table.keys(simple_table)
|
||||
assert_equal(3, #keys)
|
||||
assert_equal(true, tbl.contains(keys, "a"))
|
||||
assert_equal(true, tbl.contains(keys, "b"))
|
||||
assert_equal(true, tbl.contains(keys, "c"))
|
||||
assert_equal(true, table.contains(keys, "a"))
|
||||
assert_equal(true, table.contains(keys, "b"))
|
||||
assert_equal(true, table.contains(keys, "c"))
|
||||
|
||||
local values = tbl.values(simple_table)
|
||||
local values = table.values(simple_table)
|
||||
assert_equal(3, #values)
|
||||
assert_equal(true, tbl.contains(values, 1))
|
||||
assert_equal(true, tbl.contains(values, 2))
|
||||
assert_equal(true, tbl.contains(values, 3))
|
||||
assert_equal(true, table.contains(values, 1))
|
||||
assert_equal(true, table.contains(values, 2))
|
||||
assert_equal(true, table.contains(values, 3))
|
||||
end)
|
||||
|
||||
test("Table Pairs", function()
|
||||
local pairs_list = tbl.pairs({a = 1, b = 2})
|
||||
local pairs_list = table.pairs({a = 1, b = 2})
|
||||
assert_equal(2, #pairs_list)
|
||||
|
||||
-- Should contain key-value pairs
|
||||
@ -465,8 +464,8 @@ test("Table Merge", function()
|
||||
local t2 = {c = 3, d = 4}
|
||||
local t3 = {b = 20, e = 5}
|
||||
|
||||
local merged = tbl.merge(t1, t2, t3)
|
||||
assert_equal(5, tbl.size(merged))
|
||||
local merged = table.merge(t1, t2, t3)
|
||||
assert_equal(5, table.size(merged))
|
||||
assert_equal(1, merged.a)
|
||||
assert_equal(20, merged.b) -- Last one wins
|
||||
assert_equal(3, merged.c)
|
||||
@ -478,15 +477,15 @@ test("Table Extend", function()
|
||||
local t1 = {a = 1, b = 2}
|
||||
local t2 = {c = 3, d = 4}
|
||||
|
||||
local extended = tbl.extend(t1, t2)
|
||||
local extended = table.extend(t1, t2)
|
||||
assert_equal(t1, extended) -- Should return t1
|
||||
assert_equal(4, tbl.size(t1))
|
||||
assert_equal(4, table.size(t1))
|
||||
assert_equal(3, t1.c)
|
||||
assert_equal(4, t1.d)
|
||||
end)
|
||||
|
||||
test("Table Invert", function()
|
||||
local inverted = tbl.invert(simple_table)
|
||||
local inverted = table.invert(simple_table)
|
||||
assert_equal("a", inverted[1])
|
||||
assert_equal("b", inverted[2])
|
||||
assert_equal("c", inverted[3])
|
||||
@ -495,16 +494,16 @@ end)
|
||||
test("Table Pick and Omit", function()
|
||||
local big_table = {a = 1, b = 2, c = 3, d = 4, e = 5}
|
||||
|
||||
local picked = tbl.pick(big_table, "a", "c", "e")
|
||||
assert_equal(3, tbl.size(picked))
|
||||
local picked = table.pick(big_table, "a", "c", "e")
|
||||
assert_equal(3, table.size(picked))
|
||||
assert_equal(1, picked.a)
|
||||
assert_equal(3, picked.c)
|
||||
assert_equal(5, picked.e)
|
||||
assert_equal(nil, picked.b)
|
||||
assert_equal(nil, picked.d)
|
||||
|
||||
local omitted = tbl.omit(big_table, "b", "d")
|
||||
assert_equal(3, tbl.size(omitted))
|
||||
local omitted = table.omit(big_table, "b", "d")
|
||||
assert_equal(3, table.size(omitted))
|
||||
assert_equal(1, omitted.a)
|
||||
assert_equal(3, omitted.c)
|
||||
assert_equal(5, omitted.e)
|
||||
@ -521,21 +520,21 @@ test("Table Deep Equals", function()
|
||||
local t2 = {a = {x = 1, y = 2}, b = {1, 2, 3}}
|
||||
local t3 = {a = {x = 1, y = 3}, b = {1, 2, 3}}
|
||||
|
||||
assert_equal(true, tbl.deep_equals(t1, t2))
|
||||
assert_equal(false, tbl.deep_equals(t1, t3))
|
||||
assert_equal(true, tbl.deep_equals({}, {}))
|
||||
assert_equal(false, tbl.deep_equals({a = 1}, {a = 1, b = 2}))
|
||||
assert_equal(true, table.deep_equals(t1, t2))
|
||||
assert_equal(false, table.deep_equals(t1, t3))
|
||||
assert_equal(true, table.deep_equals({}, {}))
|
||||
assert_equal(false, table.deep_equals({a = 1}, {a = 1, b = 2}))
|
||||
end)
|
||||
|
||||
test("Table Flatten", function()
|
||||
local nested = {{1, 2}, {3, 4}, {5, {6, 7}}}
|
||||
local flattened = tbl.flatten(nested)
|
||||
local flattened = table.flatten(nested)
|
||||
assert_table_equal({1, 2, 3, 4, 5, {6, 7}}, flattened)
|
||||
|
||||
local deep_flattened = tbl.flatten(nested, 2)
|
||||
local deep_flattened = table.flatten(nested, 2)
|
||||
assert_table_equal({1, 2, 3, 4, 5, 6, 7}, deep_flattened)
|
||||
|
||||
local already_flat = tbl.flatten({1, 2, 3})
|
||||
local already_flat = table.flatten({1, 2, 3})
|
||||
assert_table_equal({1, 2, 3}, already_flat)
|
||||
end)
|
||||
|
||||
@ -543,7 +542,7 @@ test("Table Deep Merge", function()
|
||||
local t1 = {a = {x = 1}, b = 2}
|
||||
local t2 = {a = {y = 3}, c = 4}
|
||||
|
||||
local merged = tbl.deep_merge(t1, t2)
|
||||
local merged = table.deep_merge(t1, t2)
|
||||
assert_equal(1, merged.a.x)
|
||||
assert_equal(3, merged.a.y)
|
||||
assert_equal(2, merged.b)
|
||||
@ -559,24 +558,24 @@ end)
|
||||
-- ======================================================================
|
||||
|
||||
test("Table Chunk", function()
|
||||
local chunks = tbl.chunk({1, 2, 3, 4, 5, 6, 7}, 3)
|
||||
local chunks = table.chunk({1, 2, 3, 4, 5, 6, 7}, 3)
|
||||
assert_equal(3, #chunks)
|
||||
assert_table_equal({1, 2, 3}, chunks[1])
|
||||
assert_table_equal({4, 5, 6}, chunks[2])
|
||||
assert_table_equal({7}, chunks[3])
|
||||
|
||||
local exact_chunks = tbl.chunk({1, 2, 3, 4}, 2)
|
||||
local exact_chunks = table.chunk({1, 2, 3, 4}, 2)
|
||||
assert_equal(2, #exact_chunks)
|
||||
assert_table_equal({1, 2}, exact_chunks[1])
|
||||
assert_table_equal({3, 4}, exact_chunks[2])
|
||||
end)
|
||||
|
||||
test("Table Partition", function()
|
||||
local evens, odds = tbl.partition(simple_array, function(v) return v % 2 == 0 end)
|
||||
local evens, odds = table.partition(simple_array, function(v) return v % 2 == 0 end)
|
||||
assert_table_equal({2, 4}, evens)
|
||||
assert_table_equal({1, 3, 5}, odds)
|
||||
|
||||
local empty_true, all_false = tbl.partition({1, 3, 5}, function(v) return v % 2 == 0 end)
|
||||
local empty_true, all_false = table.partition({1, 3, 5}, function(v) return v % 2 == 0 end)
|
||||
assert_table_equal({}, empty_true)
|
||||
assert_table_equal({1, 3, 5}, all_false)
|
||||
end)
|
||||
@ -589,8 +588,8 @@ test("Table Group By", function()
|
||||
{name = "David", department = "sales"}
|
||||
}
|
||||
|
||||
local by_dept = tbl.group_by(people, function(person) return person.department end)
|
||||
assert_equal(2, tbl.size(by_dept))
|
||||
local by_dept = table.group_by(people, function(person) return person.department end)
|
||||
assert_equal(2, table.size(by_dept))
|
||||
assert_equal(2, #by_dept.engineering)
|
||||
assert_equal(2, #by_dept.sales)
|
||||
assert_equal("Alice", by_dept.engineering[1].name)
|
||||
@ -602,14 +601,14 @@ test("Table Zip", function()
|
||||
local ages = {25, 30, 35}
|
||||
local cities = {"NYC", "LA", "Chicago"}
|
||||
|
||||
local zipped = tbl.zip(names, ages, cities)
|
||||
local zipped = table.zip(names, ages, cities)
|
||||
assert_equal(3, #zipped)
|
||||
assert_table_equal({"Alice", 25, "NYC"}, zipped[1])
|
||||
assert_table_equal({"Bob", 30, "LA"}, zipped[2])
|
||||
assert_table_equal({"Charlie", 35, "Chicago"}, zipped[3])
|
||||
|
||||
-- Different lengths
|
||||
local short_zip = tbl.zip({1, 2, 3}, {"a", "b"})
|
||||
local short_zip = table.zip({1, 2, 3}, {"a", "b"})
|
||||
assert_equal(2, #short_zip)
|
||||
assert_table_equal({1, "a"}, short_zip[1])
|
||||
assert_table_equal({2, "b"}, short_zip[2])
|
||||
@ -617,28 +616,28 @@ end)
|
||||
|
||||
test("Table Compact", function()
|
||||
local messy = {1, nil, false, 2, nil, 3, false}
|
||||
local compacted = tbl.compact(messy)
|
||||
local compacted = table.compact(messy)
|
||||
assert_table_equal({1, 2, 3}, compacted)
|
||||
|
||||
local clean = {1, 2, 3}
|
||||
local unchanged = tbl.compact(clean)
|
||||
local unchanged = table.compact(clean)
|
||||
assert_table_equal(clean, unchanged)
|
||||
end)
|
||||
|
||||
test("Table Sample", function()
|
||||
local sample1 = tbl.sample(simple_array, 3)
|
||||
local sample1 = table.sample(simple_array, 3)
|
||||
assert_equal(3, #sample1)
|
||||
|
||||
-- All sampled elements should be from original
|
||||
for _, v in ipairs(sample1) do
|
||||
assert_equal(true, tbl.contains(simple_array, v))
|
||||
assert_equal(true, table.contains(simple_array, v))
|
||||
end
|
||||
|
||||
local single_sample = tbl.sample(simple_array)
|
||||
local single_sample = table.sample(simple_array)
|
||||
assert_equal(1, #single_sample)
|
||||
assert_equal(true, tbl.contains(simple_array, single_sample[1]))
|
||||
assert_equal(true, table.contains(simple_array, single_sample[1]))
|
||||
|
||||
local oversample = tbl.sample({1, 2}, 5)
|
||||
local oversample = table.sample({1, 2}, 5)
|
||||
assert_equal(2, #oversample)
|
||||
end)
|
||||
|
||||
@ -649,37 +648,37 @@ end)
|
||||
test("Empty Table Handling", function()
|
||||
local empty = {}
|
||||
|
||||
assert_equal(true, tbl.is_empty(empty))
|
||||
assert_equal(0, tbl.length(empty))
|
||||
assert_equal(0, tbl.size(empty))
|
||||
assert_equal(true, tbl.is_array(empty))
|
||||
assert_equal(true, table.is_empty(empty))
|
||||
assert_equal(0, table.length(empty))
|
||||
assert_equal(0, table.size(empty))
|
||||
assert_equal(true, table.is_array(empty))
|
||||
|
||||
assert_table_equal({}, tbl.filter(empty, function() return true end))
|
||||
assert_table_equal({}, tbl.map(empty, function(v) return v * 2 end))
|
||||
assert_table_equal({}, tbl.keys(empty))
|
||||
assert_table_equal({}, tbl.values(empty))
|
||||
assert_table_equal({}, table.filter(empty, function() return true end))
|
||||
assert_table_equal({}, table.map(empty, function(v) return v * 2 end))
|
||||
assert_table_equal({}, table.keys(empty))
|
||||
assert_table_equal({}, table.values(empty))
|
||||
|
||||
assert_equal(true, tbl.all(empty))
|
||||
assert_equal(false, tbl.any(empty))
|
||||
assert_equal(true, tbl.none(empty))
|
||||
assert_equal(true, table.all(empty))
|
||||
assert_equal(false, table.any(empty))
|
||||
assert_equal(true, table.none(empty))
|
||||
end)
|
||||
|
||||
test("Single Element Tables", function()
|
||||
local single = {42}
|
||||
|
||||
assert_equal(1, tbl.length(single))
|
||||
assert_equal(1, tbl.size(single))
|
||||
assert_equal(true, tbl.is_array(single))
|
||||
assert_equal(false, tbl.is_empty(single))
|
||||
assert_equal(1, table.length(single))
|
||||
assert_equal(1, table.size(single))
|
||||
assert_equal(true, table.is_array(single))
|
||||
assert_equal(false, table.is_empty(single))
|
||||
|
||||
assert_equal(42, tbl.sum(single))
|
||||
assert_equal(42, tbl.product(single))
|
||||
assert_equal(42, tbl.min(single))
|
||||
assert_equal(42, tbl.max(single))
|
||||
assert_equal(42, tbl.average(single))
|
||||
assert_equal(42, table.sum(single))
|
||||
assert_equal(42, table.product(single))
|
||||
assert_equal(42, table.min(single))
|
||||
assert_equal(42, table.max(single))
|
||||
assert_equal(42, table.average(single))
|
||||
|
||||
assert_table_equal({42}, tbl.reverse(single))
|
||||
assert_table_equal({84}, tbl.map(single, function(v) return v * 2 end))
|
||||
assert_table_equal({42}, table.reverse(single))
|
||||
assert_table_equal({84}, table.map(single, function(v) return v * 2 end))
|
||||
end)
|
||||
|
||||
test("Circular Reference Handling", function()
|
||||
@ -689,7 +688,7 @@ test("Circular Reference Handling", function()
|
||||
t2.ref = t1
|
||||
|
||||
-- Deep copy should handle circular references
|
||||
local copied = tbl.deep_copy(t1)
|
||||
local copied = table.deep_copy(t1)
|
||||
assert_equal(1, copied.a)
|
||||
assert_equal(2, copied.ref.b)
|
||||
assert_equal(copied, copied.ref.ref) -- Should maintain circular structure
|
||||
@ -701,14 +700,14 @@ test("Large Table Performance", function()
|
||||
large[i] = i
|
||||
end
|
||||
|
||||
assert_equal(10000, tbl.length(large))
|
||||
assert_equal(true, tbl.is_array(large))
|
||||
assert_equal(50005000, tbl.sum(large)) -- Sum of 1 to 10000
|
||||
assert_equal(10000, table.length(large))
|
||||
assert_equal(true, table.is_array(large))
|
||||
assert_equal(50005000, table.sum(large)) -- Sum of 1 to 10000
|
||||
|
||||
local evens = tbl.filter(large, function(v) return v % 2 == 0 end)
|
||||
local evens = table.filter(large, function(v) return v % 2 == 0 end)
|
||||
assert_equal(5000, #evens)
|
||||
|
||||
local doubled = tbl.map(large, function(v) return v * 2 end)
|
||||
local doubled = table.map(large, function(v) return v * 2 end)
|
||||
assert_equal(10000, #doubled)
|
||||
assert_equal(2, doubled[1])
|
||||
assert_equal(20000, doubled[10000])
|
||||
@ -717,12 +716,12 @@ end)
|
||||
test("Mixed Type Table Handling", function()
|
||||
local mixed = {1, "hello", true, {a = 1}, function() end}
|
||||
|
||||
assert_equal(5, tbl.length(mixed))
|
||||
assert_equal(true, tbl.is_array(mixed))
|
||||
assert_equal(true, tbl.contains(mixed, "hello"))
|
||||
assert_equal(true, tbl.contains(mixed, true))
|
||||
assert_equal(5, table.length(mixed))
|
||||
assert_equal(true, table.is_array(mixed))
|
||||
assert_equal(true, table.contains(mixed, "hello"))
|
||||
assert_equal(true, table.contains(mixed, true))
|
||||
|
||||
local strings_only = tbl.filter(mixed, function(v) return type(v) == "string" end)
|
||||
local strings_only = table.filter(mixed, function(v) return type(v) == "string" end)
|
||||
assert_equal(1, #strings_only)
|
||||
assert_equal("hello", strings_only[1])
|
||||
end)
|
||||
@ -738,23 +737,23 @@ test("Performance Test", function()
|
||||
end
|
||||
|
||||
local start = os.clock()
|
||||
local filtered = tbl.filter(large_array, function(v) return v > 500 end)
|
||||
local filtered = table.filter(large_array, function(v) return v > 500 end)
|
||||
local filter_time = os.clock() - start
|
||||
|
||||
start = os.clock()
|
||||
local mapped = tbl.map(large_array, function(v) return v * 2 end)
|
||||
local mapped = table.map(large_array, function(v) return v * 2 end)
|
||||
local map_time = os.clock() - start
|
||||
|
||||
start = os.clock()
|
||||
local sum = tbl.sum(large_array)
|
||||
local sum = table.sum(large_array)
|
||||
local sum_time = os.clock() - start
|
||||
|
||||
start = os.clock()
|
||||
local sorted = tbl.sort_by(large_array, function(v) return v end)
|
||||
local sorted = table.sort_by(large_array, function(v) return v end)
|
||||
local sort_time = os.clock() - start
|
||||
|
||||
start = os.clock()
|
||||
local unique = tbl.unique(large_array)
|
||||
local unique = table.unique(large_array)
|
||||
local unique_time = os.clock() - start
|
||||
|
||||
print(string.format(" Filter %d elements: %.3fs", #filtered, filter_time))
|
||||
@ -767,7 +766,7 @@ test("Performance Test", function()
|
||||
assert_equal(#large_array, #mapped)
|
||||
assert(sum > 0, "sum should be positive")
|
||||
assert_equal(#large_array, #sorted)
|
||||
assert(tbl.is_sorted(sorted), "should be sorted")
|
||||
assert(table.is_sorted(sorted), "should be sorted")
|
||||
end)
|
||||
|
||||
-- ======================================================================
|
||||
@ -784,28 +783,28 @@ test("Data Processing Pipeline", function()
|
||||
}
|
||||
|
||||
-- Calculate total revenue per item
|
||||
local with_revenue = tbl.map(sales_data, function(item)
|
||||
local new_item = tbl.clone(item)
|
||||
local with_revenue = table.map(sales_data, function(item)
|
||||
local new_item = table.clone(item)
|
||||
new_item.revenue = item.price * item.quantity
|
||||
return new_item
|
||||
end)
|
||||
|
||||
-- Filter high-value items (revenue >= 100)
|
||||
local high_value = tbl.filter(with_revenue, function(item)
|
||||
local high_value = table.filter(with_revenue, function(item)
|
||||
return item.revenue >= 100
|
||||
end)
|
||||
|
||||
-- Group by category
|
||||
local by_category = tbl.group_by(high_value, function(item)
|
||||
local by_category = table.group_by(high_value, function(item)
|
||||
return item.category
|
||||
end)
|
||||
|
||||
-- Calculate total revenue by category
|
||||
local category_totals = tbl.map_values(by_category, function(items)
|
||||
return tbl.sum(tbl.map(items, function(item) return item.revenue end))
|
||||
local category_totals = table.map_values(by_category, function(items)
|
||||
return table.sum(table.map(items, function(item) return item.revenue end))
|
||||
end)
|
||||
|
||||
assert_equal(2, tbl.size(category_totals))
|
||||
assert_equal(2, table.size(category_totals))
|
||||
assert_equal(4650, category_totals.electronics) -- laptop: 2000, mouse: 250, phone: 2400
|
||||
assert_equal(100, category_totals.books) -- magazine: 100
|
||||
end)
|
||||
@ -819,15 +818,15 @@ test("Complex Data Transformation", function()
|
||||
}
|
||||
|
||||
-- Find Lua developers
|
||||
local lua_devs = tbl.filter(users, function(user)
|
||||
return tbl.contains(user.skills, "lua")
|
||||
local lua_devs = table.filter(users, function(user)
|
||||
return table.contains(user.skills, "lua")
|
||||
end)
|
||||
|
||||
-- Sort by age
|
||||
local sorted_lua_devs = tbl.sort_by(lua_devs, function(user) return user.age end)
|
||||
local sorted_lua_devs = table.sort_by(lua_devs, function(user) return user.age end)
|
||||
|
||||
-- Extract just names and ages
|
||||
local simplified = tbl.map(sorted_lua_devs, function(user)
|
||||
local simplified = table.map(sorted_lua_devs, function(user)
|
||||
return {name = user.name, age = user.age}
|
||||
end)
|
||||
|
||||
@ -837,7 +836,7 @@ test("Complex Data Transformation", function()
|
||||
assert_equal("Bob", simplified[3].name) -- Oldest
|
||||
|
||||
-- Group all users by age ranges
|
||||
local age_groups = tbl.group_by(users, function(user)
|
||||
local age_groups = table.group_by(users, function(user)
|
||||
if user.age < 30 then return "young"
|
||||
else return "experienced" end
|
||||
end)
|
||||
@ -855,26 +854,26 @@ test("Statistical Analysis", function()
|
||||
}
|
||||
|
||||
-- Calculate average score for each student
|
||||
local with_averages = tbl.map(test_scores, function(student)
|
||||
local avg = tbl.average(student.scores)
|
||||
local with_averages = table.map(test_scores, function(student)
|
||||
local avg = table.average(student.scores)
|
||||
return {
|
||||
student = student.student,
|
||||
scores = student.scores,
|
||||
average = avg,
|
||||
max_score = tbl.max(student.scores),
|
||||
min_score = tbl.min(student.scores)
|
||||
max_score = table.max(student.scores),
|
||||
min_score = table.min(student.scores)
|
||||
}
|
||||
end)
|
||||
|
||||
-- Find top performer
|
||||
local top_student = tbl.reduce(with_averages, function(best, current)
|
||||
local top_student = table.reduce(with_averages, function(best, current)
|
||||
return current.average > best.average and current or best
|
||||
end)
|
||||
|
||||
-- Students above class average
|
||||
local all_averages = tbl.map(with_averages, function(s) return s.average end)
|
||||
local class_average = tbl.average(all_averages)
|
||||
local above_average = tbl.filter(with_averages, function(s)
|
||||
local all_averages = table.map(with_averages, function(s) return s.average end)
|
||||
local class_average = table.average(all_averages)
|
||||
local above_average = table.filter(with_averages, function(s)
|
||||
return s.average > class_average
|
||||
end)
|
||||
|
||||
|
@ -1,65 +1,46 @@
|
||||
-- Enhanced Test Framework - Global Functions
|
||||
-- Provides better assert reporting and test runner functionality
|
||||
|
||||
-- Test state
|
||||
local passed = 0
|
||||
local total = 0
|
||||
|
||||
-- Enhanced assert function with better error reporting
|
||||
function assert(condition, message, level)
|
||||
if condition then
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
level = level or 2
|
||||
local info = debug.getinfo(level, "Sl")
|
||||
local file = info.source
|
||||
|
||||
|
||||
-- Extract filename from source or use generic name
|
||||
if file:sub(1,1) == "@" then
|
||||
file = file:sub(2) -- Remove @ prefix for files
|
||||
else
|
||||
file = "<script>" -- Generic name for inline scripts
|
||||
end
|
||||
|
||||
|
||||
local line = info.currentline or "unknown"
|
||||
local error_msg = message or "assertion failed"
|
||||
local full_msg = string.format("%s:%s: %s", file, line, error_msg)
|
||||
|
||||
|
||||
error(full_msg, 0)
|
||||
end
|
||||
|
||||
-- Assert with tolerance for floating point comparisons
|
||||
function assert_close(expected, actual, tolerance, message)
|
||||
tolerance = tolerance or 1e-10
|
||||
local diff = math.abs(expected - actual)
|
||||
if diff <= tolerance then
|
||||
return true
|
||||
end
|
||||
|
||||
local msg = message or string.format("Expected %g, got %g (diff: %g, tolerance: %g)", expected, actual, diff, tolerance)
|
||||
assert(false, msg, 3)
|
||||
end
|
||||
|
||||
-- Assert equality with better error messages
|
||||
function assert_equal(expected, actual, message)
|
||||
if expected == actual then
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
local msg = message or string.format("Expected %s, got %s", tostring(expected), tostring(actual))
|
||||
assert(false, msg, 3)
|
||||
end
|
||||
|
||||
-- Assert table equality (deep comparison)
|
||||
function assert_table_equal(expected, actual, message, path)
|
||||
path = path or "root"
|
||||
|
||||
|
||||
if type(expected) ~= type(actual) then
|
||||
local msg = message or string.format("Type mismatch at %s: expected %s, got %s", path, type(expected), type(actual))
|
||||
assert(false, msg, 3)
|
||||
end
|
||||
|
||||
|
||||
if type(expected) ~= "table" then
|
||||
if expected ~= actual then
|
||||
local msg = message or string.format("Value mismatch at %s: expected %s, got %s", path, tostring(expected), tostring(actual))
|
||||
@ -67,7 +48,7 @@ function assert_table_equal(expected, actual, message, path)
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
-- Check all keys in a exist in b with same values
|
||||
for k, v in pairs(expected) do
|
||||
local new_path = path .. "." .. tostring(k)
|
||||
@ -77,7 +58,7 @@ function assert_table_equal(expected, actual, message, path)
|
||||
end
|
||||
assert_table_equal(v, actual[k], message, new_path)
|
||||
end
|
||||
|
||||
|
||||
-- Check all keys in b exist in a
|
||||
for k, v in pairs(actual) do
|
||||
if expected[k] == nil then
|
||||
@ -86,20 +67,19 @@ function assert_table_equal(expected, actual, message, path)
|
||||
assert(false, msg, 3)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
-- Test runner function
|
||||
function test(name, fn)
|
||||
print("Testing " .. name .. "...")
|
||||
total = total + 1
|
||||
|
||||
|
||||
local start_time = os.clock()
|
||||
local ok, err = pcall(fn)
|
||||
local end_time = os.clock()
|
||||
local duration = end_time - start_time
|
||||
|
||||
|
||||
if ok then
|
||||
passed = passed + 1
|
||||
print(string.format(" ✓ PASS (%.3fs)", duration))
|
||||
@ -113,25 +93,22 @@ function test(name, fn)
|
||||
end
|
||||
end
|
||||
|
||||
-- Test suite runner
|
||||
function run_tests(tests)
|
||||
print("Running test suite...")
|
||||
print("=" .. string.rep("=", 50))
|
||||
|
||||
|
||||
for name, test_fn in pairs(tests) do
|
||||
test(name, test_fn)
|
||||
end
|
||||
|
||||
|
||||
return summary()
|
||||
end
|
||||
|
||||
-- Reset test counters
|
||||
function reset_tests()
|
||||
passed = 0
|
||||
total = 0
|
||||
end
|
||||
|
||||
-- Get test statistics
|
||||
function test_stats()
|
||||
return {
|
||||
passed = passed,
|
||||
@ -141,11 +118,10 @@ function test_stats()
|
||||
}
|
||||
end
|
||||
|
||||
-- Print test summary and return success status
|
||||
function summary()
|
||||
print("=" .. string.rep("=", 50))
|
||||
print(string.format("Test Results: %d/%d passed", passed, total))
|
||||
|
||||
|
||||
local success = passed == total
|
||||
if success then
|
||||
print("🎉 All tests passed!")
|
||||
@ -154,32 +130,29 @@ function summary()
|
||||
local rate = total > 0 and (passed / total * 100) or 0
|
||||
print(string.format("❌ %d test(s) failed! (%.1f%% success rate)", failed, rate))
|
||||
end
|
||||
|
||||
|
||||
return success
|
||||
end
|
||||
|
||||
-- Exit with appropriate code based on test results
|
||||
function test_exit()
|
||||
local success = passed == total
|
||||
os.exit(success and 0 or 1)
|
||||
end
|
||||
|
||||
-- Convenience function to run and exit
|
||||
function run_and_exit(tests)
|
||||
local success = run_tests(tests)
|
||||
os.exit(success and 0 or 1)
|
||||
end
|
||||
|
||||
-- Benchmark function
|
||||
function benchmark(name, fn, iterations)
|
||||
iterations = iterations or 1000
|
||||
print("Benchmarking " .. name .. " (" .. iterations .. " iterations)...")
|
||||
|
||||
|
||||
-- Warmup
|
||||
for i = 1, math.min(10, iterations) do
|
||||
fn()
|
||||
end
|
||||
|
||||
|
||||
-- Actual benchmark
|
||||
local start = os.clock()
|
||||
for i = 1, iterations do
|
||||
@ -187,10 +160,10 @@ function benchmark(name, fn, iterations)
|
||||
end
|
||||
local total_time = os.clock() - start
|
||||
local avg_time = total_time / iterations
|
||||
|
||||
print(string.format(" Total: %.3fs, Average: %.6fs, Rate: %.0f ops/sec",
|
||||
|
||||
print(string.format(" Total: %.3fs, Average: %.6fs, Rate: %.0f ops/sec",
|
||||
total_time, avg_time, 1/avg_time))
|
||||
|
||||
|
||||
return {
|
||||
total_time = total_time,
|
||||
avg_time = avg_time,
|
||||
@ -199,7 +172,6 @@ function benchmark(name, fn, iterations)
|
||||
}
|
||||
end
|
||||
|
||||
-- Helper to check if file exists
|
||||
function file_exists(filename)
|
||||
local file = io.open(filename, "r")
|
||||
if file then
|
||||
@ -207,4 +179,4 @@ function file_exists(filename)
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
Loading…
x
Reference in New Issue
Block a user