|
22 | 22 |
|
23 | 23 | local M = {} |
24 | 24 |
|
| 25 | +---Enable/Disable spec.lua assertions |
| 26 | +M.assertions_enabled = true |
| 27 | + |
25 | 28 | ---Internal functions to be overwritten when desired |
26 | 29 | ---@type table<string, function> |
27 | | -M.fn = {} |
| 30 | +M.fn = { |
| 31 | + ---Assert function |
| 32 | + ---@type fun(assertion: boolean, message: string) |
| 33 | + assert = function(assertion, message) |
| 34 | + assert(assertion, string.format("%s\n%s", message, debug.traceback())) |
| 35 | + end, |
28 | 36 |
|
29 | | ----Assert function |
30 | | ----@type fun(assertion: boolean, message: string) |
31 | | -M.fn.assert = function(assertion, message) |
32 | | - assert(assertion, message) |
33 | | -end |
| 37 | + ---Print error function |
| 38 | + ---@type fun(reason: string) |
| 39 | + error = function(reason) |
| 40 | + error(string.format("%s\n%s", reason, debug.traceback())) |
| 41 | + end, |
34 | 42 |
|
35 | | ----Print error function |
36 | | ----@type fun(reason: string) |
37 | | -M.fn.error = function(reason) |
38 | | - error(reason) |
39 | | -end |
| 43 | + ---Converts table to simple string representation |
| 44 | + ---@param t table |
| 45 | + ---@param indent integer|nil |
| 46 | + ---@return string |
| 47 | + table_to_string = function(t, indent) |
| 48 | + indent = indent or 0 |
| 49 | + local str = string.rep(" ", indent) .. "{\n" |
| 50 | + for k, v in pairs(t) do |
| 51 | + local key_str = type(k) == "string" and '"' .. k .. '"' or "[" .. tostring(k) .. "]" |
| 52 | + if type(v) == "table" then |
| 53 | + str = str .. string.rep(" ", indent + 1) .. key_str .. " = " .. M.fn.table_to_string(v, indent + 1) |
| 54 | + else |
| 55 | + str = str .. string.rep(" ", indent + 1) .. key_str .. " = " .. tostring(v) .. ",\n" |
| 56 | + end |
| 57 | + end |
| 58 | + str = str .. string.rep(" ", indent) .. "}" |
| 59 | + return str |
| 60 | + end, |
40 | 61 |
|
41 | | ----Pretty printer function |
42 | | ----@type fun(object: any): string |
43 | | -M.fn.pretty_print = function(object) |
44 | | - return tostring(object) |
45 | | -end |
| 62 | + ---Pretty printer function |
| 63 | + ---@type fun(object: any): string |
| 64 | + pretty_print = function(object) |
| 65 | + if type(object) == "function" then |
| 66 | + local info = debug.getinfo(object, "nSl") |
| 67 | + return string.format("fn %s(...) defined at %s:%d", info.name or "anonymous", info.source, info.linedefined) |
| 68 | + elseif type(object) == "table" then |
| 69 | + return M.fn.table_to_string(object) |
| 70 | + end |
| 71 | + |
| 72 | + return tostring(object) |
| 73 | + end, |
| 74 | +} |
46 | 75 |
|
47 | 76 | -- predicates |
48 | 77 |
|
@@ -174,7 +203,18 @@ function M.keys(spec_map) |
174 | 203 | for key, sub_spec in pairs(spec_map) do |
175 | 204 | local val = value[key] |
176 | 205 |
|
177 | | - if val == nil or not (type(sub_spec) == "function" and sub_spec(val)) then |
| 206 | + if type(sub_spec) ~= "function" then |
| 207 | + M.fn.error( |
| 208 | + string.format( |
| 209 | + "spec.lua: Spec '%s' for key '%s' must be a function (e.g. spec.keys for tables)", |
| 210 | + M.fn.pretty_print(sub_spec), |
| 211 | + M.fn.pretty_print(key) |
| 212 | + ) |
| 213 | + ) |
| 214 | + return false |
| 215 | + end |
| 216 | + |
| 217 | + if not sub_spec(val) then |
178 | 218 | return false |
179 | 219 | end |
180 | 220 | end |
@@ -217,14 +257,16 @@ end |
217 | 257 | ---@param value T |
218 | 258 | ---@return T |
219 | 259 | function M.assert(spec, value) |
220 | | - M.fn.assert( |
221 | | - M.valid(spec, value), |
222 | | - string.format( |
223 | | - "spec.lua: Assertion failed because '%s' doesn't conform to spec '%s'", |
224 | | - M.fn.pretty_print(value), |
225 | | - M.fn.pretty_print(spec) |
| 260 | + if M.assertions_enabled then |
| 261 | + M.fn.assert( |
| 262 | + M.valid(spec, value), |
| 263 | + string.format( |
| 264 | + "spec.lua: Assertion failed because '%s' doesn't conform to spec '%s'", |
| 265 | + M.fn.pretty_print(value), |
| 266 | + M.fn.pretty_print(spec) |
| 267 | + ) |
226 | 268 | ) |
227 | | - ) |
| 269 | + end |
228 | 270 | return value |
229 | 271 | end |
230 | 272 |
|
|
0 commit comments