Zelda Wiki

Want to contribute to this wiki?
Sign up for an account, and get started!

Come join the Zelda Wiki community Discord server!

READ MORE

Zelda Wiki
Advertisement

This module takes care of parsing and validating template input, allowing module developers to focus on core logic. It aims to achieve the same goal as Wikipedia's Module:Arguments but with a different approach:

  • Validation is based on a schema that is also used to auto-generate documentation. This guarantees that the documentation is up to date with the validation code.
  • No implicit behaviour. If you want to trim a parameter, you specify trim = true. If you want to treat empty strings as nil, you specify nilIfEmpty = true. Module:Arguments does several things "automagically" by default and lacks clarity as a result.
  • No frame handling. It's up to the caller to consolidate frame.args and frame:getParent().args if needed. This can be done with utilsTable.merge. Most modules typically need only one frame anyway.

Lua error in package.lua at line 80: module 'Module:UtilsArg/Validate' not found.


local p = {}
local h = {}

local i18n = require("Module:I18n")
local s = i18n.getString
local utilsPackage = require("Module:UtilsPackage")
local utilsString = require("Module:UtilsString")
local utilsTable = require("Module:UtilsTable")
local utilsValidate = require("Module:UtilsValidate")

p = utilsPackage.submodules({
	"Module:UtilsArg/Validate"
}, {
	"Validation"
})

local VALIDATORS = {"required", "enum", "deprecated"}

function p.parse(frameArgs, templateSpec)
	local args = {}
	local err = {
		args = {},
		categories = {},
	}
	for k, v in pairs(templateSpec.params) do
		local arg = frameArgs[k]
		if v.trim then
			arg = utilsString.trim(arg)
		end
		if v.split then
			arg = utilsString.split(arg)
		end
		if v.nilIfEmpty then
			arg = utilsString.nilIfEmpty(arg)
		end
		args[v.name or k] = arg
	end
	for k, v in pairs(templateSpec.params) do
		local argErrors, errorCategories = h.validate(args[v.name or k], v, v.name or k, args)
		if #argErrors > 0 then
			err.args[v.name or k] = utilsTable.concat(err.args[v.name or k] or {}, argErrors)
		end
		err.categories = utilsTable.concat(err.categories, errorCategories)
	end
	if #err.categories == 0 then
		err = nil
	end
	return args, err
end

function h.validate(arg, param, paramName, args)
	local errors = {}
	local categories = {}
	for i, validator in ipairs(VALIDATORS) do
		local validatorData = param[validator]
		if validator == "enum" and param.enum then
			local enum = param.enum
			if type(enum) == "function" then
				local dependency = args[param.enumDependsOn]
				enum = dependency and enum(dependency)
			end
			validatorData = enum and utilsTable.merge({}, enum, {
				reference = param.enumReference
			})
		end
		local err, cat
		if validatorData ~= nil then 
			err, cat = h[validator](validatorData, arg, paramName)
		end
		if err then
			table.insert(errors, {
				msg = err,
				category = cat
			})
			table.insert(categories, cat)
		end
	end
	return errors, categories
end
function h.required(required, value, name)
	if not required then
		return
	end
	local err = utilsValidate.required(value, name)
	if err then
		return err, s("cat.invalidArgs")
	end
end
function h.enum(enum, value, name)
	if not enum then
		return
	end
	local err = utilsValidate.enum(enum)(value, name)
	if err then
		return err, s("cat.invalidArgs")
	end
end
function h.deprecated(deprecated, value, name)
	if not deprecated then
		return
	end
	local err = utilsValidate.deprecated(value, name)
	if err then
		return err, s("cat.deprecatedArgs")
	end
end

i18n.loadStrings({
	en = {
		cat = {
			invalidArgs = "Category:Pages with Invalid Arguments",
			deprecatedArgs = "Category:Pages with Deprecated Arguments",
		},
	},
})

p.Schemas = {
	parse = {
		frameArgs = {
			type = "any",
			required = true,
			desc = "Table of arguments obtained from {{Scribunto Manual|lib=Frame object|frame object}}.",
		},
		templateSpec = {
			type = "any",
			required = true,
			desc = "[[Module:Documentation#Templates|Template documentation object]].",
		}
	}
}

