API have been very heavily modified.
This commit is contained in:
parent
12adee0398
commit
fe65aeed90
152
errors.lua
152
errors.lua
|
@ -1,7 +1,9 @@
|
||||||
|
|
||||||
|
-- Check for possible loading errors
|
||||||
|
|
||||||
local string_dist
|
local string_dist
|
||||||
do
|
do
|
||||||
local thispath = ... and select('1', ...):match('.+%.') or ''
|
local thispath = ... and (...):match('.+%.') or ''
|
||||||
local function import (name, ignore_failure)
|
local function import (name, ignore_failure)
|
||||||
local was_loaded, lib_or_error = pcall(require, thispath..name)
|
local was_loaded, lib_or_error = pcall(require, thispath..name)
|
||||||
if not was_loaded then
|
if not was_loaded then
|
||||||
|
@ -14,16 +16,26 @@ do
|
||||||
string_dist = import 'string_distance'
|
string_dist = import 'string_distance'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if not debug then
|
||||||
|
error('[errors/loading]: Expected the debug library to be available in _G.')
|
||||||
|
elseif not debug.getinfo then
|
||||||
|
error('[errors/loading]: Expected debug.getinfo to be available in the debug library.')
|
||||||
|
end
|
||||||
|
|
||||||
assert(debug and debug.getinfo)
|
--------------------------------------------------------------------------------
|
||||||
|
-- Constants
|
||||||
|
|
||||||
|
local DEFAULT_ERROR_MSG_NORMAL = 'Unknown error occured'
|
||||||
|
local DEFAULT_ERROR_MSG_INTERNAL = 'Unknown internal error occured'
|
||||||
|
local DEFAULT_ERROR_MSG_CORRECT = 'Unexpected input "%s", maybe you meant %s?'
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
-- Util
|
-- Util
|
||||||
|
|
||||||
local function format_probable_strings (probable_strings, amount)
|
local function format_probable_strings (probable_strings, amount)
|
||||||
assert(type(probable_strings) == 'table')
|
assert(type(probable_strings) == 'table')
|
||||||
assert(type(amount) == 'number')
|
|
||||||
assert(#probable_strings > 0)
|
assert(#probable_strings > 0)
|
||||||
|
assert(type(amount) == 'number')
|
||||||
|
|
||||||
local l = {}
|
local l = {}
|
||||||
for i = 1, math.min(amount, #probable_strings) do
|
for i = 1, math.min(amount, #probable_strings) do
|
||||||
|
@ -36,70 +48,96 @@ local function format_probable_strings (probable_strings, amount)
|
||||||
end
|
end
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
-- Error handler
|
|
||||||
|
|
||||||
local ErrorHandler = setmetatable({}, {__call = function (c, ...) return c.new(...) end})
|
local function internal_error (self, module_suffix, format_msg, ...)
|
||||||
ErrorHandler.__index = ErrorHandler
|
-- internal_error(error_handler, module_suffix [, format_msg, ...])
|
||||||
|
|
||||||
function ErrorHandler.new (module_name)
|
-- Error check
|
||||||
return setmetatable({module_name = module_name, registered = {}}, ErrorHandler)
|
assert(type(self) == 'table' and self.is_error_handler)
|
||||||
|
assert(type(module_suffix) == 'string')
|
||||||
|
format_msg = format_msg or DEFAULT_ERROR_MSG_NORMAL
|
||||||
|
assert(type(format_msg) == 'string')
|
||||||
|
-- Format
|
||||||
|
return error(('[%s%s]: '..format_msg):format(self.module_name, module_suffix, ...), 3)
|
||||||
end
|
end
|
||||||
|
|
||||||
function ErrorHandler:register (f)
|
local function external_error (self, module_suffix, format_msg, ...)
|
||||||
assert(type(self) == 'table')
|
-- external_error(error_handler, module_suffix [, format_msg, ...])
|
||||||
assert(type(f) == 'function')
|
|
||||||
self.registered[f] = true
|
-- Error check
|
||||||
|
assert(type(self) == 'table' and self.is_error_handler)
|
||||||
|
assert(type(module_suffix) == 'string')
|
||||||
|
format_msg = format_msg or DEFAULT_ERROR_MSG_INTERNAL
|
||||||
|
assert(type(format_msg) == 'string')
|
||||||
|
-- Find error level
|
||||||
|
local level = 3
|
||||||
|
while self.registered[debug.getinfo(level, 'f').func] do
|
||||||
|
level = level + 1
|
||||||
|
end
|
||||||
|
-- Format
|
||||||
|
return error(('[%s%s]: '..format_msg):format(self.module_name, module_suffix, ...), level -1)
|
||||||
end
|
end
|
||||||
|
|
||||||
local ERROR_MODES = { ['internal'] = true, ['external'] = true }
|
local function correct_error (self, module_suffix, format_msg, gotten_string, possible_strings)
|
||||||
|
-- correct_error(error_handler, module_suffix [, format_msg], gotten_string, possible_strings)
|
||||||
|
|
||||||
function ErrorHandler:getErrorFunc (mode, name)
|
-- Error check
|
||||||
|
assert(type(self) == 'table' and self.is_error_handler)
|
||||||
|
assert(type(module_suffix) == 'string')
|
||||||
|
format_msg = format_msg or DEFAULT_ERROR_MSG_CORRECT
|
||||||
|
assert(type(format_msg) == 'string')
|
||||||
|
assert(type(gotten_string) == 'string')
|
||||||
|
assert(type(possible_strings) == 'table')
|
||||||
|
|
||||||
-- Parameter fixing
|
-- Do stuff
|
||||||
local mode = mode or 'internal'
|
local possible_strings = string_dist.strings_with_highest_similarity(gotten_string, possible_strings)
|
||||||
local name = name or (mode == 'external' and '') or mode
|
local list_string = format_probable_strings(possible_strings, 3)
|
||||||
if name ~= '' then name = '/'..name end
|
-- Format
|
||||||
|
return error(('[%s%s]: '..format_msg):format(self.module_name, module_suffix, gotten_string, list_string), 3)
|
||||||
|
end
|
||||||
|
|
||||||
-- Error checking
|
|
||||||
assert(ERROR_MODES[mode])
|
|
||||||
assert(type(name) == 'string')
|
|
||||||
|
|
||||||
-- Create error funcs.
|
local ErrorHandler_mt = {__call = function (self, ...) internal_error(self, '', ...) end}
|
||||||
if mode == 'internal' then
|
|
||||||
return function (format_msg, ...)
|
|
||||||
assert(type(format_msg) == 'string')
|
return function (module_name)
|
||||||
return error(('[%s%s]: '..format_msg):format(self.module_name, name, ...), 2)
|
assert(type(module_name) == 'string')
|
||||||
end
|
|
||||||
elseif mode == 'external' then
|
local err_hdl = setmetatable({}, ErrorHandler_mt)
|
||||||
return function (format_msg, ...)
|
err_hdl.is_error_handler = true
|
||||||
assert(type(format_msg) == 'string')
|
err_hdl.module_name = module_name
|
||||||
local level = 2
|
err_hdl.registered = {}
|
||||||
while self.registered[debug.getinfo(level, 'f').func] do
|
err_hdl.any_registered = false
|
||||||
level = level + 1
|
|
||||||
end
|
function err_hdl.internal (...)
|
||||||
return error(('[%s%s]: '..format_msg):format(self.module_name, name, ...), level)
|
local args = {...}
|
||||||
|
if args[1] == err_hdl then table.remove(args, 1) end
|
||||||
|
internal_error(err_hdl, '/internal', unpack(args))
|
||||||
|
end
|
||||||
|
|
||||||
|
function err_hdl.external (...)
|
||||||
|
local args = {...}
|
||||||
|
if args[1] == err_hdl then table.remove(args, 1) end
|
||||||
|
external_error(err_hdl, '', unpack(args))
|
||||||
|
end
|
||||||
|
|
||||||
|
function err_hdl.correct (...)
|
||||||
|
local args = {...}
|
||||||
|
if args[1] == err_hdl then table.remove(args, 1) end
|
||||||
|
if #args == 2 then table.insert(args, 1, DEFAULT_ERROR_MSG_CORRECT) end
|
||||||
|
correct_error(err_hdl, '', unpack(args))
|
||||||
|
end
|
||||||
|
|
||||||
|
function err_hdl.register (...)
|
||||||
|
local args = {...}
|
||||||
|
if args[1] == err_hdl then table.remove(args, 1) end
|
||||||
|
assert(#args > 0)
|
||||||
|
for i = 1, #args do
|
||||||
|
assert(type(args[i]) == 'function')
|
||||||
|
err_hdl.registered[args[i]] = true
|
||||||
|
err_hdl.any_registered = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
assert(false)
|
|
||||||
|
return err_hdl
|
||||||
end
|
end
|
||||||
|
|
||||||
function ErrorHandler:attemptCorrection (format_msg, gotten_string, probable_strings)
|
|
||||||
assert(type(self) == 'table', 'Wow, mister, remember to call with OO notation.')
|
|
||||||
assert(type(format_msg) == 'string')
|
|
||||||
assert(type(gotten_string) == 'string')
|
|
||||||
assert(type(probable_strings) == 'table')
|
|
||||||
|
|
||||||
local probable_strings = string_dist.strings_with_highest_similarity(gotten_string, probable_strings)
|
|
||||||
local list_string = format_probable_strings(probable_strings, 3)
|
|
||||||
return self:getErrorFunc('internal', '')(format_msg, gotten_string, list_string)
|
|
||||||
end
|
|
||||||
|
|
||||||
function ErrorHandler:__call (format_msg, ...)
|
|
||||||
return self:getErrorFunc('internal')(format_msg, ...)
|
|
||||||
end
|
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
return {
|
|
||||||
ErrorHandler = ErrorHandler
|
|
||||||
}
|
|
||||||
|
|
2
init.lua
2
init.lua
|
@ -1,2 +1,2 @@
|
||||||
|
|
||||||
return require 'errors.errors'
|
return require (((...) ~= 'init' and (...) .. '.' or '') .. 'errors')
|
||||||
|
|
|
@ -1,69 +1,95 @@
|
||||||
|
|
||||||
local error_handler = require('errors').ErrorHandler 'errors_test'
|
local SUITE = require 'TestSuite' 'errors'
|
||||||
|
|
||||||
local SUITE = require('TestSuite').new('errors')
|
|
||||||
SUITE:setEnviroment {
|
SUITE:setEnviroment {
|
||||||
error_handler = error_handler,
|
error = require 'errors' 'test_errors'
|
||||||
external_error = error_handler:getErrorFunc 'external',
|
|
||||||
internal_error = error_handler:getErrorFunc 'internal',
|
|
||||||
}
|
}
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
-- Basic errors functionallity
|
-- Basic errors functions
|
||||||
|
|
||||||
SUITE:addTest('errors basic functionallity', function()
|
SUITE:addTest('Basic error', function()
|
||||||
local func_a = function () external_error 'Hello World' end
|
local expected_error = './test/test_errors.lua:'..curline(1)..': [test_errors]: Hello World'
|
||||||
local func_b, func_b_line = function () func_a() end, curline()
|
assert_equal(expected_error, select(2, pcall(error, 'Hello World')))
|
||||||
error_handler:register(func_a)
|
|
||||||
|
|
||||||
local status, error_msg = pcall(func_b)
|
|
||||||
assert_equal(false, status)
|
|
||||||
local expected_error = './test/test_errors.lua:'..func_b_line..': [errors_test]: Hello World'
|
|
||||||
assert_equal(expected_error, error_msg)
|
|
||||||
end)
|
end)
|
||||||
|
|
||||||
SUITE:addTest('errors deep nesting works', function()
|
SUITE:addTest('Basic formatting', function()
|
||||||
local func_a = function () external_error 'Hello World' end
|
local expected_error = './test/test_errors.lua:'..curline(1)..': [test_errors]: Welcome to the Errors'
|
||||||
local func_b, func_b_line = function () func_a() end, curline()
|
assert_equal(expected_error, select(2, pcall(error, '%s to the %s', 'Welcome', 'Errors')))
|
||||||
local func_c, func_c_line = function () func_b() end, curline()
|
|
||||||
error_handler:register(func_a)
|
|
||||||
error_handler:register(func_b)
|
|
||||||
|
|
||||||
local status, error_msg = pcall(func_c)
|
|
||||||
assert_equal(false, status)
|
|
||||||
local expected_error = './test/test_errors.lua:'..func_c_line..': [errors_test]: Hello World'
|
|
||||||
assert_equal(expected_error, error_msg)
|
|
||||||
end)
|
end)
|
||||||
|
|
||||||
SUITE:addTest('internal errors also works', function()
|
SUITE:addTest('Default generic error', function()
|
||||||
local func_a, func_a_line = function () internal_error 'Hello World' end, curline()
|
local expected_error = './test/test_errors.lua:'..curline(1)..': [test_errors]: Unknown error occured'
|
||||||
local func_b, func_b_line = function () func_a() end, curline()
|
assert_equal(expected_error, select(2, pcall(error)))
|
||||||
local func_c, func_c_line = function () func_b() end, curline()
|
|
||||||
error_handler:register(func_a)
|
|
||||||
error_handler:register(func_b)
|
|
||||||
|
|
||||||
local status, error_msg = pcall(func_c)
|
|
||||||
assert_equal(false, status)
|
|
||||||
local expected_error = './test/test_errors.lua:'..func_a_line..': [errors_test/internal]: Hello World'
|
|
||||||
assert_equal(expected_error, error_msg)
|
|
||||||
end)
|
end)
|
||||||
|
|
||||||
SUITE:addTest('errors modes can be overwritten', function()
|
SUITE:addTest('Internal is almost the same as error in itself', function()
|
||||||
local extra_error = error_handler:getErrorFunc('external', 'extra')
|
local expected_error = './test/test_errors.lua:'..curline(1)..': [test_errors/internal]: World Hello'
|
||||||
local func, func_line = function () extra_error 'Hi' end, curline()
|
assert_equal(expected_error, select(2, pcall(error.internal, 'World Hello')))
|
||||||
|
|
||||||
local status, error_msg = pcall(func)
|
|
||||||
local expected_error = './test/test_errors.lua:'..func_line..': [errors_test/extra]: Hi'
|
|
||||||
assert_equal(expected_error, error_msg)
|
|
||||||
end)
|
end)
|
||||||
|
|
||||||
SUITE:addTest('errors modes can be overwritten, including internal', function()
|
SUITE:addTest('Internal can also be called using object index', function()
|
||||||
local extra_error = error_handler:getErrorFunc('internal', 'extra')
|
local expected_error = './test/test_errors.lua:'..curline(1)..': [test_errors/internal]: André the Giant'
|
||||||
local func, func_line = function () extra_error 'Hi' end, curline()
|
assert_equal(expected_error, select(2, pcall(error.internal, error, 'André the Giant')))
|
||||||
|
end)
|
||||||
|
|
||||||
local status, error_msg = pcall(func)
|
SUITE:addTest('Internal includes the stack', function()
|
||||||
local expected_error = './test/test_errors.lua:'..func_line..': [errors_test/extra]: Hi'
|
local func_a = function() error.internal 'Thomas the Steam Train' end
|
||||||
assert_equal(expected_error, error_msg)
|
local expected_error = './test/test_errors.lua:'..curline(-1)..': [test_errors/internal]: Thomas the Steam Train'
|
||||||
|
assert_equal(expected_error, select(2, pcall(func_a)))
|
||||||
|
end)
|
||||||
|
|
||||||
|
SUITE:addTest('Internal includes the stack 2', function()
|
||||||
|
local func_a = function() error.internal 'Thomas the Steam Train' end
|
||||||
|
local func_b = function() return pcall(func_a) end
|
||||||
|
local expected_error = './test/test_errors.lua:'..curline(-2)..': [test_errors/internal]: Thomas the Steam Train'
|
||||||
|
assert_equal(expected_error, select(2, pcall(func_a)))
|
||||||
|
end)
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
-- External
|
||||||
|
|
||||||
|
SUITE:addTest('External errors hides the stack', function()
|
||||||
|
local func_a = function () error.external 'Hello World' end
|
||||||
|
local func_b = function () func_a() end
|
||||||
|
error.register(func_a)
|
||||||
|
|
||||||
|
local expected_error = './test/test_errors.lua:'..curline(-3)..': [test_errors]: Hello World'
|
||||||
|
assert_equal(expected_error, select(2, pcall(func_b)))
|
||||||
|
end)
|
||||||
|
|
||||||
|
SUITE:addTest('External errors can be triggered deep', function()
|
||||||
|
local func_a = function () error.external 'Hello World' end
|
||||||
|
local func_b = function () func_a() end
|
||||||
|
local func_c = function () func_b() end
|
||||||
|
error.register(func_a, func_b)
|
||||||
|
|
||||||
|
local expected_error = './test/test_errors.lua:'..curline(-3)..': [test_errors]: Hello World'
|
||||||
|
assert_equal(expected_error, select(2, pcall(func_c)))
|
||||||
|
end)
|
||||||
|
|
||||||
|
SUITE:addTest('Internal errors don\'t hide the stack', function()
|
||||||
|
local func_a = function () error.internal 'Hello World' end
|
||||||
|
local func_b = function () func_a() end
|
||||||
|
local func_c = function () func_b() end
|
||||||
|
error.register(func_a, func_b)
|
||||||
|
|
||||||
|
local expected_error = './test/test_errors.lua:'..curline(-5)..': [test_errors/internal]: Hello World'
|
||||||
|
assert_equal(expected_error, select(2, pcall(func_c)))
|
||||||
|
end)
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
-- Correcting errors
|
||||||
|
|
||||||
|
SUITE:addTest('Correct errors helps with autocorrection', function()
|
||||||
|
local _, err_msg = pcall(error.correct, 'Not %s, maybe: %s', 'help', {'Help', 'whelp', 'halp'})
|
||||||
|
local expected_error = './test/test_errors.lua:'..curline(-1)..': [test_errors]: Not help, maybe: Help, whelp or halp'
|
||||||
|
assert_equal(expected_error, err_msg)
|
||||||
|
end)
|
||||||
|
|
||||||
|
SUITE:addTest('Correct errors has nice default string', function()
|
||||||
|
local _, err_msg = pcall(error.correct, 'Vlard', {'Vlad', 'Question Mark', 'Frankenstein'})
|
||||||
|
local expected_error = './test/test_errors.lua:'..curline(-1)..': [test_errors]: Unexpected input "Vlard", maybe you meant Vlad, Question Mark or Frankenstein?'
|
||||||
|
assert_equal(expected_error, err_msg)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
|
|
||||||
local SUITE = require('TestSuite').new('string_distance')
|
local SUITE = require 'TestSuite' 'string_distance'
|
||||||
SUITE:setEnviroment {
|
SUITE:setEnviroment {
|
||||||
levenshtein = require('string_distance').levenshtein,
|
levenshtein = require('string_distance').levenshtein,
|
||||||
longest_common_subsequence = require('string_distance').longest_common_subsequence,
|
longest_common_subsequence = require('string_distance').longest_common_subsequence,
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
|
|
||||||
package.path = package.path .. ';./test/?.lua;./src/?.lua'
|
package.path = package.path .. ';./test/?.lua;./src/?.lua'
|
||||||
|
|
||||||
local TEST_SUITE = require("TestSuite").new('errors')
|
local TEST_SUITE = require 'TestSuite' 'errors'
|
||||||
TEST_SUITE:addModules('test/test_*')
|
TEST_SUITE:addModules 'test/test_*'
|
||||||
TEST_SUITE:setOptions(...)
|
TEST_SUITE:setOptions(...)
|
||||||
TEST_SUITE:runTests()
|
TEST_SUITE:runTests()
|
||||||
|
|
Loading…
Reference in New Issue
Block a user