Module:UtilsSchema

local p = {} local h = {}

local utilsTable = require("Module:UtilsTable")

local SYMBOLS = { optional = "[%s]", required = "%s!", array = "{%s}", oneOf = "%s|%s", default = "%s=%s", }

p.TYPES = { oneOf = "oneOf", array = "array", record = "record", }

function p.getTypeDefinitions(schemaName, schema, formattingFn) schema = h.resolveReferences(schema, h.collectReferences(schema)) return h.getTypeDefs(schemaName, schema, formattingFn) end

function h.collectReferences(schema, references) references = references or {} h.walkSchema(function(schemaNode)		if schemaNode._id then			references[schemaNode._id] = schemaNode		end	end, schema) return references end

function h.resolveReferences(schema, references) h.walkSchema(function(schemaNode)		if schemaNode._ref then			local resolvedSchema = utilsTable.merge({}, references[schemaNode._ref], schemaNode)			utilsTable.merge(schemaNode, resolvedSchema)			schemaNode._ref = nil		end	end, schema) return schema end

function h.walkSchema(fn, schema) fn(schema) if schema.items then h.walkSchema(fn, schema.items) end if schema.properties then for i, v in ipairs(schema.properties) do			h.walkSchema(fn, v)		end end if schema.oneOf then for i, v in ipairs(schema.oneOf) do			h.walkSchema(fn, v)		end end end

function h.getTypeDefs(schemaName, schema, formattingFn, parentSchema) local rawType = schema.type local symbolicType local subkeys if rawType == p.TYPES.record then subkeys = {} for _, prop in ipairs(schema.properties) do			local propDef = h.getTypeDefs(prop.name, prop, formattingFn, schema) table.insert(subkeys, propDef) end elseif rawType == p.TYPES.array then local subtypeKeys, subtype = h.getTypeDefs(nil, schema.items, formattingFn, schema) subkeys = subtypeKeys symbolicType = string.format(SYMBOLS.array, subtype) elseif schema.oneOf then rawType = p.TYPES.oneOf subkeys = {} local subtypes = {} for i, subschema in ipairs(schema.oneOf) do			local keys, subtype = h.getTypeDefs(i == 0 and "" or "OR", subschema, formattingFn, schema) table.insert(subkeys, keys) if i == 1 then symbolicType = subtype else symbolicType = string.format(SYMBOLS.oneOf, symbolicType, subtype) end end end symbolicType = symbolicType or rawType local parentType if parentSchema then parentType = parentSchema.type or (parentSchema.oneOf and p.TYPES.oneOf) end local key = schemaName if schema.default then key = key and string.format(SYMBOLS.default, schemaName, schema.default) end if schema.required then symbolicType = string.format(SYMBOLS.required, symbolicType) elseif parentType ~= p.TYPES.oneOf and parentType ~= p.TYPES.array then -- otherwise leads to nonsense like [[string], instead of [{string}|string]		symbolicType = string.format(SYMBOLS.optional, symbolicType)		key = key and string.format(SYMBOLS.optional, key)	end	local formattedDef = formattingFn({		key = key,		rawType = rawType,		symbolicType = symbolicType,		desc = schema.desc,		parentType = parentType,		subkeys = subkeys,	})	return formattedDef, symbolicType end

return p