p.Documentation = {
	parse = {
		params = {"frameArgs", "templateSpec"},
		returns = {
			"A table of arguments parsed from the template input.",
			"A table of argument validation errors, or nil if there are none. The error messages are also logged using {{Scribunto Manual|lib=mw.addWarning}}.",
		},
		cases = {
			outputOnly = true,
			{
				desc = "Positional arguments are assigned to their names.",
				snippet = "PositionalAndNamedArgs",
				expect = {
					{
						game = "OoT",
						page = "Boss Key",
					},
					nil
				}
			},
			{
				desc = "Validation of required arguments.",
				snippet = "RequiredArgs",
				expect = {
					{
						page = "Boss Key"
					},
					{
						categories = {"Category:Pages with Invalid Arguments"},
						args = {
							game = {
								{
									category = "Category:Pages with Invalid Arguments",
									msg = "<code>game</code> is required but is <code>nil</code>.",
								},
							},
						},
					},
				}
			},
			{
				desc = "If <code>nilIfEmpty</code> and <code>required</code> are set, then the argument is invalid if it is an empty string.",
				snippet = "NilIfEmptyWithRequiredArgs",
				expect = {
					{},
					{
						categories = {"Category:Pages with Invalid Arguments"},
						args = {
							game = {
								{
									category = "Category:Pages with Invalid Arguments",
									msg = "<code>game</code> is required but is <code>nil</code>.",
								},
							},
						},
					},
				},
			},
			{
				desc = "Validation of deprecated arguments.",
				snippet = "Deprecated",
				expect = {
					{ oldArg = "foo" },
					{
						categories = {"Category:Pages with Deprecated Arguments"},
						args = {
							oldArg = {
								{
									category = "Category:Pages with Deprecated Arguments",
									msg = "<code>oldArg</code> is deprecated but has value <code>foo</code>.",
								},
							},
						},
					},
				}
			},
			{
				desc = "<code>enum</code> validation.",
				snippet = "Enum",
				expect = {
					{
						triforce2 = "Limpah",
						game = "ALttZ",
						triforce1 = "Kooloo",
					},
					{
						categories = {
							"Category:Pages with Invalid Arguments",
							"Category:Pages with Invalid Arguments",
							"Category:Pages with Invalid Arguments",
						},
						args = {
							triforce2 = {
								{
									category = "Category:Pages with Invalid Arguments",
									msg = "<code>triforce2</code> has unexpected value <code>Limpah</code>. For a list of accepted values, refer to [[Triforce]].",
								},
							},
							game = {
								{
									category = "Category:Pages with Invalid Arguments",
									msg = "<code>game</code> has unexpected value <code>ALttZ</code>. For a list of accepted values, refer to [[Data:Franchise]].",
								},
							},
							triforce1 = {
								{
									category = "Category:Pages with Invalid Arguments",
									msg = '<code>triforce1</code> has unexpected value <code>Kooloo</code>. The accepted values are: <code>{"Courage", "Power", "Wisdom"}</code>',
								},
							},
						},
					},
				},
			},
			{
				desc = "<code>split</code> is used to parse comma-separated strings as arrays. Each array item can be validated against an <code>enum</code>.",
				snippet = "SplitEnum",
				expect = {
					{
						games = {"OoT", "fakeGame", "BotW"},
					},
					{
						categories = {"Category:Pages with Invalid Arguments"},
						args = {
							games = {
								{
									category = "Category:Pages with Invalid Arguments",
									msg = "<code>games[2]</code> has unexpected value <code>fakeGame</code>. For a list of accepted values, refer to [[Data:Franchise]].",
								}
							}
						}
					}
				}
			},
			{
				desc = "<code>enum</code> can be written as a function, when the list of acceptable values depends on the value of another argument.",
				snippet = "EnumDependsOn",
				expect = {
					{
						term = "Dinolfos",
						game = "TP"
					},
					{
						categories = {"Category:Pages with Invalid Arguments"},
						args = {
							term = {
								{
									category = "Category:Pages with Invalid Arguments",
									msg = '<code>term</code> has unexpected value <code>Dinolfos</code>. The accepted values are: <code>{"Dynalfos"}</code>',
								},
							},
						},
					},
				},
			},
			{
				desc = "If <code>enumDependsOn</code> refers to a required parameter, then <code>enum</code> is not evaluated when that parameter is nil.",
				snippet = "EnumDependsOnNil",
				expect = {
					{ term = "Dinolfos" },
					{
						categories = {"Category:Pages with Invalid Arguments"},
						args = {
							game = {
								{
									category = "Category:Pages with Invalid Arguments",
						    		msg = "<code>game</code> is required but is <code>nil</code>.",
								}
							},
						},
					},
				},
			},
			{
				desc = "Altogether now",
				snippet = "TermStorePass",
				expect = {
					{
						term = "Dinolfos",
						games = {"OoT", "MM"},
					},
					nil
				}
			},
			{
				snippet = "TermStoreFail",
				expect = {
					{
						plural = "true",
						games = {"YY", "ZZ"},
					},
					{
						categories = {
							"Category:Pages with Invalid Arguments",
							"Category:Pages with Deprecated Arguments",
							"Category:Pages with Invalid Arguments",
						},
						args = {
							term = {
								{
									category = "Category:Pages with Invalid Arguments",
									msg = "<code>term</code> is required but is <code>nil</code>.",
								},
							},
							games = {
								{
									category = "Category:Pages with Invalid Arguments",
									msg = "<code>games[1]</code> has unexpected value <code>YY</code>. For a list of accepted values, refer to [[Data:Franchise]]."
								},
								-- TODO
								-- {
								-- 	category = "Category:Pages with Invalid Arguments",
								-- 	msg = "<code>games[2]</code> has unexpected value <code>ZZ</code>. For a list of accepted values, refer to [[Data:Franchise]]."
								-- },
							},
							plural = {
								{
									category = "Category:Pages with Deprecated Arguments",
									msg = "<code>plural</code> is deprecated but has value <code>true</code>.",
								},
							},
						},
					},
				},
			},
		},
	},
}

return p
Advertisement