Module:UtilsValidate

local p = {}

local i18n = require("Module:I18n") local s = i18n.getString local utilsError = require("Module:UtilsError") local utilsTable = require("Module:UtilsTable")

local function code(s) return string.format(" ", s) end

local function err(str, name, path, options, vars) vars = utilsTable.merge(vars or {}, {		path = code(name .. utilsTable.printPath(path))	}) local msg = i18n.getString(str, vars) local options = options or {} local quiet = options.quiet local stackTrace = options.stackTrace local isUsageError = options.isUsageError if isUsageError then local invokeFrameTitle = mw.getCurrentFrame:getParent:getTitle if mw.title.new(invokeFrameTitle).nsText == "Template" then msg = string.format("Misuse of %s: %s", invokeFrameTitle, msg) end end if not quiet then utilsError.warn(msg, {			traceBack = stackTrace,			omitFrames = 2		}) end return msg end

function p.required(value, name, path, isKey, options) if value == nil then return err("msg.required", name, path, options) end end

function p.nonEmpty(value, name, path, isKey, options) local isEmpty = value == nil or type(value) == "string" and value == "" or type(value) == "table" and utilsTable.isEqual({}, value) if isEmpty then return err("msg.empty", name, path, options, {			actualValue = code(utilsTable.print(value))		}) end end

function p.deprecated(value, name, path, isKey, options) if value ~= nil then return err("msg.deprecated", name, path, options, {			value = code(value)		}) end end

function p.type(expectedType) return function(value, name, path, isKey, options) local actualType = type(value) if value ~= nil and actualType ~= expectedType then local msg = isKey and "msg.typeKey" or "msg.type" return err(msg, name, path, options, {				expectedType = code(expectedType),				actualType = code(actualType),				key = isKey and code(tostring(value)) or nil,			}) end end end

local function enumDetails(enum) if enum.reference then return s("msg.enumReference", {			referencePage = enum.reference		}) else return s("msg.enumAccepted", {			values = code(utilsTable.print(enum, true))		}) end end function p.enum(acceptedValues) return function (value, name, path, isKey, options) if type(acceptedValues) == "function" then acceptedValues = acceptedValues end -- Sometimes the argument is a list of values. It's easier to pretend that's always the case. local values = value local multivalue = type(value) == "table" if not multivalue then values = { value } end for k, value in ipairs(values) do			value = mw.text.trim(value) if not utilsTable.keyOf(acceptedValues, value) then local path = utilsTable.concat(path or {}, multivalue and k or nil) local msg = isKey and "msg.enumKey" or "msg.enum" return err(msg, name, path, options, {					actualValue = code(value),					enumDetails = enumDetails(acceptedValues),					key = code(tostring(value)),				}) end end end end

i18n.loadStrings({	en = {		msg = {			empty = "${path} must be non-empty but is ${actualValue}.",			required = "${path} is required but is .",			type = "${path} is type ${actualType} but type ${expectedType} was expected.",			typeKey = "${path} key ${key} is type ${actualType} but type ${expectedType} was expected.",			deprecated = "${path} is deprecated but has value ${value}.",			enum = "${path} has unexpected value ${actualValue}. ${enumDetails}",			enumKey = "${key} is not an acceptable key for ${path}. ${enumDetails}",			enumAccepted = "The accepted values are: ${values}",			enumReference = "For a list of accepted values, refer to ${referencePage}.",		},	}, })

return p