--- Is expression library -- @module Utils.Is -- @usage -- local Is = require('__stdlib__/stdlib/utils/is') -- Is.True(true) -- Is.Not.True(false) -- Is.Assert.True(true) -- Is.Assert.Not.True(false) --- Is Table -- @section Table --- Is the test true. -- @table Is -- @field Not Is the test not true. -- @field Assert Assert that the test is true. -- @field Assert.Not Assert that the test is not true. --- Is Table Callers -- @section Callers --- Is the test truthy -- @function Is -- @tparam mixed var -- @treturn boolean local Is = {} --- Is the test not truthy -- @function Not -- @tparam mixed var -- @treturn boolean Is.Not = {} --- Assert that the test is Truthy -- @function Assert -- @tparam mixed var -- @treturn boolean Is.Assert = {} --- Assert that the test is not Truthy -- @function Assert.Not -- @tparam mixed var -- @treturn boolean Is.Assert.Not = {} --- Functions -- @section Functions local M = {} local type = type local floor = math.floor local huge = math.huge --- Returns the var if the passed variable is a table. -- @tparam mixed var The variable to check -- @treturn mixed function M.Table(var) return type(var) == 'table' and var end M.table = M.Table --- Returns the var if the passed variable is a string. -- @tparam mixed var The variable to check -- @treturn mixed function M.String(var) return type(var) == 'string' and var end M.string = M.String --- Returns the var if the passed variable is a number. -- @tparam mixed var The variable to check -- @treturn mixed function M.Number(var) return type(var) == 'number' and var end M.number = M.Number function M.Thread(var) return type(var) == 'thread' and var end M.thread = M.Thread function M.Userdata(var) return type(var) == 'userdata' and var end M.userdata = M.Userdata --- Returns true if the passed variable is nil. -- @tparam mixed var The variable to check -- @treturn boolean function M.Nil(var) return type(var) == 'nil' end M.is_nil = M.Nil --- Returns true if the passed variable is a boolean. -- @tparam mixed var The variable to check -- @treturn boolean function M.Boolean(var) return type(var) == 'boolean' end M.boolean = M.boolean --- Returns true if the passed variable is true -- @tparam mixed var The variable to check -- @treturn boolean function M.True(var) return var == true end M.is_true = M.True --- Returns the var if the passed variable is not nil or false. -- @tparam mixed var The variable to check -- @treturn mixed function M.Truthy(var) return var or false end M.truthy = M.Truthy --- Returns true if the passed variable is false. -- @tparam mixed var The variable to check -- @treturn boolean function M.False(var) return var == false end M.is_false = M.False --- Returns true if the passed variable is false or nil. -- @tparam mixed var The variable to check -- @treturn boolean function M.Falsy(var) return not var end M.falsy = M.Falsy --- Returns true if the passed variable is nil, an empty table, or an empty string. -- @tparam mixed var The variable to check -- @treturn boolean function M.Empty(var) if M.Table(var) then return table_size and table_size(var) == 0 or next(var) == nil elseif M.String(var) then return #string == 0 end return M.Nil(var) end M.empty = M.Empty function M.None(var) return M.Empty(var) or M.False(var) or var == 0 or var ~= var end M.none = M.None --- Returns the passed var if it is positive. -- @tparam mixed var The variable to check -- @treturn mixed function M.Positive(var) return M.Number(var) and var >= 0 and var end M.positive = M.Positive --- Returns the passed var if it is odd. -- @tparam mixed var The variable to check -- @treturn mixed function M.odd(var) return M.number(var) and (var % 2 ~= 0) and var end --- Returns the passed var if it is even. -- @tparam mixed var The variable to check -- @treturn mixed function M.even(var) return M.number(var) and (var % 2 == 0) and var end --- Returns the passed var if it is negative. -- @tparam mixed var The variable to check -- @treturn mixed function M.Negative(var) return M.Number(var) and var < 0 and var end M.negative = M.Negative --- Returns the passed var if it is not a number. -- @tparam mixed var The variable to check -- @treturn mixed function M.NaN(var) return var ~= var end M.nan = M.NaN --- Returns the passed var if it is finite. -- @tparam mixed var The variable to check -- @treturn mixed function M.Finite(var) return M.Number(var) and (var < huge and var > -huge) and var end M.finite = M.Finite --- Returns the passed var if it is an int. -- @tparam mixed var The variable to check -- @treturn mixed function M.Int(var) return M.Finite(var) and rawequal(floor(var), var) and var end M.int = M.Int --- Returns the passed var if it is an int8. -- @tparam mixed var The variable to check -- @treturn mixed function M.Int8(var) return M.Int(var) and var >= -128 and var <= 127 and var end M.int8 = M.Int8 --- Returns the passed var if it is an int16. -- @tparam mixed var The variable to check -- @treturn mixed function M.Int16(var) return M.Int(var) and var >= -32768 and var <= 32767 and var end M.int16 = M.Int16 --- Returns the passed var if it is an int32. -- @tparam mixed var The variable to check -- @treturn mixed function M.Int32(var) return M.Int(var) and var >= -2147483648 and var <= 2147483647 and var end M.int32 = M.Int32 --- Returns the passed var if it is an unsigned int. -- @tparam mixed var The variable to check -- @treturn mixed function M.Unsigned(var) return Is.Number(var) and (var < huge and var >= 0) and var end M.unsigned = M.Unsigned --- Returns the passed var if it is an unsigned int. -- @tparam mixed var The variable to check -- @treturn mixed function M.UInt(var) return M.Unsigned(var) and rawequal(floor(var), var) and var end M.uint = M.UInt --- Returns the passed var if it is an unsigned int8. -- @tparam mixed var The variable to check -- @treturn mixed function M.UInt8(var) return M.UInt(var) and var <= 255 and var end M.uint8 = M.UInt8 --- Returns the passed var if it is an unsigned int16. -- @tparam mixed var The variable to check -- @treturn mixed function M.UInt16(var) return M.UInt(var) and var <= 65535 and var end M.uint16 = M.UInt16 --- Returns the passed var if it is an unsigned int32. -- @tparam mixed var The variable to check -- @treturn mixed function M.UInt32(var) return M.UInt(var) and var <= 4294967295 and var end M.uint32 = M.UInt32 --- Returns the passed var if it is a float. -- @tparam mixed var The variable to check -- @treturn mixed function M.Float(var) return M.number(var) and var >= 0 and var < 1 and var end M.float = M.Float --- Returns the passed var if it is a full position. -- @tparam mixed var The variable to check -- @treturn mixed function M.Position(var) return M.Table(var) and (var.x and var.y) and var end M.position = M.Position --- Returns the passed var if it is a full area. -- @tparam mixed var The variable to check -- @treturn mixed function M.Area(var) return M.Table(var) and (M.Position(var.left_top) and M.Position(var.right_bottom)) and var end M.area = M.Area --- Returns the passed var if it is a simple position/vector. -- @tparam mixed var The variable to check -- @treturn mixed function M.Vector(var) return M.Table(var) and ((M.Number(var[1]) and M.Number(var[2])) or M.Position(var)) and var end M.vector = M.Vector --- Returns the passed var if it is a simple area/boundingbox. -- @tparam mixed var The variable to check -- @treturn mixed function M.BoundingBox(var) return M.Table(var) and (M.Vector(var[1]) and M.Vector(var[2])) end M.boundingbox = M.BoundingBox M.bounding_box = M.BoundingBox M.Bounding_Box = M.BoundingBox --- Returns the hex value of the passed var if it is hexadecimal. -- @tparam mixed var The variable to check -- @treturn mixed function M.Hex(var) return M.String(var) and var:match('%x%x%x%x%x%x$') end M.hex = M.Hex --- Returns true if the passed variable is a single alphbetical word. -- Does not allow any special chars -- @tparam mixed var The variable to check -- @treturn boolean true if the passed variable is a single alphbetical word function M.StrictWord(var) return M.String(var) and var:find('^[%a]+$') == 1 end M.strict_word = M.StrictWord --- Returns true if the passed variable is a single alphbetical word. -- Allows _ and - as part of the word -- @tparam mixed var The variable to check -- @treturn boolean true if the passed variable is a single alphbetical word function M.AlphabetWord(var) return M.String(var) and var:find('^[%a%_%-]+$') == 1 end M.Word = M.AlphabetWord --- Returns true if the passed variable is a single alphbetical word. -- Must start with a letter, allows _ and - as part of the word -- @tparam mixed var The variable to check -- @treturn boolean true if the passed variable is a single alphbetical word function M.AlphanumWord(var) return M.String(var) and var:find('^%a+[%w%_%-]*$') == 1 end M.Alpha = M.AlphanumWord M.alpha = M.AlphanumWord M.alphanumword = M.AlphanumWord --- Is this a factorio object -- @tparam LuaObject var The variable to check -- @treturn mixed the var if this is an LuaObject function M.Object(var) return M.Table(var) and var.__self and var end M.object = M.Object --- Is this factorio object valid -- @tparam LuaObject var The variable to check -- @treturn mixed the var if this is a valid LuaObject function M.Valid(var) return M.Object(var) and var.valid and var end M.valid = M.Valid --- Returns true if the passed variable is a callable function. -- @tparam mixed var The variable to check -- @treturn boolean true if the passed variable is a callable function function M.Callable(var) return type(var) == 'function' or type((getmetatable(var) or {}).__call) == 'function' end M.callable = M.Callable M.Function = M.Callable M.is_function = M.Callable setmetatable( Is, { __index = function(_, k) return M[k] and function(_assert) return M[k](_assert) end or nil end, __call = function(_, ...) return (...) end } ) setmetatable( Is.Not, { __index = function(_, k) return M[k] and function(_assert) return not M[k](_assert) end or nil end, __call = function(_, ...) return not (...) end } ) Is.is_not = Is.Not -- convenience function for un-lambda-ing deferred error messages local function safe_invoke(f) local ok, msg = xpcall(f, debug.traceback) if not ok then -- ensure msg really is a string so there is theoretically no chance -- of a triple fault (i.e.: from a monkey-patched debug.traceback -- returning something that now fails to concatenate to a string) if type(msg) == 'string' then msg = '<<< DOUBLE FAULT: ' .. msg .. ' >>>' end end -- for sanity-preservation, always return something truthy return msg or 'Unknown Error' end setmetatable( Is.Assert, { __index = function(_, k) return M[k] and function(_assert, _message, _level) _level = tonumber(_level) or 3 ---@type integer return M[k](_assert) or error(type(_message) == 'function' and safe_invoke(_message) or _message or 'assertion failed', _level) end or nil end, __call = function(_, ...) local param = { ... } local _level = tonumber(param[3]) or 3 --[[@as integer]] return param[1] or error(type(param[2]) == 'function' and safe_invoke(param[2]) or param[2] or 'assertion failed', _level) end } ) Is.assert = Is.Assert setmetatable( Is.Assert.Not, { __index = function(_, k) return M[k] and function(_assert, _message, _level) _level = tonumber(_level) or 3 ---@type integer return not M[k](_assert) or error(type(_message) == 'function' and safe_invoke(_message) or _message or 'assertion failed', _level) end or nil end, __call = function(_, ...) local param = { ... } local _level = tonumber(param[3]) or 3 --[[@as integer]] return not param[1] or error(type(param[2]) == 'function' and safe_invoke(param[2]) or param[2] or 'assertion failed', _level) end } ) Is.assert.is_not = Is.Assert.Not return Is