From ae4af71822f659947dedde97f7b772fdf2bef1ab Mon Sep 17 00:00:00 2001 From: Sky Johnson Date: Thu, 24 Jul 2025 16:52:28 -0500 Subject: [PATCH] fix table as global mod --- modules/mysql/mysql.lua | 101 ++++--- modules/postgres/postgres.lua | 69 +++-- modules/sqlite/sqlite.lua | 79 +++-- modules/{table => table+}/table.lua | 0 tests/table.lua | 433 ++++++++++++++-------------- tests/tests.lua | 72 ++--- 6 files changed, 361 insertions(+), 393 deletions(-) rename modules/{table => table+}/table.lua (100%) diff --git a/modules/mysql/mysql.lua b/modules/mysql/mysql.lua index d675f65..3b1dccd 100644 --- a/modules/mysql/mysql.lua +++ b/modules/mysql/mysql.lua @@ -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 diff --git a/modules/postgres/postgres.lua b/modules/postgres/postgres.lua index 709ddb3..7e7c681 100644 --- a/modules/postgres/postgres.lua +++ b/modules/postgres/postgres.lua @@ -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 diff --git a/modules/sqlite/sqlite.lua b/modules/sqlite/sqlite.lua index 6677608..3b8f5f4 100644 --- a/modules/sqlite/sqlite.lua +++ b/modules/sqlite/sqlite.lua @@ -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 diff --git a/modules/table/table.lua b/modules/table+/table.lua similarity index 100% rename from modules/table/table.lua rename to modules/table+/table.lua diff --git a/tests/table.lua b/tests/table.lua index 5240d62..1f2e9ad 100644 --- a/tests/table.lua +++ b/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) diff --git a/tests/tests.lua b/tests/tests.lua index ec8a19f..aeac696 100644 --- a/tests/tests.lua +++ b/tests/tests.lua @@ -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 = "