1
0
mirror of https://github.com/SquidDev-CC/CC-Tweaked synced 2024-12-12 03:00:30 +00:00

Add utility to check table keys

expect.field acts very similarly to expect.expect, though checks a
specific table key rather than a function argument.
This commit is contained in:
SquidDev 2020-04-18 14:38:20 +01:00
parent 865fc239a0
commit 10a27a7a25
2 changed files with 87 additions and 36 deletions

View File

@ -5,28 +5,29 @@
local native_select, native_type = select, type
--- Expect an argument to have a specific type.
--
-- @tparam number index The 1-based argument index.
-- @param value The argument's value.
-- @tparam string ... The allowed types of the argument.
-- @throws If the value is not one of the allowed types.
local function expect(index, value, ...)
local t = native_type(value)
for i = 1, native_select("#", ...) do
if t == native_select(i, ...) then return true end
end
local function get_type_names(...)
local types = table.pack(...)
for i = types.n, 1, -1 do
if types[i] == "nil" then table.remove(types, i) end
end
local type_names
if #types <= 1 then
type_names = tostring(...)
return tostring(...)
else
type_names = table.concat(types, ", ", 1, #types - 1) .. " or " .. types[#types]
return table.concat(types, ", ", 1, #types - 1) .. " or " .. types[#types]
end
end
--- Expect an argument to have a specific type.
--
-- @tparam number index The 1-based argument index.
-- @param value The argument's value.
-- @tparam string ... The allowed types of the argument.
-- @return The given `value`.
-- @throws If the value is not one of the allowed types.
local function expect(index, value, ...)
local t = native_type(value)
for i = 1, native_select("#", ...) do
if t == native_select(i, ...) then return value end
end
-- If we can determine the function name with a high level of confidence, try to include it.
@ -36,6 +37,7 @@ local function expect(index, value, ...)
if ok and info.name and #info.name ~= "" and info.what ~= "C" then name = info.name end
end
local type_names = get_type_names(...)
if name then
error(("bad argument #%d to '%s' (expected %s, got %s)"):format(index, name, type_names, t), 3)
else
@ -43,4 +45,31 @@ local function expect(index, value, ...)
end
end
return { expect = expect }
--- Expect an field to have a specific type.
--
-- @tparam table tbl The table to index.
-- @tparam string index The field name to check.
-- @tparam string ... The allowed types of the argument.
-- @return The contents of the given field.
-- @throws If the field is not one of the allowed types.
local function field(tbl, index, ...)
expect(1, tbl, "table")
expect(2, index, "string")
local value = tbl[index]
local t = native_type(value)
for i = 1, native_select("#", ...) do
if t == native_select(i, ...) then return value end
end
if value == nil then
error(("field '%s' missing from table"):format(index), 3)
else
error(("bad field '%s' (expected %s, got %s)"):format(index, get_type_names(...), t), 3)
end
end
return {
expect = expect,
field = field,
}

View File

@ -1,31 +1,53 @@
describe("cc.expect", function()
local e = require("cc.expect")
it("checks a single type", function()
expect(e.expect(1, "test", "string")):eq(true)
expect(e.expect(1, 2, "number")):eq(true)
describe("expect", function()
it("checks a single type", function()
expect(e.expect(1, "test", "string")):eq("test")
expect(e.expect(1, 2, "number")):eq(2)
expect.error(e.expect, 1, nil, "string"):eq("bad argument #1 (expected string, got nil)")
expect.error(e.expect, 2, 1, "nil"):eq("bad argument #2 (expected nil, got number)")
expect.error(e.expect, 1, nil, "string"):eq("bad argument #1 (expected string, got nil)")
expect.error(e.expect, 2, 1, "nil"):eq("bad argument #2 (expected nil, got number)")
end)
it("checks multiple types", function()
expect(e.expect(1, "test", "string", "number")):eq("test")
expect(e.expect(1, 2, "string", "number")):eq(2)
expect.error(e.expect, 1, nil, "string", "number"):eq("bad argument #1 (expected string or number, got nil)")
expect.error(e.expect, 2, false, "string", "table", "number", "nil")
:eq("bad argument #2 (expected string, table or number, got boolean)")
end)
it("includes the function name", function()
local function worker()
expect(e.expect(1, nil, "string")):eq(true)
end
local function trampoline()
worker()
end
expect.error(trampoline):eq("expect_spec.lua:27: bad argument #1 to 'worker' (expected string, got nil)")
end)
end)
it("checks multiple types", function()
expect(e.expect(1, "test", "string", "number")):eq(true)
expect(e.expect(1, 2, "string", "number")):eq(true)
describe("field", function()
it("checks a single type", function()
expect(e.field({ k = "test" }, "k", "string")):eq("test")
expect(e.field({ k = 2 }, "k", "number")):eq(2)
expect.error(e.expect, 1, nil, "string", "number"):eq("bad argument #1 (expected string or number, got nil)")
expect.error(e.expect, 2, false, "string", "table", "number", "nil")
:eq("bad argument #2 (expected string, table or number, got boolean)")
end)
expect.error(e.field, { k = nil }, "k", "string"):eq("field 'k' missing from table")
expect.error(e.field, { l = 1 }, "l", "nil"):eq("bad field 'l' (expected nil, got number)")
end)
it("includes the function name", function()
local function worker()
expect(e.expect(1, nil, "string")):eq(true)
end
local function trampoline()
worker()
end
it("checks multiple types", function()
expect(e.field({ k = "test" }, "k", "string", "number")):eq("test")
expect(e.field({ k = 2 }, "k", "string", "number")):eq(2)
expect.error(trampoline):eq("expect_spec.lua:26: bad argument #1 to 'worker' (expected string, got nil)")
expect.error(e.field, { k = nil }, "k", "string", "number")
:eq("field 'k' missing from table")
expect.error(e.field, { l = false }, "l", "string", "table", "number", "nil")
:eq("bad field 'l' (expected string, table or number, got boolean)")
end)
end)
end)