Error handling overhaul

This commit is contained in:
Pablo Ariel Mayobre 2023-02-14 18:14:24 -03:00
parent 743d662ef9
commit 892f4d4700
6 changed files with 57 additions and 28 deletions

View file

@ -16,19 +16,19 @@ Component.__mt = {
-- @treturn Component A new ComponentClass -- @treturn Component A new ComponentClass
function Component.new(name, populate) function Component.new(name, populate)
if (type(name) ~= "string") then if (type(name) ~= "string") then
error("bad argument #1 to 'Component.new' (string expected, got "..type(name)..")", 2) Utils.error(2, "bad argument #1 to 'Component.new' (string expected, got %s)", type(name))
end end
if (string.match(name, Components.__REJECT_MATCH) ~= "") then if (string.match(name, Components.__REJECT_MATCH) ~= "") then
error("bad argument #1 to 'Component.new' (Component names can't start with '"..Components.__REJECT_PREFIX.."', got "..name..")", 2) Utils.error(2, "bad argument #1 to 'Component.new' (Component names can't start with '%s', got %s)", Components.__REJECT_PREFIX, name)
end end
if (rawget(Components, name)) then if (rawget(Components, name)) then
error("bad argument #1 to 'Component.new' (ComponentClass with name '"..name.."' was already registerd)", 2) -- luacheck: ignore Utils.error(2, "bad argument #1 to 'Component.new' (ComponentClass with name '%s' was already registerd)", name) -- luacheck: ignore
end end
if (type(populate) ~= "function" and type(populate) ~= "nil") then if (type(populate) ~= "function" and type(populate) ~= "nil") then
error("bad argument #1 to 'Component.new' (function/nil expected, got "..type(populate)..")", 2) Utils.error(2, "bad argument #1 to 'Component.new' (function/nil expected, got %s)", type(populate))
end end
local componentClass = setmetatable({ local componentClass = setmetatable({

View file

@ -18,7 +18,7 @@ Entity.__mt = {
-- @treturn Entity A new Entity -- @treturn Entity A new Entity
function Entity.new(world) function Entity.new(world)
if (world ~= nil and not Type.isWorld(world)) then if (world ~= nil and not Type.isWorld(world)) then
error("bad argument #1 to 'Entity.new' (world/nil expected, got "..type(world)..")", 2) Utils.error(2, "bad argument #1 to 'Entity.new' (world/nil expected, got %s)", type(world))
end end
local e = setmetatable({ local e = setmetatable({
@ -68,7 +68,7 @@ function Entity:give(name, ...)
local ok, componentClass = Components.try(name) local ok, componentClass = Components.try(name)
if not ok then if not ok then
error("bad argument #1 to 'Entity:get' ("..componentClass..")", 2) Utils.error(2, "bad argument #1 to 'Entity:get' (%s)", componentClass)
end end
give(self, name, componentClass, ...) give(self, name, componentClass, ...)
@ -85,7 +85,7 @@ function Entity:ensure(name, ...)
local ok, componentClass = Components.try(name) local ok, componentClass = Components.try(name)
if not ok then if not ok then
error("bad argument #1 to 'Entity:ensure' ("..componentClass..")", 2) Utils.error(2, "bad argument #1 to 'Entity:ensure' (%s)", componentClass)
end end
if self[name] then if self[name] then
@ -104,7 +104,7 @@ function Entity:remove(name)
local ok, componentClass = Components.try(name) local ok, componentClass = Components.try(name)
if not ok then if not ok then
error("bad argument #1 to 'Entity:remove' ("..componentClass..")", 2) Utils.error(2, "bad argument #1 to 'Entity:remove' (%s)", componentClass)
end end
remove(self, name) remove(self, name)
@ -118,7 +118,7 @@ end
-- @treturn Entity self -- @treturn Entity self
function Entity:assemble(assemblage, ...) function Entity:assemble(assemblage, ...)
if type(assemblage) ~= "function" then if type(assemblage) ~= "function" then
error("bad argument #1 to 'Entity:assemble' (function expected, got "..type(assemblage)..")") Utils.error(2, "bad argument #1 to 'Entity:assemble' (function expected, got %s)", type(assemblage))
end end
assemblage(self, ...) assemblage(self, ...)
@ -154,7 +154,7 @@ function Entity:has(name)
local ok, componentClass = Components.try(name) local ok, componentClass = Components.try(name)
if not ok then if not ok then
error("bad argument #1 to 'Entity:has' ("..componentClass..")", 2) Utils.error(2, "bad argument #1 to 'Entity:has' (%s)", componentClass)
end end
return self[name] and true or false return self[name] and true or false
@ -167,7 +167,7 @@ function Entity:get(name)
local ok, componentClass = Components.try(name) local ok, componentClass = Components.try(name)
if not ok then if not ok then
error("bad argument #1 to 'Entity:get' ("..componentClass..")", 2) Utils.error(2, "bad argument #1 to 'Entity:get' (%s)", componentClass)
end end
return self[name] return self[name]
@ -219,7 +219,7 @@ function Entity:deserialize(data)
local componentData = data[i] local componentData = data[i]
if (not Components.has(componentData.__name)) then if (not Components.has(componentData.__name)) then
error("bad argument #1 to 'Entity:deserialize' (ComponentClass '"..tostring(componentData.__name).."' wasn't yet loaded)") -- luacheck: ignore Utils.error(2, "bad argument #1 to 'Entity:deserialize' (ComponentClass '%s' wasn't yet loaded)", tostring(componentData.__name)) -- luacheck: ignore
end end
local componentClass = Components[componentData.__name] local componentClass = Components[componentData.__name]

View file

@ -6,6 +6,7 @@ local PATH = (...):gsub('%.[^%.]+$', '')
local List = require(PATH..".list") local List = require(PATH..".list")
local Type = require(PATH..".type") local Type = require(PATH..".type")
local Utils = require(PATH..".utils")
local Components = require(PATH..".components") local Components = require(PATH..".components")
@ -20,18 +21,18 @@ Filter.__mt = {
-- @tparam onComponent Optional function, called when a component is valid. -- @tparam onComponent Optional function, called when a component is valid.
function Filter.validate (name, def, onComponent) function Filter.validate (name, def, onComponent)
if type(def) ~= 'table' then if type(def) ~= 'table' then
error("invalid component list for filter '"..name.."' (table expected, got "..type(def)..")", 3) Utils.error(3, "invalid component list for filter '%s' (table expected, got %s)", name, type(def))
end end
if not onComponent and def.constructor and not Type.isCallable(def.constructor) then if not onComponent and def.constructor and not Type.isCallable(def.constructor) then
error("invalid pool constructor (callable expected, got "..type(def.constructor)..")", 3) Utils.error(3, "invalid pool constructor for filter '%s' (callable expected, got %s)", name, type(def.constructor))
end end
for n, component in ipairs(def) do for n, component in ipairs(def) do
local ok, err, reject = Components.try(component, true) local ok, err, reject = Components.try(component, true)
if not ok then if not ok then
error("invalid component for filter '"..name.."' at position #"..n.." ("..err..")", 3) Utils.error(3, "invalid component for filter '%s' at position #%d (%s)", name, n, err)
end end
if onComponent then if onComponent then
@ -61,14 +62,38 @@ function Filter.parse (name, def)
return required, rejected return required, rejected
end end
local REQUIRED_METHODS = {"add", "remove", "has", "clear"}
local VALID_POOL_TYPES = {table=true, userdata=true, lightuserdata=true, cdata=true}
function Filter.isValidPool (name, pool)
local poolType = type(pool)
--Check that pool is not nil
if not VALID_POOL_TYPES[poolType] then
Utils.error(3, "invalid value returned by pool '%s' constructor (table expected, got %s).", name, type(pool))
end
--Check if methods are callables
for _, method in ipairs(REQUIRED_METHODS) do
if not Type.isCallable(pool[method]) then
Utils.error(3, "invalid :%s method on pool '%s' (callable expected, got %s).", method, name, type(pool[method]))
end
end
end
--- Creates a new Filter --- Creates a new Filter
-- @string name Name for the Filter. -- @string name Name for the Filter.
-- @tparam table definition Table containing the Filter Definition -- @tparam table definition Table containing the Filter Definition
-- @treturn Filter The new Filter -- @treturn Filter The new Filter
-- @treturn Pool The associated Pool -- @treturn Pool The associated Pool
function Filter.new (name, def) function Filter.new (name, def)
local constructor = def.constructor or List local pool
local pool = constructor(def)
if def.constructor then
pool = def.constructor(def)
Filter.isValidPool(name, pool)
else
pool = List()
end
local required, rejected = Filter.parse(name, def) local required, rejected = Filter.parse(name, def)

View file

@ -50,7 +50,7 @@ System.mt = {
function System.new(definition) function System.new(definition)
for name, def in pairs(definition) do for name, def in pairs(definition) do
if type(name) ~= 'string' then if type(name) ~= 'string' then
error("invalid name for filter (string key expected, got "..type(name)..")", 2) Utils.error(2, "invalid name for filter (string key expected, got %s)", type(name))
end end
Filter.validate(name, def) Filter.validate(name, def)

View file

@ -3,6 +3,10 @@
local Utils = {} local Utils = {}
function Utils.error(level, str, ...)
error(string.format(str, ...), level + 1)
end
--- Does a shallow copy of a table and appends it to a target table. --- Does a shallow copy of a table and appends it to a target table.
-- @param orig Table to copy -- @param orig Table to copy
-- @param target Table to append to -- @param target Table to append to
@ -22,13 +26,13 @@ end
-- @treturn table The namespace table -- @treturn table The namespace table
function Utils.loadNamespace(pathOrFiles, namespace) function Utils.loadNamespace(pathOrFiles, namespace)
if type(pathOrFiles) ~= "string" and type(pathOrFiles) ~= "table" then if type(pathOrFiles) ~= "string" and type(pathOrFiles) ~= "table" then
error("bad argument #1 to 'loadNamespace' (string/table of strings expected, got "..type(pathOrFiles)..")", 2) Utils.error(2, "bad argument #1 to 'loadNamespace' (string/table of strings expected, got %s)", type(pathOrFiles))
end end
if type(pathOrFiles) == "string" then if type(pathOrFiles) == "string" then
local info = love.filesystem.getInfo(pathOrFiles) -- luacheck: ignore local info = love.filesystem.getInfo(pathOrFiles) -- luacheck: ignore
if info == nil or info.type ~= "directory" then if info == nil or info.type ~= "directory" then
error("bad argument #1 to 'loadNamespace' (path '"..pathOrFiles.."' not found)", 2) Utils.error(2, "bad argument #1 to 'loadNamespace' (path '%s' not found)", pathOrFiles)
end end
local files = love.filesystem.getDirectoryItems(pathOrFiles) local files = love.filesystem.getDirectoryItems(pathOrFiles)
@ -50,7 +54,7 @@ function Utils.loadNamespace(pathOrFiles, namespace)
elseif type(pathOrFiles) == "table" then elseif type(pathOrFiles) == "table" then
for _, path in ipairs(pathOrFiles) do for _, path in ipairs(pathOrFiles) do
if type(path) ~= "string" then if type(path) ~= "string" then
error("bad argument #2 to 'loadNamespace' (string/table of strings expected, got table containing "..type(path)..")", 2) -- luacheck: ignore Utils.error(2, "bad argument #2 to 'loadNamespace' (string/table of strings expected, got table containing %s)", type(path)) -- luacheck: ignore
end end
local name = path local name = path

View file

@ -52,7 +52,7 @@ end
-- @treturn World self -- @treturn World self
function World:addEntity(e) function World:addEntity(e)
if not Type.isEntity(e) then if not Type.isEntity(e) then
error("bad argument #1 to 'World:addEntity' (Entity expected, got "..type(e)..")", 2) Utils.error(2, "bad argument #1 to 'World:addEntity' (Entity expected, got %s)", type(e))
end end
if e.__world then if e.__world then
@ -70,7 +70,7 @@ end
-- @treturn World self -- @treturn World self
function World:removeEntity(e) function World:removeEntity(e)
if not Type.isEntity(e) then if not Type.isEntity(e) then
error("bad argument #1 to 'World:removeEntity' (Entity expected, got "..type(e)..")", 2) Utils.error(2, "bad argument #1 to 'World:removeEntity' (Entity expected, got %s)", type(e))
end end
self.__removed:add(e) self.__removed:add(e)
@ -207,7 +207,7 @@ function World:addSystem(systemClass)
local ok, err = tryAddSystem(self, systemClass) local ok, err = tryAddSystem(self, systemClass)
if not ok then if not ok then
error("bad argument #1 to 'World:addSystem' ("..err..")", 2) Utils.error(2, "bad argument #1 to 'World:addSystem' (%s)", err)
end end
return self return self
@ -225,7 +225,7 @@ function World:addSystems(...)
local ok, err = tryAddSystem(self, systemClass) local ok, err = tryAddSystem(self, systemClass)
if not ok then if not ok then
error("bad argument #"..i.." to 'World:addSystems' ("..err..")", 2) Utils.error(2, "bad argument #%d to 'World:addSystems' (%s)", i, err)
end end
end end
@ -237,7 +237,7 @@ end
-- @treturn boolean -- @treturn boolean
function World:hasSystem(systemClass) function World:hasSystem(systemClass)
if not Type.isSystemClass(systemClass) then if not Type.isSystemClass(systemClass) then
error("bad argument #1 to 'World:getSystem' (systemClass expected, got "..type(systemClass)..")", 2) Utils.error(2, "bad argument #1 to 'World:hasSystem' (SystemClass expected, got %s)", type(systemClass))
end end
return self.__systemLookup[systemClass] and true or false return self.__systemLookup[systemClass] and true or false
@ -248,7 +248,7 @@ end
-- @treturn System System to get -- @treturn System System to get
function World:getSystem(systemClass) function World:getSystem(systemClass)
if not Type.isSystemClass(systemClass) then if not Type.isSystemClass(systemClass) then
error("bad argument #1 to 'World:getSystem' (systemClass expected, got "..type(systemClass)..")", 2) Utils.error(2, "bad argument #1 to 'World:getSystem' (SystemClass expected, got %s)", type(systemClass))
end end
return self.__systemLookup[systemClass] return self.__systemLookup[systemClass]
@ -261,7 +261,7 @@ end
-- @treturn World self -- @treturn World self
function World:emit(functionName, ...) function World:emit(functionName, ...)
if not functionName or type(functionName) ~= "string" then if not functionName or type(functionName) ~= "string" then
error("bad argument #1 to 'World:emit' (String expected, got "..type(functionName)..")") Utils.error(2, "bad argument #1 to 'World:emit' (String expected, got %s)", type(functionName))
end end
local shouldFlush = self.__emitSDepth == 0 local shouldFlush = self.__emitSDepth == 0