streamling errorhandler calls
This commit is contained in:
@@ -542,28 +542,28 @@ Animation.__index = Animation
|
||||
function Animation.new(props)
|
||||
if type(props) ~= "table" then
|
||||
if Animation._ErrorHandler then
|
||||
Animation._ErrorHandler:warn("Animation", "Animation.new() requires a table argument. Using default values.")
|
||||
Animation._ErrorHandler:warn("Animation", "ANIM_001")
|
||||
end
|
||||
props = { duration = 1, start = {}, final = {} }
|
||||
end
|
||||
|
||||
if type(props.duration) ~= "number" or props.duration <= 0 then
|
||||
if Animation._ErrorHandler then
|
||||
Animation._ErrorHandler:warn("Animation", "Animation duration must be a positive number. Using 1 second.")
|
||||
Animation._ErrorHandler:warn("Animation", "ANIM_002")
|
||||
end
|
||||
props.duration = 1
|
||||
end
|
||||
|
||||
if type(props.start) ~= "table" then
|
||||
if Animation._ErrorHandler then
|
||||
Animation._ErrorHandler:warn("Animation", "Animation start must be a table. Using empty table.")
|
||||
Animation._ErrorHandler:warn("Animation", "ANIM_001")
|
||||
end
|
||||
props.start = {}
|
||||
end
|
||||
|
||||
if type(props.final) ~= "table" then
|
||||
if Animation._ErrorHandler then
|
||||
Animation._ErrorHandler:warn("Animation", "Animation final must be a table. Using empty table.")
|
||||
Animation._ErrorHandler:warn("Animation", "ANIM_001")
|
||||
end
|
||||
props.final = {}
|
||||
end
|
||||
@@ -925,7 +925,7 @@ end
|
||||
function Animation:apply(element)
|
||||
if not element or type(element) ~= "table" then
|
||||
if Animation._ErrorHandler then
|
||||
Animation._ErrorHandler:warn("Animation", "Cannot apply animation to nil or non-table element.")
|
||||
Animation._ErrorHandler:warn("Animation", "ANIM_003")
|
||||
end
|
||||
return
|
||||
end
|
||||
@@ -1035,7 +1035,7 @@ function Animation:chain(nextAnimation)
|
||||
return nextAnimation
|
||||
else
|
||||
if Animation._ErrorHandler then
|
||||
Animation._ErrorHandler:warn("Animation", "chain() requires an Animation or function.")
|
||||
Animation._ErrorHandler:warn("Animation", "ANIM_004")
|
||||
end
|
||||
return self
|
||||
end
|
||||
@@ -1047,7 +1047,7 @@ end
|
||||
function Animation:delay(seconds)
|
||||
if type(seconds) ~= "number" or seconds < 0 then
|
||||
if Animation._ErrorHandler then
|
||||
Animation._ErrorHandler:warn("Animation", "delay() requires a non-negative number. Using 0.")
|
||||
Animation._ErrorHandler:warn("Animation", "ANIM_005")
|
||||
end
|
||||
seconds = 0
|
||||
end
|
||||
@@ -1062,7 +1062,7 @@ end
|
||||
function Animation:repeatCount(count)
|
||||
if type(count) ~= "number" or count < 0 then
|
||||
if Animation._ErrorHandler then
|
||||
Animation._ErrorHandler:warn("Animation", "repeatCount() requires a non-negative number. Using 0.")
|
||||
Animation._ErrorHandler:warn("Animation", "ANIM_006")
|
||||
end
|
||||
count = 0
|
||||
end
|
||||
@@ -1138,21 +1138,21 @@ end
|
||||
function Animation.keyframes(props)
|
||||
if type(props) ~= "table" then
|
||||
if Animation._ErrorHandler then
|
||||
Animation._ErrorHandler:warn("Animation", "Animation.keyframes() requires a table argument. Using defaults.")
|
||||
Animation._ErrorHandler:warn("Animation", "ANIM_007")
|
||||
end
|
||||
props = { duration = 1, keyframes = {} }
|
||||
end
|
||||
|
||||
if type(props.duration) ~= "number" or props.duration <= 0 then
|
||||
if Animation._ErrorHandler then
|
||||
Animation._ErrorHandler:warn("Animation", "Keyframe duration must be positive. Using 1 second.")
|
||||
Animation._ErrorHandler:warn("Animation", "ANIM_002")
|
||||
end
|
||||
props.duration = 1
|
||||
end
|
||||
|
||||
if type(props.keyframes) ~= "table" or #props.keyframes < 2 then
|
||||
if Animation._ErrorHandler then
|
||||
Animation._ErrorHandler:warn("Animation", "Keyframes require at least 2 keyframes. Using empty animation.")
|
||||
Animation._ErrorHandler:warn("Animation", "ANIM_008")
|
||||
end
|
||||
props.keyframes = {
|
||||
{ at = 0, values = {} },
|
||||
@@ -1224,14 +1224,14 @@ AnimationGroup.__index = AnimationGroup
|
||||
function AnimationGroup.new(props)
|
||||
if type(props) ~= "table" then
|
||||
if Animation._ErrorHandler then
|
||||
Animation._ErrorHandler:warn("AnimationGroup", "AnimationGroup.new() requires a table. Using defaults.")
|
||||
Animation._ErrorHandler:warn("AnimationGroup", "ANIM_009")
|
||||
end
|
||||
props = { animations = {} }
|
||||
end
|
||||
|
||||
if type(props.animations) ~= "table" or #props.animations == 0 then
|
||||
if Animation._ErrorHandler then
|
||||
Animation._ErrorHandler:warn("AnimationGroup", "AnimationGroup requires at least one animation.")
|
||||
Animation._ErrorHandler:warn("AnimationGroup", "ANIM_010")
|
||||
end
|
||||
props.animations = {}
|
||||
end
|
||||
@@ -1246,7 +1246,9 @@ function AnimationGroup.new(props)
|
||||
|
||||
if self.mode ~= "parallel" and self.mode ~= "sequence" and self.mode ~= "stagger" then
|
||||
if Animation._ErrorHandler then
|
||||
Animation._ErrorHandler:warn("AnimationGroup", string.format("Invalid mode: %s. Using 'parallel'.", tostring(self.mode)))
|
||||
Animation._ErrorHandler:warn("AnimationGroup", "ANIM_011", {
|
||||
mode = tostring(self.mode),
|
||||
})
|
||||
end
|
||||
self.mode = "parallel"
|
||||
end
|
||||
@@ -1512,7 +1514,7 @@ end
|
||||
function AnimationGroup:apply(element)
|
||||
if not element or type(element) ~= "table" then
|
||||
if Animation._ErrorHandler then
|
||||
Animation._ErrorHandler:warn("AnimationGroup", "Cannot apply group to nil or non-table element.")
|
||||
Animation._ErrorHandler:warn("AnimationGroup", "ANIM_003")
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
@@ -355,7 +355,9 @@ local function checkLargeBlurWarning(elementId, width, height, blurType)
|
||||
local suggestion =
|
||||
"Consider using retained mode for this component to avoid recreating blur effects every frame. Large blur operations are expensive and can cause performance issues in immediate mode."
|
||||
|
||||
Blur._ErrorHandler:warn("Blur", "PERF_003", message, suggestion)
|
||||
Blur._ErrorHandler:warn("Blur", "PERF_003", {
|
||||
area = string.format("%.0fx%.0f", width or 0, height or 0),
|
||||
})
|
||||
end
|
||||
|
||||
--- Create a new blur effect instance
|
||||
@@ -388,7 +390,7 @@ end
|
||||
function Blur:applyToRegion(intensity, x, y, width, height, drawFunc)
|
||||
if type(drawFunc) ~= "function" then
|
||||
if Blur._ErrorHandler then
|
||||
Blur._ErrorHandler:warn("Blur", "applyToRegion requires a draw function.")
|
||||
Blur._ErrorHandler:warn("Blur", "BLUR_001")
|
||||
end
|
||||
return
|
||||
end
|
||||
@@ -467,7 +469,7 @@ end
|
||||
function Blur:applyBackdrop(intensity, x, y, width, height, backdropCanvas)
|
||||
if not backdropCanvas then
|
||||
if Blur._ErrorHandler then
|
||||
Blur._ErrorHandler:warn("Blur", "applyBackdrop requires a backdrop canvas.")
|
||||
Blur._ErrorHandler:warn("Blur", "BLUR_002")
|
||||
end
|
||||
return
|
||||
end
|
||||
@@ -586,7 +588,7 @@ function Blur:applyBackdropCached(intensity, x, y, width, height, backdropCanvas
|
||||
-- Not cached, render and cache
|
||||
if not backdropCanvas then
|
||||
if Blur._ErrorHandler then
|
||||
Blur._ErrorHandler:warn("Blur", "applyBackdrop requires a backdrop canvas.")
|
||||
Blur._ErrorHandler:warn("Blur", "BLUR_002")
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
@@ -55,7 +55,7 @@ end
|
||||
function Color.fromHex(hexWithTag)
|
||||
-- Validate input type
|
||||
if type(hexWithTag) ~= "string" then
|
||||
Color._ErrorHandler:warn("Color", "VAL_004", "Invalid color format", {
|
||||
Color._ErrorHandler:warn("Color", "VAL_004", {
|
||||
input = tostring(hexWithTag),
|
||||
issue = "not a string",
|
||||
fallback = "white (#FFFFFF)",
|
||||
@@ -69,7 +69,7 @@ function Color.fromHex(hexWithTag)
|
||||
local g = tonumber("0x" .. hex:sub(3, 4))
|
||||
local b = tonumber("0x" .. hex:sub(5, 6))
|
||||
if not r or not g or not b then
|
||||
Color._ErrorHandler:warn("Color", "VAL_004", "Invalid color format", {
|
||||
Color._ErrorHandler:warn("Color", "VAL_004", {
|
||||
input = hexWithTag,
|
||||
issue = "invalid hex digits",
|
||||
fallback = "white (#FFFFFF)",
|
||||
@@ -83,7 +83,7 @@ function Color.fromHex(hexWithTag)
|
||||
local b = tonumber("0x" .. hex:sub(5, 6))
|
||||
local a = tonumber("0x" .. hex:sub(7, 8))
|
||||
if not r or not g or not b or not a then
|
||||
Color._ErrorHandler:warn("Color", "VAL_004", "Invalid color format", {
|
||||
Color._ErrorHandler:warn("Color", "VAL_004", {
|
||||
input = hexWithTag,
|
||||
issue = "invalid hex digits",
|
||||
fallback = "white (#FFFFFFFF)",
|
||||
@@ -92,7 +92,7 @@ function Color.fromHex(hexWithTag)
|
||||
end
|
||||
return Color.new(r / 255, g / 255, b / 255, a / 255)
|
||||
else
|
||||
Color._ErrorHandler:warn("Color", "VAL_004", "Invalid color format", {
|
||||
Color._ErrorHandler:warn("Color", "VAL_004", {
|
||||
input = hexWithTag,
|
||||
expected = "#RRGGBB or #RRGGBBAA",
|
||||
hexLength = #hex,
|
||||
|
||||
@@ -351,7 +351,7 @@ function Element.new(props)
|
||||
|
||||
-- Validate property combinations: passwordMode disables multiline
|
||||
if self.passwordMode and props.multiline then
|
||||
Element._ErrorHandler:warn("Element", "passwordMode is enabled, multiline will be disabled")
|
||||
Element._ErrorHandler:warn("Element", "ELEM_006")
|
||||
self.multiline = false
|
||||
elseif self.passwordMode then
|
||||
self.multiline = false
|
||||
@@ -743,15 +743,16 @@ function Element.new(props)
|
||||
-- Pixel units
|
||||
self.textSize = value
|
||||
else
|
||||
Element._ErrorHandler:error(
|
||||
"Element",
|
||||
string.format("Unknown textSize unit '%s'. Valid units: px, %%, vw, vh, ew, eh. Or use presets: xs, sm, md, lg, xl, xxl, 2xl, 3xl, 4xl", unit)
|
||||
)
|
||||
Element._ErrorHandler:error("Element", "ELEM_002", {
|
||||
unit = unit,
|
||||
})
|
||||
end
|
||||
else
|
||||
-- Validate pixel textSize value
|
||||
if props.textSize <= 0 then
|
||||
Element._ErrorHandler:error("Element", "textSize must be greater than 0, got: " .. tostring(props.textSize))
|
||||
Element._ErrorHandler:error("Element", "ELEM_001", {
|
||||
value = tostring(props.textSize),
|
||||
})
|
||||
end
|
||||
|
||||
-- Pixel textSize value
|
||||
@@ -3071,13 +3072,15 @@ function Element:setTransition(property, config)
|
||||
end
|
||||
|
||||
if type(config) ~= "table" then
|
||||
Element._ErrorHandler:warn("Element", "setTransition() requires a config table. Using default config.")
|
||||
Element._ErrorHandler:warn("Element", "ELEM_003")
|
||||
config = {}
|
||||
end
|
||||
|
||||
-- Validate config
|
||||
if config.duration and (type(config.duration) ~= "number" or config.duration < 0) then
|
||||
Element._ErrorHandler:warn("Element", "transition duration must be a non-negative number. Using 0.3 seconds.")
|
||||
Element._ErrorHandler:warn("Element", "ELEM_004", {
|
||||
value = tostring(config.duration),
|
||||
})
|
||||
config.duration = 0.3
|
||||
end
|
||||
|
||||
@@ -3095,7 +3098,7 @@ end
|
||||
---@param properties table Array of property names
|
||||
function Element:setTransitionGroup(groupName, config, properties)
|
||||
if type(properties) ~= "table" then
|
||||
Element._ErrorHandler:warn("Element", "setTransitionGroup() requires a properties array. No transitions set.")
|
||||
Element._ErrorHandler:warn("Element", "ELEM_005")
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
@@ -313,6 +313,170 @@ local ErrorCodes = {
|
||||
description = "CallSite counters accumulating",
|
||||
suggestion = "This indicates incrementFrame() may not be called properly. Check immediate mode frame management.",
|
||||
},
|
||||
|
||||
-- Animation Errors (ANIM_001 - ANIM_099)
|
||||
ANIM_001 = {
|
||||
code = "FLEXLOVE_ANIM_001",
|
||||
category = "VAL",
|
||||
description = "Invalid animation configuration",
|
||||
suggestion = "Animation.new() requires a table argument with duration, start, and final properties",
|
||||
},
|
||||
ANIM_002 = {
|
||||
code = "FLEXLOVE_ANIM_002",
|
||||
category = "VAL",
|
||||
description = "Invalid animation duration",
|
||||
suggestion = "Animation duration must be a positive number in seconds",
|
||||
},
|
||||
ANIM_003 = {
|
||||
code = "FLEXLOVE_ANIM_003",
|
||||
category = "VAL",
|
||||
description = "Invalid animation target",
|
||||
suggestion = "Animation can only be applied to table elements",
|
||||
},
|
||||
ANIM_004 = {
|
||||
code = "FLEXLOVE_ANIM_004",
|
||||
category = "VAL",
|
||||
description = "Invalid animation chain",
|
||||
suggestion = "chain() requires an Animation object or function",
|
||||
},
|
||||
ANIM_005 = {
|
||||
code = "FLEXLOVE_ANIM_005",
|
||||
category = "VAL",
|
||||
description = "Invalid animation delay",
|
||||
suggestion = "delay() requires a non-negative number in seconds",
|
||||
},
|
||||
ANIM_006 = {
|
||||
code = "FLEXLOVE_ANIM_006",
|
||||
category = "VAL",
|
||||
description = "Invalid repeat count",
|
||||
suggestion = "repeatCount() requires a non-negative number",
|
||||
},
|
||||
ANIM_007 = {
|
||||
code = "FLEXLOVE_ANIM_007",
|
||||
category = "VAL",
|
||||
description = "Invalid keyframes configuration",
|
||||
suggestion = "Animation.keyframes() requires a table with duration and keyframes array",
|
||||
},
|
||||
ANIM_008 = {
|
||||
code = "FLEXLOVE_ANIM_008",
|
||||
category = "VAL",
|
||||
description = "Insufficient keyframes",
|
||||
suggestion = "Keyframe animations require at least 2 keyframes",
|
||||
},
|
||||
ANIM_009 = {
|
||||
code = "FLEXLOVE_ANIM_009",
|
||||
category = "VAL",
|
||||
description = "Invalid animation group configuration",
|
||||
suggestion = "AnimationGroup.new() requires a table with animations array",
|
||||
},
|
||||
ANIM_010 = {
|
||||
code = "FLEXLOVE_ANIM_010",
|
||||
category = "VAL",
|
||||
description = "Empty animation group",
|
||||
suggestion = "AnimationGroup requires at least one animation",
|
||||
},
|
||||
ANIM_011 = {
|
||||
code = "FLEXLOVE_ANIM_011",
|
||||
category = "VAL",
|
||||
description = "Invalid animation group mode",
|
||||
suggestion = "AnimationGroup mode must be 'parallel' or 'sequence'",
|
||||
},
|
||||
|
||||
-- Blur Errors (BLUR_001 - BLUR_099)
|
||||
BLUR_001 = {
|
||||
code = "FLEXLOVE_BLUR_001",
|
||||
category = "VAL",
|
||||
description = "Missing draw function",
|
||||
suggestion = "applyToRegion requires a draw function to render the content to be blurred",
|
||||
},
|
||||
BLUR_002 = {
|
||||
code = "FLEXLOVE_BLUR_002",
|
||||
category = "VAL",
|
||||
description = "Missing backdrop canvas",
|
||||
suggestion = "applyBackdrop requires a backdrop canvas parameter",
|
||||
},
|
||||
|
||||
-- FlexLove Core Errors (CORE_001 - CORE_099)
|
||||
CORE_001 = {
|
||||
code = "FLEXLOVE_CORE_001",
|
||||
category = "VAL",
|
||||
description = "Invalid callback function",
|
||||
suggestion = "deferCallback expects a function argument",
|
||||
},
|
||||
CORE_002 = {
|
||||
code = "FLEXLOVE_CORE_002",
|
||||
category = "SYS",
|
||||
description = "Deferred callback execution failed",
|
||||
suggestion = "Check the callback function for errors. Error details included in message.",
|
||||
},
|
||||
CORE_003 = {
|
||||
code = "FLEXLOVE_CORE_003",
|
||||
category = "VAL",
|
||||
description = "Invalid garbage collection strategy",
|
||||
suggestion = "GC strategy must be one of: 'default', 'aggressive', 'conservative'",
|
||||
},
|
||||
|
||||
-- Element Errors (ELEM_001 - ELEM_099)
|
||||
ELEM_001 = {
|
||||
code = "FLEXLOVE_ELEM_001",
|
||||
category = "VAL",
|
||||
description = "Invalid text size",
|
||||
suggestion = "textSize must be greater than 0",
|
||||
},
|
||||
ELEM_002 = {
|
||||
code = "FLEXLOVE_ELEM_002",
|
||||
category = "VAL",
|
||||
description = "Invalid text size unit",
|
||||
suggestion = "textSize unit must be one of: px, %, vw, vh, ew, eh, or presets: xs, sm, md, lg, xl, xxl, 2xl, 3xl, 4xl",
|
||||
},
|
||||
ELEM_003 = {
|
||||
code = "FLEXLOVE_ELEM_003",
|
||||
category = "VAL",
|
||||
description = "Invalid transition configuration",
|
||||
suggestion = "setTransition() requires a table with transition properties",
|
||||
},
|
||||
ELEM_004 = {
|
||||
code = "FLEXLOVE_ELEM_004",
|
||||
category = "VAL",
|
||||
description = "Invalid transition duration",
|
||||
suggestion = "Transition duration must be a non-negative number in seconds",
|
||||
},
|
||||
ELEM_005 = {
|
||||
code = "FLEXLOVE_ELEM_005",
|
||||
category = "VAL",
|
||||
description = "Invalid transition group",
|
||||
suggestion = "setTransitionGroup() requires an array of property names",
|
||||
},
|
||||
ELEM_006 = {
|
||||
code = "FLEXLOVE_ELEM_006",
|
||||
category = "VAL",
|
||||
description = "Incompatible element configuration",
|
||||
suggestion = "passwordMode and multiline cannot be used together. Multiline will be disabled.",
|
||||
},
|
||||
|
||||
-- Module Loader Warnings (MOD_001 - MOD_099)
|
||||
MOD_001 = {
|
||||
code = "FLEXLOVE_MOD_001",
|
||||
category = "RES",
|
||||
description = "Optional module not found",
|
||||
suggestion = "Using stub implementation for optional module. This is expected if the module is not required.",
|
||||
},
|
||||
|
||||
-- Utility Errors (UTIL_001 - UTIL_099)
|
||||
UTIL_001 = {
|
||||
code = "FLEXLOVE_UTIL_001",
|
||||
category = "VAL",
|
||||
description = "Text truncation warning",
|
||||
suggestion = "Text was truncated to fit within the maximum allowed length",
|
||||
},
|
||||
|
||||
-- Image/Rendering Errors (IMG_001 - IMG_099)
|
||||
IMG_001 = {
|
||||
code = "FLEXLOVE_IMG_001",
|
||||
category = "REN",
|
||||
description = "Stencil buffer not available",
|
||||
suggestion = "Cannot apply corner radius to image without stencil buffer support. Check graphics capabilities.",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -670,64 +834,33 @@ function ErrorHandler:_formatStackTrace(level)
|
||||
return ""
|
||||
end
|
||||
|
||||
--- Format an error or warning message with optional error code
|
||||
--- Format an error or warning message using error code lookup
|
||||
---@param module string The module name (e.g., "Element", "Units", "Theme")
|
||||
---@param level string "Error" or "Warning"
|
||||
---@param codeOrMessage string Error code (e.g., "VAL_001") or message
|
||||
---@param messageOrDetails string|table|nil Message or details object
|
||||
---@param detailsOrSuggestion table|string|nil Details or suggestion
|
||||
---@param suggestionOrNil string|nil Suggestion
|
||||
---@param code string Error code (e.g., "VAL_001")
|
||||
---@param details table|nil Optional details object
|
||||
---@return string Formatted message
|
||||
function ErrorHandler:_formatMessage(module, level, codeOrMessage, messageOrDetails, detailsOrSuggestion, suggestionOrNil)
|
||||
local code = nil
|
||||
local message = codeOrMessage
|
||||
local details = nil
|
||||
local suggestion = nil
|
||||
function ErrorHandler:_formatMessage(module, level, code, details)
|
||||
local codeInfo = ErrorCodes.get(code)
|
||||
|
||||
-- Parse arguments (support multiple signatures)
|
||||
if type(codeOrMessage) == "string" and ErrorCodes.get(codeOrMessage) then
|
||||
-- Called with error code
|
||||
code = codeOrMessage
|
||||
message = messageOrDetails or ErrorCodes.describe(code)
|
||||
|
||||
if type(detailsOrSuggestion) == "table" then
|
||||
details = detailsOrSuggestion
|
||||
suggestion = suggestionOrNil or ErrorCodes.getSuggestion(code)
|
||||
elseif type(detailsOrSuggestion) == "string" then
|
||||
suggestion = detailsOrSuggestion
|
||||
else
|
||||
suggestion = ErrorCodes.getSuggestion(code)
|
||||
end
|
||||
else
|
||||
-- Called with message only (backward compatibility)
|
||||
message = codeOrMessage
|
||||
if type(messageOrDetails) == "table" then
|
||||
details = messageOrDetails
|
||||
suggestion = detailsOrSuggestion
|
||||
elseif type(messageOrDetails) == "string" then
|
||||
suggestion = messageOrDetails
|
||||
end
|
||||
if not codeInfo then
|
||||
return string.format("[FlexLove - %s] %s: Unknown error code: %s", module, level, code)
|
||||
end
|
||||
|
||||
-- Build formatted message
|
||||
local parts = {}
|
||||
|
||||
-- Header: [FlexLove - Module] Level [CODE]: Message
|
||||
if code then
|
||||
local codeInfo = ErrorCodes.get(code)
|
||||
table.insert(parts, string.format("[FlexLove - %s] %s [%s]: %s", module, level, codeInfo.code, message))
|
||||
else
|
||||
table.insert(parts, string.format("[FlexLove - %s] %s: %s", module, level, message))
|
||||
end
|
||||
-- Header: [FlexLove - Module] Level [CODE]: Description
|
||||
table.insert(parts, string.format("[FlexLove - %s] %s [%s]: %s", module, level, codeInfo.code, codeInfo.description))
|
||||
|
||||
-- Details section
|
||||
if details then
|
||||
if details and type(details) == "table" then
|
||||
table.insert(parts, self:_formatDetails(details))
|
||||
end
|
||||
|
||||
-- Suggestion section
|
||||
if suggestion and suggestion ~= "" then
|
||||
table.insert(parts, string.format("\n\nSuggestion: %s", suggestion))
|
||||
if codeInfo.suggestion and codeInfo.suggestion ~= "" then
|
||||
table.insert(parts, string.format("\n\nSuggestion: %s", codeInfo.suggestion))
|
||||
end
|
||||
|
||||
return table.concat(parts, "")
|
||||
@@ -807,43 +940,17 @@ end
|
||||
|
||||
--- Throw a critical error (stops execution)
|
||||
---@param module string The module name
|
||||
---@param codeOrMessage string Error code or message
|
||||
---@param messageOrDetails string|table|nil Message or details
|
||||
---@param detailsOrSuggestion table|string|nil Details or suggestion
|
||||
---@param suggestion string|nil Suggestion
|
||||
function ErrorHandler:error(module, codeOrMessage, messageOrDetails, detailsOrSuggestion, suggestion)
|
||||
local formattedMessage = self:_formatMessage(module, "Error", codeOrMessage, messageOrDetails, detailsOrSuggestion, suggestion)
|
||||
---@param code string Error code (e.g., "VAL_001")
|
||||
---@param details table|nil Optional details object
|
||||
function ErrorHandler:error(module, code, details)
|
||||
local formattedMessage = self:_formatMessage(module, "Error", code, details)
|
||||
|
||||
-- Parse arguments for logging
|
||||
local code = nil
|
||||
local message = codeOrMessage
|
||||
local details = nil
|
||||
local logSuggestion = nil
|
||||
|
||||
if type(codeOrMessage) == "string" and ErrorCodes.get(codeOrMessage) then
|
||||
code = codeOrMessage
|
||||
message = messageOrDetails or ErrorCodes.describe(code)
|
||||
|
||||
if type(detailsOrSuggestion) == "table" then
|
||||
details = detailsOrSuggestion
|
||||
logSuggestion = suggestion or ErrorCodes.getSuggestion(code)
|
||||
elseif type(detailsOrSuggestion) == "string" then
|
||||
logSuggestion = detailsOrSuggestion
|
||||
else
|
||||
logSuggestion = ErrorCodes.getSuggestion(code)
|
||||
end
|
||||
else
|
||||
message = codeOrMessage
|
||||
if type(messageOrDetails) == "table" then
|
||||
details = messageOrDetails
|
||||
logSuggestion = detailsOrSuggestion
|
||||
elseif type(messageOrDetails) == "string" then
|
||||
logSuggestion = messageOrDetails
|
||||
end
|
||||
end
|
||||
local codeInfo = ErrorCodes.get(code)
|
||||
local message = codeInfo and codeInfo.description or code
|
||||
local suggestion = codeInfo and codeInfo.suggestion or nil
|
||||
|
||||
-- Log the error
|
||||
self:_writeLog("ERROR", LOG_LEVEL.ERROR, module, code, message, details, logSuggestion)
|
||||
self:_writeLog("ERROR", LOG_LEVEL.ERROR, module, code, message, details, suggestion)
|
||||
|
||||
if self.includeStackTrace then
|
||||
formattedMessage = formattedMessage .. self:_formatStackTrace(3)
|
||||
@@ -854,41 +961,15 @@ end
|
||||
|
||||
--- Print a warning (non-critical, continues execution)
|
||||
---@param module string The module name
|
||||
---@param codeOrMessage string Warning code or message
|
||||
---@param messageOrDetails string|table|nil Message or details
|
||||
---@param detailsOrSuggestion table|string|nil Details or suggestion
|
||||
---@param suggestion string|nil Suggestion
|
||||
function ErrorHandler:warn(module, codeOrMessage, messageOrDetails, detailsOrSuggestion, suggestion)
|
||||
-- Parse arguments for logging
|
||||
local code = nil
|
||||
local message = codeOrMessage
|
||||
local details = nil
|
||||
local logSuggestion = nil
|
||||
|
||||
if type(codeOrMessage) == "string" and ErrorCodes.get(codeOrMessage) then
|
||||
code = codeOrMessage
|
||||
message = messageOrDetails or ErrorCodes.describe(code)
|
||||
|
||||
if type(detailsOrSuggestion) == "table" then
|
||||
details = detailsOrSuggestion
|
||||
logSuggestion = suggestion or ErrorCodes.getSuggestion(code)
|
||||
elseif type(detailsOrSuggestion) == "string" then
|
||||
logSuggestion = detailsOrSuggestion
|
||||
else
|
||||
logSuggestion = ErrorCodes.getSuggestion(code)
|
||||
end
|
||||
else
|
||||
message = codeOrMessage
|
||||
if type(messageOrDetails) == "table" then
|
||||
details = messageOrDetails
|
||||
logSuggestion = detailsOrSuggestion
|
||||
elseif type(messageOrDetails) == "string" then
|
||||
logSuggestion = messageOrDetails
|
||||
end
|
||||
end
|
||||
---@param code string Warning code (e.g., "VAL_001")
|
||||
---@param details table|nil Optional details object
|
||||
function ErrorHandler:warn(module, code, details)
|
||||
local codeInfo = ErrorCodes.get(code)
|
||||
local message = codeInfo and codeInfo.description or code
|
||||
local suggestion = codeInfo and codeInfo.suggestion or nil
|
||||
|
||||
-- Log the warning
|
||||
self:_writeLog("WARNING", LOG_LEVEL.WARNING, module, code, message, details, logSuggestion)
|
||||
self:_writeLog("WARNING", LOG_LEVEL.WARNING, module, code, message, details, suggestion)
|
||||
end
|
||||
|
||||
--- Validate that a value is not nil
|
||||
|
||||
@@ -633,9 +633,9 @@ function EventHandler:_invokeCallback(element, event)
|
||||
self.onEvent(element, event)
|
||||
end)
|
||||
else
|
||||
EventHandler._ErrorHandler:error("EventHandler", "SYS_003", "FlexLove.deferCallback not available", {
|
||||
EventHandler._ErrorHandler:error("EventHandler", "SYS_003", {
|
||||
eventType = event.type,
|
||||
}, "Ensure FlexLove module is properly loaded")
|
||||
})
|
||||
end
|
||||
else
|
||||
self.onEvent(element, event)
|
||||
|
||||
@@ -30,7 +30,7 @@ function ImageRenderer.calculateFit(imageWidth, imageHeight, boundsWidth, bounds
|
||||
objectPosition = objectPosition or "center center"
|
||||
|
||||
if imageWidth <= 0 or imageHeight <= 0 or boundsWidth <= 0 or boundsHeight <= 0 then
|
||||
ErrorHandler:error("ImageRenderer", "VAL_002", "Dimensions must be positive", {
|
||||
ErrorHandler:error("ImageRenderer", "VAL_002", {
|
||||
imageWidth = imageWidth,
|
||||
imageHeight = imageHeight,
|
||||
boundsWidth = boundsWidth,
|
||||
@@ -116,7 +116,7 @@ function ImageRenderer.calculateFit(imageWidth, imageHeight, boundsWidth, bounds
|
||||
return ImageRenderer.calculateFit(imageWidth, imageHeight, boundsWidth, boundsHeight, "contain", objectPosition)
|
||||
end
|
||||
else
|
||||
ErrorHandler:warn("ImageRenderer", "VAL_007", string.format("Invalid fit mode: '%s'. Must be one of: fill, contain, cover, scale-down, none", tostring(fitMode)), {
|
||||
ErrorHandler:warn("ImageRenderer", "VAL_007", {
|
||||
fitMode = fitMode,
|
||||
fallback = "fill"
|
||||
})
|
||||
@@ -362,7 +362,7 @@ function ImageRenderer.drawTiled(image, x, y, width, height, repeatMode, opacity
|
||||
end
|
||||
end
|
||||
else
|
||||
ErrorHandler:warn("ImageRenderer", "VAL_007", string.format("Invalid repeat mode: '%s'. Using 'no-repeat'", tostring(repeatMode)), {
|
||||
ErrorHandler:warn("ImageRenderer", "VAL_007", {
|
||||
repeatMode = repeatMode,
|
||||
fallback = "no-repeat"
|
||||
})
|
||||
|
||||
@@ -27,11 +27,13 @@ end
|
||||
---@return love.ImageData -- Scaled image data
|
||||
function ImageScaler.scaleNearest(sourceImageData, srcX, srcY, srcW, srcH, destW, destH)
|
||||
if not sourceImageData then
|
||||
ErrorHandler:error("ImageScaler", "VAL_001", "Source ImageData cannot be nil")
|
||||
ErrorHandler:error("ImageScaler", "VAL_001", {
|
||||
parameter = "sourceImageData"
|
||||
})
|
||||
end
|
||||
|
||||
if srcW <= 0 or srcH <= 0 or destW <= 0 or destH <= 0 then
|
||||
ErrorHandler:warn("ImageScaler", "VAL_002", "Dimensions must be positive", {
|
||||
ErrorHandler:warn("ImageScaler", "VAL_002", {
|
||||
srcW = srcW,
|
||||
srcH = srcH,
|
||||
destW = destW,
|
||||
@@ -95,11 +97,13 @@ end
|
||||
---@return love.ImageData -- Scaled image data
|
||||
function ImageScaler.scaleBilinear(sourceImageData, srcX, srcY, srcW, srcH, destW, destH)
|
||||
if not sourceImageData then
|
||||
ErrorHandler:error("ImageScaler", "VAL_001", "Source ImageData cannot be nil")
|
||||
ErrorHandler:error("ImageScaler", "VAL_001", {
|
||||
parameter = "sourceImageData"
|
||||
})
|
||||
end
|
||||
|
||||
if srcW <= 0 or srcH <= 0 or destW <= 0 or destH <= 0 then
|
||||
ErrorHandler:warn("ImageScaler", "VAL_002", "Dimensions must be positive", {
|
||||
ErrorHandler:warn("ImageScaler", "VAL_002", {
|
||||
srcW = srcW,
|
||||
srcH = srcH,
|
||||
destW = destW,
|
||||
|
||||
@@ -240,18 +240,18 @@ function LayoutEngine:layoutChildren()
|
||||
-- Warn if child uses percentage sizing but parent has autosizing
|
||||
if child.units and child.units.width then
|
||||
if child.units.width.unit == "%" and self.element.autosizing and self.element.autosizing.width then
|
||||
LayoutEngine._ErrorHandler:warn("LayoutEngine", "LAY_004", "Invalid sizing combination", {
|
||||
LayoutEngine._ErrorHandler:warn("LayoutEngine", "LAY_004", {
|
||||
child = child.id or "unnamed",
|
||||
issue = "percentage width with parent auto-sizing",
|
||||
}, "Use fixed or viewport units instead of percentage when parent has auto-sizing enabled")
|
||||
})
|
||||
end
|
||||
end
|
||||
if child.units and child.units.height then
|
||||
if child.units.height.unit == "%" and self.element.autosizing and self.element.autosizing.height then
|
||||
LayoutEngine._ErrorHandler:warn("LayoutEngine", "LAY_004", "Invalid sizing combination", {
|
||||
LayoutEngine._ErrorHandler:warn("LayoutEngine", "LAY_004", {
|
||||
child = child.id or "unnamed",
|
||||
issue = "percentage height with parent auto-sizing",
|
||||
}, "Use fixed or viewport units instead of percentage when parent has auto-sizing enabled")
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -134,7 +134,10 @@ function ModuleLoader.safeRequire(modulePath, isOptional)
|
||||
if ModuleLoader._ErrorHandler then
|
||||
ModuleLoader._ErrorHandler:warn(
|
||||
"ModuleLoader",
|
||||
string.format("Optional module '%s' not found, using stub implementation", modulePath)
|
||||
"MOD_001",
|
||||
{
|
||||
modulePath = modulePath
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
|
||||
@@ -271,16 +271,13 @@ function Performance:_addWarning(name, value, level)
|
||||
|
||||
if now - lastWarningTime >= 60 then
|
||||
if self._ErrorHandler and self._ErrorHandler.warn then
|
||||
local message = string.format("%s = %.2fms", name, value)
|
||||
local code = level == "critical" and "PERF_002" or "PERF_001"
|
||||
local suggestion = level == "critical" and "This operation is causing frame drops. Consider optimizing or reducing frequency."
|
||||
or "This operation is taking longer than recommended. Monitor for patterns."
|
||||
|
||||
self._ErrorHandler:warn("Performance", code, message, {
|
||||
self._ErrorHandler:warn("Performance", code, {
|
||||
metric = name,
|
||||
value = string.format("%.2fms", value),
|
||||
threshold = level == "critical" and self.criticalThresholdMs or self.warningThresholdMs,
|
||||
}, suggestion)
|
||||
})
|
||||
else
|
||||
local prefix = level == "critical" and "[CRITICAL]" or "[WARNING]"
|
||||
print(string.format("%s Performance: %s = %.2fms", prefix, name, value))
|
||||
@@ -405,7 +402,7 @@ function Performance:logWarning(warningKey, module, message, details, suggestion
|
||||
end
|
||||
|
||||
if self._ErrorHandler and self._ErrorHandler.warn then
|
||||
self._ErrorHandler:warn(module, "PERF_001", message, details or {}, suggestion)
|
||||
self._ErrorHandler:warn(module, "PERF_001", details or {})
|
||||
else
|
||||
print(string.format("[FlexLove - %s] Performance Warning: %s", module, message))
|
||||
if suggestion then
|
||||
@@ -529,12 +526,12 @@ function Performance:_sampleMemory()
|
||||
if not self._shownWarnings[name] then
|
||||
local message = string.format("Table '%s' growing consistently", name)
|
||||
if self._ErrorHandler and self._ErrorHandler.warn then
|
||||
self._ErrorHandler:warn("Performance", "MEM_001", message, {
|
||||
self._ErrorHandler:warn("Performance", "MEM_001", {
|
||||
table = name,
|
||||
initialSize = sizes[1],
|
||||
currentSize = sizes[#sizes],
|
||||
growthPercent = math.floor(((sizes[#sizes] / sizes[1]) - 1) * 100),
|
||||
}, "Check for memory leaks. Review cache eviction policies and ensure objects are released.")
|
||||
})
|
||||
end
|
||||
|
||||
self._shownWarnings[name] = true
|
||||
|
||||
@@ -233,11 +233,11 @@ function Renderer:_drawImage(x, y, paddingLeft, paddingTop, contentWidth, conten
|
||||
self.cornerRadius.bottomRight
|
||||
)
|
||||
end
|
||||
Renderer._ErrorHandler:warn("Renderer", "IMG_001", "Cannot apply corner radius to image: stencil buffer not available", {
|
||||
Renderer._ErrorHandler:warn("Renderer", "IMG_001", {
|
||||
imagePath = self.imagePath or "unknown",
|
||||
cornerRadius = cornerRadiusStr,
|
||||
error = tostring(err),
|
||||
}, "Ensure the active canvas has stencil=true enabled, or remove cornerRadius from images")
|
||||
})
|
||||
-- Continue without corner radius
|
||||
hasCornerRadius = false
|
||||
else
|
||||
@@ -380,9 +380,9 @@ end
|
||||
---@param backdropCanvas table|nil Backdrop canvas for backdrop blur
|
||||
function Renderer:draw(element, backdropCanvas)
|
||||
if not element then
|
||||
Renderer._ErrorHandler:warn("Renderer", "SYS_002", "Element parameter required", {
|
||||
Renderer._ErrorHandler:warn("Renderer", "SYS_002", {
|
||||
method = "draw",
|
||||
}, "Pass element as first parameter to draw()")
|
||||
})
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
@@ -270,10 +270,10 @@ function StateManager.getState(id, defaultState)
|
||||
if not ErrorHandler then
|
||||
ErrorHandler = require("modules.ErrorHandler")
|
||||
end
|
||||
ErrorHandler.error("StateManager", "SYS_001", "Invalid state ID", {
|
||||
ErrorHandler.error("StateManager", "SYS_001", {
|
||||
parameter = "id",
|
||||
value = "nil",
|
||||
}, "Provide a valid non-nil ID string to getState()")
|
||||
})
|
||||
end
|
||||
|
||||
-- Create state if it doesn't exist
|
||||
@@ -306,10 +306,10 @@ function StateManager.setState(id, state)
|
||||
if not ErrorHandler then
|
||||
ErrorHandler = require("modules.ErrorHandler")
|
||||
end
|
||||
ErrorHandler.error("StateManager", "SYS_001", "Invalid state ID", {
|
||||
ErrorHandler.error("StateManager", "SYS_001", {
|
||||
parameter = "id",
|
||||
value = "nil",
|
||||
}, "Provide a valid non-nil ID string to setState()")
|
||||
})
|
||||
end
|
||||
|
||||
-- Create sparse state (remove default values)
|
||||
@@ -529,12 +529,11 @@ function StateManager.getStats()
|
||||
-- Warn if callSiteCounters is unexpectedly large
|
||||
if callSiteCount > 1000 then
|
||||
if ErrorHandler then
|
||||
local message = string.format("callSiteCounters has %d entries (expected near 0 per frame)", callSiteCount)
|
||||
ErrorHandler.warn("StateManager", "STATE_001", message, {
|
||||
ErrorHandler.warn("StateManager", "STATE_001", {
|
||||
count = callSiteCount,
|
||||
expected = "near 0",
|
||||
frameNumber = frameNumber,
|
||||
}, "This indicates incrementFrame() may not be called properly or counters aren't being reset. Check immediate mode frame management.")
|
||||
})
|
||||
else
|
||||
print(string.format("[StateManager] WARNING: callSiteCounters has %d entries", callSiteCount))
|
||||
end
|
||||
|
||||
@@ -370,7 +370,7 @@ local activeTheme = nil
|
||||
function Theme.new(definition)
|
||||
-- Validate input type first
|
||||
if type(definition) ~= "table" then
|
||||
Theme._ErrorHandler:warn("Theme", "THM_001", "Invalid theme definition", {
|
||||
Theme._ErrorHandler:warn("Theme", "THM_001", {
|
||||
error = "Theme definition must be a table, got " .. type(definition),
|
||||
})
|
||||
return Theme.new({ name = "fallback", components = {}, colors = {}, fonts = {} })
|
||||
@@ -379,7 +379,7 @@ function Theme.new(definition)
|
||||
-- Validate theme definition
|
||||
local valid, err = validateThemeDefinition(definition)
|
||||
if not valid then
|
||||
Theme._ErrorHandler:warn("Theme", "THM_001", "Invalid theme definition", {
|
||||
Theme._ErrorHandler:warn("Theme", "THM_001", {
|
||||
error = tostring(err),
|
||||
})
|
||||
return Theme.new({ name = "fallback", components = {}, colors = {}, fonts = {} })
|
||||
@@ -397,7 +397,7 @@ function Theme.new(definition)
|
||||
self.atlas = image
|
||||
self.atlasData = imageData
|
||||
else
|
||||
Theme._ErrorHandler:warn("Theme", "RES_001", "Failed to load global atlas", {
|
||||
Theme._ErrorHandler:warn("Theme", "RES_001", {
|
||||
theme = definition.name,
|
||||
path = resolvedPath,
|
||||
error = loaderr,
|
||||
@@ -425,7 +425,7 @@ function Theme.new(definition)
|
||||
local contentHeight = srcHeight - 2
|
||||
|
||||
if contentWidth <= 0 or contentHeight <= 0 then
|
||||
Theme._ErrorHandler:warn("Theme", "RES_002", "Nine-patch image too small", {
|
||||
Theme._ErrorHandler:warn("Theme", "RES_002", {
|
||||
width = srcWidth,
|
||||
height = srcHeight,
|
||||
reason = "Image must be larger than 2x2 pixels to have content after stripping 1px border",
|
||||
@@ -460,7 +460,7 @@ function Theme.new(definition)
|
||||
comp.insets = parseResult.insets
|
||||
comp._ninePatchData = parseResult
|
||||
else
|
||||
Theme._ErrorHandler:warn("Theme", "RES_003", "Failed to parse nine-patch image", {
|
||||
Theme._ErrorHandler:warn("Theme", "RES_003", {
|
||||
context = errorContext,
|
||||
path = resolvedPath,
|
||||
error = tostring(parseErr),
|
||||
@@ -481,7 +481,7 @@ function Theme.new(definition)
|
||||
comp._loadedAtlasData = imageData
|
||||
end
|
||||
else
|
||||
Theme._ErrorHandler:warn("Theme", "RES_001", "Failed to load atlas", {
|
||||
Theme._ErrorHandler:warn("Theme", "RES_001", {
|
||||
context = errorContext,
|
||||
path = resolvedPath,
|
||||
error = tostring(loaderr),
|
||||
@@ -575,12 +575,12 @@ function Theme.load(path)
|
||||
if success then
|
||||
definition = result
|
||||
else
|
||||
Theme._ErrorHandler:warn("Theme", "RES_004", "Failed to load theme file", {
|
||||
Theme._ErrorHandler:warn("Theme", "RES_004", {
|
||||
theme = path,
|
||||
tried = themePath,
|
||||
error = tostring(result),
|
||||
fallback = "nil (no theme loaded)",
|
||||
}, "Check that the theme file exists in the themes/ directory or provide a valid module path")
|
||||
})
|
||||
return nil
|
||||
end
|
||||
end
|
||||
@@ -607,11 +607,11 @@ function Theme.setActive(themeOrName)
|
||||
end
|
||||
|
||||
if not activeTheme then
|
||||
Theme._ErrorHandler:warn("Theme", "THM_002", "Failed to set active theme", {
|
||||
Theme._ErrorHandler:warn("Theme", "THM_002", {
|
||||
theme = tostring(themeOrName),
|
||||
reason = "Theme not found or not loaded",
|
||||
fallback = "current theme unchanged",
|
||||
}, "Ensure the theme is loaded with Theme.load() before setting it active")
|
||||
})
|
||||
-- Keep current activeTheme unchanged (fallback behavior)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -23,48 +23,48 @@ function Units.parse(value)
|
||||
end
|
||||
|
||||
if type(value) ~= "string" then
|
||||
Units._ErrorHandler:warn("Units", "VAL_001", "Invalid property type", {
|
||||
Units._ErrorHandler:warn("Units", "VAL_001", {
|
||||
property = "unit value",
|
||||
expected = "string or number",
|
||||
got = type(value),
|
||||
}, "Using fallback: 0px")
|
||||
})
|
||||
return 0, "px"
|
||||
end
|
||||
|
||||
-- Check for unit-only input (e.g., "px", "%", "vw" without a number)
|
||||
local validUnits = { px = true, ["%"] = true, vw = true, vh = true, ew = true, eh = true }
|
||||
if validUnits[value] then
|
||||
Units._ErrorHandler:warn("Units", "VAL_005", "Invalid unit format", {
|
||||
Units._ErrorHandler:warn("Units", "VAL_005", {
|
||||
input = value,
|
||||
expected = "number + unit (e.g., '50" .. value .. "')",
|
||||
}, string.format("Add a numeric value before '%s', like '50%s'. Using fallback: 0px", value, value))
|
||||
})
|
||||
return 0, "px"
|
||||
end
|
||||
|
||||
-- Check for invalid format (space between number and unit)
|
||||
if value:match("%d%s+%a") then
|
||||
Units._ErrorHandler:warn("Units", "VAL_005", "Invalid unit format", {
|
||||
Units._ErrorHandler:warn("Units", "VAL_005", {
|
||||
input = value,
|
||||
issue = "contains space between number and unit",
|
||||
}, "Remove spaces: use '50px' not '50 px'. Using fallback: 0px")
|
||||
})
|
||||
return 0, "px"
|
||||
end
|
||||
|
||||
-- Match number followed by optional unit
|
||||
local numStr, unit = value:match("^([%-]?[%d%.]+)(.*)$")
|
||||
if not numStr then
|
||||
Units._ErrorHandler:warn("Units", "VAL_005", "Invalid unit format", {
|
||||
Units._ErrorHandler:warn("Units", "VAL_005", {
|
||||
input = value,
|
||||
}, "Expected format: number + unit (e.g., '50px', '10%', '2vw'). Using fallback: 0px")
|
||||
})
|
||||
return 0, "px"
|
||||
end
|
||||
|
||||
local num = tonumber(numStr)
|
||||
if not num then
|
||||
Units._ErrorHandler:warn("Units", "VAL_005", "Invalid unit format", {
|
||||
Units._ErrorHandler:warn("Units", "VAL_005", {
|
||||
input = value,
|
||||
issue = "numeric value cannot be parsed",
|
||||
}, "Using fallback: 0px")
|
||||
})
|
||||
return 0, "px"
|
||||
end
|
||||
|
||||
@@ -75,11 +75,11 @@ function Units.parse(value)
|
||||
|
||||
-- validUnits is already defined at the top of the function
|
||||
if not validUnits[unit] then
|
||||
Units._ErrorHandler:warn("Units", "VAL_005", "Invalid unit format", {
|
||||
Units._ErrorHandler:warn("Units", "VAL_005", {
|
||||
input = value,
|
||||
unit = unit,
|
||||
validUnits = "px, %, vw, vh, ew, eh",
|
||||
}, string.format("Treating '%s' as pixels", value))
|
||||
})
|
||||
return num, "px"
|
||||
end
|
||||
|
||||
@@ -99,10 +99,10 @@ function Units.resolve(value, unit, viewportWidth, viewportHeight, parentSize)
|
||||
return value
|
||||
elseif unit == "%" then
|
||||
if not parentSize then
|
||||
Units._ErrorHandler:warn("Units", "LAY_003", "Invalid dimensions", {
|
||||
Units._ErrorHandler:warn("Units", "LAY_003", {
|
||||
unit = "%",
|
||||
issue = "parent dimension not available",
|
||||
}, "Percentage units require a parent element with explicit dimensions. Using fallback: 0px")
|
||||
})
|
||||
return 0
|
||||
end
|
||||
return (value / 100) * parentSize
|
||||
@@ -111,10 +111,10 @@ function Units.resolve(value, unit, viewportWidth, viewportHeight, parentSize)
|
||||
elseif unit == "vh" then
|
||||
return (value / 100) * viewportHeight
|
||||
else
|
||||
Units._ErrorHandler:warn("Units", "VAL_005", "Invalid unit format", {
|
||||
Units._ErrorHandler:warn("Units", "VAL_005", {
|
||||
unit = unit,
|
||||
validUnits = "px, %, vw, vh, ew, eh",
|
||||
}, string.format("Unknown unit type: '%s'. Using fallback: 0px", unit))
|
||||
})
|
||||
return 0
|
||||
end
|
||||
end
|
||||
|
||||
@@ -342,7 +342,11 @@ local function validateEnum(value, enumTable, propName, moduleName)
|
||||
table.sort(validOptions)
|
||||
|
||||
if ErrorHandler then
|
||||
ErrorHandler:error(moduleName or "Element", string.format("%s must be one of: %s. Got: '%s'", propName, table.concat(validOptions, ", "), tostring(value)))
|
||||
ErrorHandler:error(moduleName or "Element", "VAL_007", {
|
||||
property = propName,
|
||||
expected = table.concat(validOptions, ", "),
|
||||
got = tostring(value),
|
||||
})
|
||||
else
|
||||
error(string.format("%s must be one of: %s. Got: '%s'", propName, table.concat(validOptions, ", "), tostring(value)))
|
||||
end
|
||||
@@ -361,17 +365,22 @@ local function validateRange(value, min, max, propName, moduleName)
|
||||
end
|
||||
if type(value) ~= "number" then
|
||||
if ErrorHandler then
|
||||
ErrorHandler:error(moduleName or "Element", string.format("%s must be a number, got %s", propName, type(value)))
|
||||
ErrorHandler:error(moduleName or "Element", "VAL_001", {
|
||||
property = propName,
|
||||
expected = "number",
|
||||
got = type(value),
|
||||
})
|
||||
else
|
||||
error(string.format("%s must be a number, got %s", propName, type(value)))
|
||||
end
|
||||
end
|
||||
if value < min or value > max then
|
||||
elseif value < min or value > max then
|
||||
if ErrorHandler then
|
||||
ErrorHandler:error(
|
||||
moduleName or "Element",
|
||||
string.format("%s must be between %s and %s, got %s", propName, tostring(min), tostring(max), tostring(value))
|
||||
)
|
||||
ErrorHandler:error(moduleName or "Element", "VAL_002", {
|
||||
property = propName,
|
||||
min = tostring(min),
|
||||
max = tostring(max),
|
||||
value = tostring(value),
|
||||
})
|
||||
else
|
||||
error(string.format("%s must be between %s and %s, got %s", propName, tostring(min), tostring(max), tostring(value)))
|
||||
end
|
||||
@@ -392,7 +401,11 @@ local function validateType(value, expectedType, propName, moduleName)
|
||||
local actualType = type(value)
|
||||
if actualType ~= expectedType then
|
||||
if ErrorHandler then
|
||||
ErrorHandler:error(moduleName or "Element", string.format("%s must be %s, got %s", propName, expectedType, actualType))
|
||||
ErrorHandler:error(moduleName or "Element", "VAL_001", {
|
||||
property = propName,
|
||||
expected = expectedType,
|
||||
got = actualType,
|
||||
})
|
||||
else
|
||||
error(string.format("%s must be %s, got %s", propName, expectedType, actualType))
|
||||
end
|
||||
@@ -555,6 +568,12 @@ local function sanitizeText(text, options)
|
||||
-- Limit string length (use UTF-8 character count, not byte count)
|
||||
local charCount = utf8.len(text)
|
||||
if charCount and charCount > maxLength then
|
||||
if ErrorHandler then
|
||||
ErrorHandler:warn("utils", "UTIL_001", {
|
||||
original = charCount,
|
||||
truncated = maxLength,
|
||||
})
|
||||
end
|
||||
-- Truncate to maxLength UTF-8 characters
|
||||
local bytePos = utf8.offset(text, maxLength + 1)
|
||||
if bytePos then
|
||||
|
||||
Reference in New Issue
Block a user