You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
104 lines
4.0 KiB
104 lines
4.0 KiB
-- Lua-implemented part of the utils module, extending the global utils table.
|
|
-- The native parts of the module (dump, toJSON, fromJSON, NULL, isNull)
|
|
-- are declared in Rust before this file is loaded.
|
|
|
|
-- Substituted when a callback throws a nil error value (e.g. plain `error()`),
|
|
-- so that the returned err is always non-nil on failure.
|
|
local NIL_ERROR_PLACEHOLDER = "unspecified error"
|
|
|
|
--- @brief Call a function, capturing any error - like pcall, but with Go-style returns.
|
|
---
|
|
--- local value, err = utils.try(callback)
|
|
--- local value, err = utils.try(callback, arg1, arg2) -- pcall-style shorthand
|
|
--- if err then
|
|
--- print(err)
|
|
--- else
|
|
--- -- handle value
|
|
--- end
|
|
---
|
|
--- - err is nil if the callback succeeded, otherwise the error value
|
|
--- - value is the callback's first return value (nil on error)
|
|
--- - extra arguments are passed to the callback, like with pcall
|
|
---
|
|
--- This is less confusing than pcall when the return value is actually needed.
|
|
--- The thrown error value is passed through as-is, so a table thrown with
|
|
--- error({code = 42}) stays a table. The downside is that the result can't be
|
|
--- used directly as a condition in "if" or "while" - simply use pcall there.
|
|
---
|
|
--- @param callback function: Function to call
|
|
--- @param ... any: Arguments passed to the callback
|
|
--- @return any, any: The callback's first return value and nil, or nil and the error
|
|
function utils.try(callback, ...)
|
|
if type(callback) ~= "function" then
|
|
error("utils.try: callback is not a function (got " .. type(callback) .. ")")
|
|
end
|
|
|
|
local ok, result = pcall(callback, ...)
|
|
if ok then
|
|
return result, nil
|
|
end
|
|
if result == nil then
|
|
result = NIL_ERROR_PLACEHOLDER
|
|
end
|
|
return nil, result
|
|
end
|
|
|
|
local TRYN_MAX_COUNT = 64
|
|
|
|
--- @brief Call a function, capturing any error - like utils.try, but for N return values.
|
|
---
|
|
--- local value1, value2, err = utils.tryn(2, callback)
|
|
--- local value1, value2, err = utils.tryn(2, callback, arg1, arg2) -- pcall-style shorthand
|
|
--- if err then
|
|
--- print(err)
|
|
--- else
|
|
--- -- handle (value1, value2)
|
|
--- end
|
|
---
|
|
--- - err is nil if the callback succeeded, otherwise the error value
|
|
--- - values are the callback's return values, padded with nils or truncated to exactly N
|
|
--- - extra arguments are passed to the callback, like with pcall
|
|
---
|
|
--- This is less confusing than pcall for multiple return values, where the error
|
|
--- and the first value share a slot:
|
|
---
|
|
--- local status, errorOrValue1, value2 = pcall(callback)
|
|
---
|
|
--- @param expectedCount number: How many return values to pass through (integer, 0 to 64)
|
|
--- @param callback function: Function to call
|
|
--- @param ... any: Arguments passed to the callback
|
|
--- @return any ...: expectedCount values followed by the error or nil
|
|
function utils.tryn(expectedCount, callback, ...)
|
|
local count = math.tointeger(expectedCount)
|
|
if count == nil then
|
|
error("utils.tryn: expectedCount must be an integer number")
|
|
end
|
|
expectedCount = count
|
|
if expectedCount < 0 then
|
|
error("utils.tryn: expectedCount must not be negative")
|
|
end
|
|
if expectedCount > TRYN_MAX_COUNT then
|
|
error("utils.tryn: at most " .. TRYN_MAX_COUNT .. " return values are supported")
|
|
end
|
|
if type(callback) ~= "function" then
|
|
error("utils.tryn: callback is not a function (got " .. type(callback) .. ")")
|
|
end
|
|
|
|
local results = table.pack(pcall(callback, ...))
|
|
|
|
if results[1] then
|
|
-- Success: pass through values 2..expectedCount+1 (truncating or nil-padding),
|
|
-- with nil in the error slot. The explicit assignment overwrites a possible
|
|
-- extra return value so it cannot leak into the error slot.
|
|
results[expectedCount + 2] = nil
|
|
return table.unpack(results, 2, expectedCount + 2)
|
|
end
|
|
|
|
local err = results[2]
|
|
if err == nil then
|
|
err = NIL_ERROR_PLACEHOLDER
|
|
end
|
|
local out = {}
|
|
out[expectedCount + 1] = err
|
|
return table.unpack(out, 1, expectedCount + 1)
|
|
end
|
|
|