Module:TemplateFieldProcessor
Appearance
Documentation for this module may be created at Module:TemplateFieldProcessor/doc
--[[
* TemplateFieldProcessor.lua
* Provides field processing functionality for ICANNWiki templates
*
* This module handles the processing of template fields, including:
* - Standard field processors for common field types
* - Processor initialization and caching
* - Field value retrieval
* - Field processing with error handling
]]
local p = {}
-- ========== Constants as Upvalues ==========
-- Common empty string for fast returns
local EMPTY_STRING = ''
-- Wiki link pattern for regex matching
local WIKI_LINK_PATTERN = '%[%[.-%]%]'
-- ========== Required modules ==========
local ErrorHandling = require('Module:ErrorHandling')
local TemplateHelpers = require('Module:TemplateHelpers')
local LinkParser = require('Module:LinkParser')
local NormalizationText = require('Module:NormalizationText')
-- Module-level caches for expensive operations
local processorCache = {}
local moduleCache = {} -- Cache for lazy-loaded modules
-- Lazy module loader
local function lazyRequire(moduleName)
return function()
if not moduleCache[moduleName] then
moduleCache[moduleName] = require(moduleName)
end
return moduleCache[moduleName]
end
end
-- Lazily loaded modules
local getNormalizationDate = lazyRequire('Module:NormalizationDate')
-- ========== Cache Management ==========
-- Create a cache key for processors
-- @param template table The template object
-- @return string Cache key
function p.createProcessorCacheKey(template)
if not template.config.processors or not next(template.config.processors) then
return template.type .. '_default'
end
local processorCount = 0
for _ in pairs(template.config.processors) do processorCount = processorCount + 1 end
local keys = {}
local keyIndex = 1
for k in pairs(template.config.processors) do
keys[keyIndex] = k
keyIndex = keyIndex + 1
end
table.sort(keys)
local parts = {template.type}
for i = 1, processorCount do
parts[i+1] = keys[i]
end
return table.concat(parts, '_')
end
-- ========== Field Value Management ==========
-- Get field value from args (delegated to TemplateHelpers)
-- @param field table The field definition
-- @param args table The template arguments
-- @return string|nil The field value or nil if not found
function p.getFieldValue(field, args)
if not field then
return nil
end
local _, value = TemplateHelpers.getFieldValue(args, field)
return value
end
-- ========== Processor Management ==========
-- Initialize processors for a template
-- @param template table The template object
-- @return table The initialized processors
function p.initializeProcessors(template)
local cacheKey = p.createProcessorCacheKey(template)
if processorCache[cacheKey] then
local wrappedProcessors = {}
for processorId, processor in pairs(processorCache[cacheKey]) do
wrappedProcessors[processorId] = function(value, args)
return processor(value, args, template)
end
end
return wrappedProcessors
end
local customProcessors = template.config.processors or {}
local processors = {}
for processorId, processor in pairs(p.standardProcessors) do
processors[processorId] = processor
end
for processorId, processor in pairs(customProcessors) do
processors[processorId] = processor
end
processorCache[cacheKey] = processors
local wrappedProcessors = {}
for processorId, processor in pairs(processors) do
wrappedProcessors[processorId] = function(value, args)
return processor(value, args, template)
end
end
return wrappedProcessors
end
-- Get processor for a field
-- Internal helper function: determines which processor to use for a field; called by processField to select the appropriate processor based on field configuration
-- @param processors table The processors table
-- @param field table The field definition
-- @return function The processor function
function p.getFieldProcessor(processors, field)
-- Check if there's a processor matching the field key (most common case)
local fieldKey = field.key or (field.keys and field.keys[1])
if fieldKey and processors[fieldKey] then
return processors[fieldKey]
end
-- Check if field has explicit processor
if field.processor and processors[field.processor] then
return processors[field.processor]
end
-- Fall back to identity processor
return processors.identity
end
-- ========== Standard Field Processors ==========
-- Standard field processors
p.standardProcessors = {
-- Identity processor (default, returns value unchanged)
identity = function(value, args, template)
return value
end,
-- Website processor
website = function(value, args, template)
return TemplateHelpers.normalizeWebsites(value)
end,
-- Country processor
country = function(value, args, template)
return TemplateHelpers.normalizeCountries(value)
end,
-- Date processor (joins start and ending dates for Event templates)
date = function(value, args, template)
-- For Event templates, if an ending date exists, render a range
if template and template.type == 'Event' then
local endValue = args['ending'] or args['end']
if endValue and endValue ~= '' then
return TemplateHelpers.formatDateRange(value, endValue, {outputMode="complete"})
end
end
-- Default single-date formatting
return getNormalizationDate().formatDate(value)
end,
-- Language processor
language = function(value, args, template)
return TemplateHelpers.normalizeLanguages(value)
end,
-- Text processor (wikifies text)
text = function(value, args, template)
return TemplateHelpers.wikifyText(value)
end,
-- Email processor
email = function(value, args, template)
return TemplateHelpers.normalizeEmail(value)
end,
-- Multi-value processor
multivalue = function(value, args, template)
return TemplateHelpers.normalizeMultiValue(value)
end
}
-- ========== Field Processing ==========
-- Process a field using its processor
-- @param template table The template object
-- @param field table The field definition
-- @param args table The template arguments
-- @param errorContext table Optional error context for error handling
-- @return string The processed field value
function p.processField(template, field, args, errorContext)
if not field then
return EMPTY_STRING
end
local fieldKey = field.key or (field.keys and field.keys[1] or "unknown")
local value = p.getFieldValue(field, args)
if not value or value == '' then
return EMPTY_STRING
end
-- Wiki Link Handling Strategy: preserveWikiLinks prevents stripping of wiki links
local sanitizeOptions = {
preserveWikiLinks = field.autoWikiLink or field.preserveWikiLinks
}
-- Get processors - this assumes template._processors exists
local processors = template._processors
if not processors then
processors = p.initializeProcessors(template)
end
local processor = p.getFieldProcessor(processors, field)
if processor == processors.identity then
if sanitizeOptions.preserveWikiLinks then
return value
else
return NormalizationText.sanitizeUserInput(value, nil, nil, sanitizeOptions)
end
end
-- Use error context if provided
local protectFunc = function(func, fallback, ...)
if errorContext then
return ErrorHandling.protect(
errorContext,
'FieldProcessor_' .. fieldKey,
func,
fallback,
...
)
else
-- Simple pcall if no error context
local success, result = pcall(func, ...)
return success and result or fallback
end
end
local result = protectFunc(
function()
local processed = processor(value, args)
if sanitizeOptions.preserveWikiLinks and processed and processed ~= '' then
if value:match(WIKI_LINK_PATTERN) and not processed:match(WIKI_LINK_PATTERN) then
return value
end
end
return processed
end,
value,
value,
args
)
return result or EMPTY_STRING
end
return p