Module:UtilsSchema

local p = {} local h = {}

local utilsTable = require("Module:UtilsTable")

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

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

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

function h.collectReferences(schemas, references) references = references or {} for k, schema in pairs(schemas) do references["#/" .. k] = schema h.walkSchema(function(schemaNode)			if schemaNode._id then				references[schemaNode._id] = schemaNode			end		end, schema) end 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.keys then h.walkSchema(fn, schema.keys) end if schema.values then h.walkSchema(fn, schema.values) 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 rawType == p.TYPES.map then local _, keyType = h.getTypeDefs(nil, schema.keys, formattingFn, schema) local valueDef, valueType = h.getTypeDefs(nil, schema.values, formattingFn, schema) symbolicType = string.format(SYMBOLS.map, keyType, valueType) subkeys = valueDef 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 == 1 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 and parentType ~= p.TYPES.map 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	if schema._showDoc == false then		subkeys = nil	end	local formattedDef = formattingFn({		key = key,		rawType = rawType,		symbolicType = symbolicType,		desc = schema.desc,		parentType = parentType,		subkeys = subkeys,	})	return formattedDef, symbolicType end

return p