Jump to content

Module:ErrorHandling

From ICANNWiki

Documentation for this module may be created at Module:ErrorHandling/doc

-- Module:ErrorHandling
-- A centralized error handling system for templates that provides error tracking,
-- reporting, and graceful failure recovery. It formats errors as data attributes
-- for the debug monitor to display in the browser console.

local ErrorHandling = {}

-- Creates a new error context for a template
-- @param templateName The name of the template (for error reporting)
-- @return A new error context table
function ErrorHandling.createContext(templateName)
    return {
        TEMPLATE_NAME = templateName or "UnknownTemplate",
        START_TIME = os.clock() * 1000,  -- Store in milliseconds
        ERROR_COUNT = 0,
        ERRORS = {},
        HAS_CRITICAL_ERROR = false
    }
end

-- Add an error to the context
-- @param context The error context
-- @param source The source of the error (function name)
-- @param message The error message
-- @param details Optional additional details about the error
-- @param isCritical Whether this is a critical error (default: false)
-- @return The error context (for chaining)
function ErrorHandling.addError(context, source, message, details, isCritical)
    -- Increment the error count
    context.ERROR_COUNT = context.ERROR_COUNT + 1
    
    -- Add the error to the list
    table.insert(context.ERRORS, {
        id = context.ERROR_COUNT,
        source = source or "unknown",
        message = message or "Unknown error",
        details = details or "",
        isCritical = isCritical or false
    })
    
    -- Update critical error flag if needed
    if isCritical then
        context.HAS_CRITICAL_ERROR = true
    end
    
    -- Return the error context for chaining
    return context
end

-- Format the error context for debugging output using data attributes
-- @param context The error context
-- @return HTML string with data attributes containing error information
function ErrorHandling.formatOutput(context)
    -- If no errors, return empty string
    if context.ERROR_COUNT == 0 then
        return ""
    end
    
    -- Build minimal data attribute div
    local divAttributes = {
        ['data-template-error'] = "1",
        ['data-error-count'] = tostring(context.ERROR_COUNT)
    }
    
    -- Add individual error attributes with minimal naming
    for _, err in ipairs(context.ERRORS) do
        divAttributes['data-error-' .. err.id .. '-source'] = err.source
        divAttributes['data-error-' .. err.id .. '-msg'] = err.message
        if err.details and err.details ~= "" then
            divAttributes['data-error-' .. err.id .. '-details'] = err.details
        end
    end
    
    -- Build attribute string efficiently
    local attrStr = ""
    for k, v in pairs(divAttributes) do
        attrStr = attrStr .. ' ' .. k .. '="' .. v .. '"'
    end
    
    -- Create hidden div with minimal footprint
    return string.format('<div style="display:none"%s></div>', attrStr)
end

-- Create emergency display for catastrophic failures with minimal markup
-- @param args The template arguments
-- @param errorSource The source of the error
-- @param errorMessage The error message
-- @param templateType The type of template (e.g. "Event", "Person")
-- @return HTML string with a minimal template display and error data
function ErrorHandling.createEmergencyDisplay(args, errorSource, errorMessage, templateType)
    -- Extract critical information for minimal display
    local title = args.name or "Unnamed " .. (templateType or "Item")
    
    -- Create minimal fallback with error data attributes
    return string.format(
        '{| class="template-table" cellpadding="2"\n' ..
        '|-\n| class="template-title template-title-%s" | <span>%s</span>\n' ..
        '|}\n' ..
        '<div style="display:none" data-template-error="1" data-critical="1" ' ..
        'data-error-1-source="%s" data-error-1-msg="%s"></div>',
        string.lower(templateType or "generic"),
        title,
        errorSource,
        errorMessage
    )
end

-- Protect a function with pcall and error handling
-- @param context The error context
-- @param funcName Name of the function (for error reporting)
-- @param func The function to protect
-- @param fallback Value to return if the function fails
-- @param ... Arguments to pass to the function
-- @return The result of the function, or fallback value on error
function ErrorHandling.protect(context, funcName, func, fallback, ...)
    local success, result = pcall(func, ...)
    
    if not success then
        ErrorHandling.addError(
            context,
            funcName,
            "Function execution failed",
            tostring(result),
            false
        )
        return fallback
    end
    
    return result
end

-- Protected module loading
-- @param context The error context
-- @param moduleName Name of the module to load
-- @param isCritical Whether failure to load is critical (default: false)
-- @return The loaded module, or an empty table on failure
function ErrorHandling.safeRequire(context, moduleName, isCritical)
    local success, module = pcall(require, moduleName)
    if not success then
        -- Record the error
        ErrorHandling.addError(
            context,
            "ModuleLoading",
            "Failed to load module",
            moduleName,
            isCritical or false
        )
        
        -- Return an empty table as fallback
        return {}
    end
    return module
end

return ErrorHandling