1
0
mirror of https://github.com/SquidDev-CC/CC-Tweaked synced 2024-12-12 11:10:29 +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 local native_select, native_type = select, type
--- Expect an argument to have a specific type. local function get_type_names(...)
--
-- @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 types = table.pack(...) local types = table.pack(...)
for i = types.n, 1, -1 do for i = types.n, 1, -1 do
if types[i] == "nil" then table.remove(types, i) end if types[i] == "nil" then table.remove(types, i) end
end end
local type_names
if #types <= 1 then if #types <= 1 then
type_names = tostring(...) return tostring(...)
else 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 end
-- If we can determine the function name with a high level of confidence, try to include it. -- 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 if ok and info.name and #info.name ~= "" and info.what ~= "C" then name = info.name end
end end
local type_names = get_type_names(...)
if name then if name then
error(("bad argument #%d to '%s' (expected %s, got %s)"):format(index, name, type_names, t), 3) error(("bad argument #%d to '%s' (expected %s, got %s)"):format(index, name, type_names, t), 3)
else else
@ -43,4 +45,31 @@ local function expect(index, value, ...)
end end
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,17 +1,18 @@
describe("cc.expect", function() describe("cc.expect", function()
local e = require("cc.expect") local e = require("cc.expect")
describe("expect", function()
it("checks a single type", function() it("checks a single type", function()
expect(e.expect(1, "test", "string")):eq(true) expect(e.expect(1, "test", "string")):eq("test")
expect(e.expect(1, 2, "number")):eq(true) 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, 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, 2, 1, "nil"):eq("bad argument #2 (expected nil, got number)")
end) end)
it("checks multiple types", function() it("checks multiple types", function()
expect(e.expect(1, "test", "string", "number")):eq(true) expect(e.expect(1, "test", "string", "number")):eq("test")
expect(e.expect(1, 2, "string", "number")):eq(true) 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, 1, nil, "string", "number"):eq("bad argument #1 (expected string or number, got nil)")
expect.error(e.expect, 2, false, "string", "table", "number", "nil") expect.error(e.expect, 2, false, "string", "table", "number", "nil")
@ -26,6 +27,27 @@ describe("cc.expect", function()
worker() worker()
end end
expect.error(trampoline):eq("expect_spec.lua:26: bad argument #1 to 'worker' (expected string, got nil)") expect.error(trampoline):eq("expect_spec.lua:27: bad argument #1 to 'worker' (expected string, got nil)")
end)
end)
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.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("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(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)
end) end)