Открыть меню
Открыть персональное меню
Вы не представились системе
Your IP address will be publicly visible if you make any edits.

Модуль:ResultOrError

Материал из wiki.iccup.org
Версия от 18:34, 24 сентября 2024; DarkMuse (обсуждение | вклад) (Новая страница: «--- -- @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 R...»)
(разн.) ← Предыдущая версия | Текущая версия (разн.) | Следующая версия → (разн.)

Для документации этого модуля может быть создана страница Модуль: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