local function deepEqual(a: any, b: any): (boolean, string?)
if typeof(a) ~= typeof(b) then
local message = ("{1} is of type %s, but {2} is of type %s"):format(typeof(a), typeof(b))
return false, message
end
if typeof(a) == "table" then
local visitedKeys = {}
for key, value in pairs(a) do
visitedKeys[key] = true
local success, innerMessage = deepEqual(value, b[key])
if not success and innerMessage then
local message = innerMessage
:gsub("{1}", ("{1}[%s]"):format(tostring(key)))
:gsub("{2}", ("{2}[%s]"):format(tostring(key)))
return false, message
end
end
for key, value in pairs(b) do
if not visitedKeys[key] then
local success, innerMessage = deepEqual(value, a[key])
if not success and innerMessage then
local message = innerMessage
:gsub("{1}", ("{1}[%s]"):format(tostring(key)))
:gsub("{2}", ("{2}[%s]"):format(tostring(key)))
return false, message
end
end
end
return true, nil
end
if a == b then
return true, nil
end
local message = "{1} ~= {2}"
return false, message
end
local function assertDeepEqual(a, b)
local success, innerMessageTemplate = deepEqual(a, b)
if not success and innerMessageTemplate then
local innerMessage = innerMessageTemplate:gsub("{1}", "first"):gsub("{2}", "second")
local message = ("Values were not deep-equal.\n%s"):format(innerMessage)
error(message, 2)
end
end
return assertDeepEqual