Module:UtilsSchema

local p = {} local h = {}

local utilsMarkup = require("Module:UtilsMarkup") local utilsTable = require("Module:UtilsTable")

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

function p.getTypeDefinitions(schemaName, schema) schema = h.resolveReferences(schema, h.collectReferences(schema)) local definitions = h.getTypeDefs(schemaName, schema, function(keyDef)		local key = keyDef.key		local symbolicType = keyDef.symbolicType		local rawType = keyDef.rawType		local desc = keyDef.desc		local subkeys = keyDef.subkeys		local parentType = keyDef.parentType		if key then			key = utilsMarkup.code(key)			symbolicType = utilsMarkup.code(symbolicType)			key = utilsMarkup.explain(symbolicType)(key)		end		-- Common case such as {record}|record. Only need to render the subkeys once.		if rawType == "oneOf" and #subkeys == 2 then			local subkeys1 = utilsTable.tail(subkeys[1][1])			local subkeys2 = utilsTable.tail(subkeys[2][1])			if utilsTable.isEqual(subkeys1)(subkeys2) then				subkeys = subkeys1			end		end		definition = utilsTable.mergeArrays({key, desc}, subkeys)		if parentType == "record" or parentType == "oneOf" then			definition = {definition}		end		return definition	end) local definitionsList = utilsMarkup.definitionList({definitions}) return definitionsList 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 == "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 == "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 = "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 -- if schema.desc then -- 	table.insert(definitions, 1, schema.desc) -- end -- if isSubtype then -- 	table.insert(definitions, 1, "") -- 	return definitions, type -- end local parentType if parentSchema then parentType = parentSchema.type or (parentSchema.oneOf and "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 ~= "oneOf" and parentType ~= "array" then 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