Дополнительные действия
Для документации этого модуля может быть создана страница Модуль:ResultOrError/doc
---
-- @iCCup
-- wiki=commons
-- page=Module:ResultOrError
--
-- Please see https://github.com/iCCup/Lua-Modules to contribute
--
local Class = require('Module:Class')
local Error = require('Module:Error')
--[[
Класс ResultOrError представляет результат или ошибку.
Он используется для обработки исхода функции, которая может выбросить ошибку.
]]
---@class ResultOrError: BaseClass
local ResultOrError = Class.new(function(self)
-- ResultOrError является абстрактным классом.
end)
-- Применяет функцию к результату или обрабатывает ошибку.
---@param f? fun(any: any): any
---@param onError? fun(error: Error?): any
---@return ResultOrError
function ResultOrError:map(f, onError) error('Abstract method') end
-- Возвращает результат или выбрасывает ошибку
---@return any
function ResultOrError:get() error('Abstract method') end
---@param onError fun(error: Error?): any
---@return ResultOrError
function ResultOrError:catch(onError)
return self:map(nil, onError)
end
---@param f fun(error: Error?): any
---@return ResultOrError
function ResultOrError:finally(f)
local ret = self:map(f, f)
if ret:isError() then
return ret
end
return self
end
---@return boolean
function ResultOrError:isResult()
return Class.instanceOf(self, ResultOrError.Result)
end
---@return boolean
function ResultOrError:isError()
return Class.instanceOf(self, ResultOrError.Error)
end
--[[
Кейс с результатом
]]
---@class RoEResult: ResultOrError
---@field result any
ResultOrError.Result = Class.new(ResultOrError, function(self, result)
self.result = result
end)
---@param f? fun(any: any): any
---@param _ any
---@return RoEResult|RoEError
function ResultOrError.Result:map(f, _)
return f
and ResultOrError.try(function() return f(self.result) end)
or self
end
---@return any
function ResultOrError.Result:get()
return self.result
end
--[[
Кейс с ошибкой
]]
---@class RoEError: ResultOrError
---@field error Error
ResultOrError.Error = Class.new(ResultOrError, function(self, error)
self.error = error
end)
---@param _ any
---@param onError? fun(error: Error?): any
---@return RoEResult|RoEError
function ResultOrError.Error:map(_, onError)
return onError
and ResultOrError.try(function() return onError(self.error) end, self.error)
or ResultOrError.Result()
end
---@return any
function ResultOrError.Error:get()
error(self.error)
end
--[[
Вызывает функцию и помещает результат или ошибку в ResultOrError.
Если результат является экземпляром ResultOrError, то он "разворачивается".
]]
---@param f fun(): any
---@param originalError table?
---@return RoEResult|RoEError
function ResultOrError.try(f, originalError)
local resultOrError
xpcall(
function()
local result = f()
local isResultOrError = Class.instanceOf(result, ResultOrError)
resultOrError = isResultOrError
and result
or ResultOrError.Result(result)
end,
function(any)
local error = Error.isError(any) and any or Error(any)
if originalError and error ~= originalError then
if type(error.originalErrors) ~= 'table' then
error.originalErrors = {}
end
table.insert(error.originalErrors, originalError)
elseif not error.noStack then
if type(error.stacks) ~= 'table' then
error.stacks = {}
end
table.insert(error.stacks, 1, debug.traceback())
end
resultOrError = ResultOrError.Error(error)
end
)
return resultOrError
end
--[[
Если все входные ResultOrError — результаты, то возвращается массив результатов.
В противном случае возвращается первая ошибка.
]]
---@param resultOrErrors ResultOrError[]
---@return RoEResult|RoEError
function ResultOrError.all(resultOrErrors)
local results = {}
for _, resultOrError in ipairs(resultOrErrors) do
if resultOrError:isResult() then
table.insert(results, resultOrError.result)
else
return resultOrError
end
end
return ResultOrError.Result(results)
end
--[[
Если хотя бы один из входных ResultOrError — результат, то возвращается первый такой.
Иначе возвращается совокупная ошибка из всех ошибок.
]]
---@param resultOrErrors ResultOrError[]
---@return RoEResult|RoEError
function ResultOrError.any(resultOrErrors)
local errors = {}
for _, resultOrError in ipairs(resultOrErrors) do
if resultOrError:isResult() then
return resultOrError
else
table.insert(errors, resultOrError.error)
end
end
local error = {
childErrors = errors,
message = table.concat(errors, '\n'),
stacks = {debug.traceback()},
}
return ResultOrError.Error(error)
end
return ResultOrError