Module:Database

From Cassette Beasts
Revision as of 18:20, 24 May 2022 by Tomc (talk | contribs)

Documentation for this module may be created at Module:Database/doc

local p = {}

local db = mw.loadData("Module:Database/data")

-- Private helpers

function table.empty(t)
	for _,_ in pairs(t) do
		return false
	end
	return true
end

function table.clone(t)
	local result = {}
	for k,v in pairs(t) do
		result[k] = v
	end
	return result
end

function table.values(t)
	local result = {}
	for _,v in pairs(t) do
		table.insert(result, v)
	end
	return result
end

function string.split(s, delim)
	local result = {}
	if s == "" then
		return result
	end
	local current = ""
	for i = 1, string.len(s) do
		if string.sub(s, i, i + string.len(delim) - 1) == delim then
			i = i + string.len(delim) - 1
			table.insert(result, current)
			current = ""
		else
			current = current .. string.sub(s, i, i)
		end
	end
	table.insert(result, current)
	return result
end

function species_sort_index(species)
	local i = species.bestiary_index
	if i == nil or i < 0 then
		return 99999
	else
		return i
	end
end

function cmp_species(a, b)
	local ai = species_sort_index(a)
	local bi = species_sort_index(b)
	if ai == bi then
		return a.name < b.name
	end
	return ai < bi
end

function cmp_moves(a, b)
	return a.name < b.name
end

function get_species(id)
	if tonumber(id) ~= nil then
		id = tonumber(id)
	end
	if type(id) == "number" then
		return db.species.by_index[id] or db.species.unknown
	else
		return db.species.by_name[id] or db.species.unknown
	end
end

function get_move(id)
	return db.moves.by_name[id] or db.moves.unknown
end

function subscript(value, path, path_start)
	while value ~= nil and path[path_start] ~= nil do
		if type(value) ~= "table" then
			return nil
		end
		local key = path[path_start]
		if tonumber(key) ~= nil then
			value = value[tonumber(key)]
		else
			value = value[key]
		end
		path_start = path_start + 1
	end
	return value
end

function flatten_template_args(args)
	for k,v in pairs(args) do
		if type(v) == "table" and v.name then
			args[k] = v.name
		end
		args[k] = tostring(v)
	end
end

function textify(frame, value)
	if frame.args["if_" .. tostring(value)] ~= nil then
		return frame.args["if_" .. tostring(value)]
	elseif type(value) == "number" and frame.args.if_number ~= nil then
		return string.format(frame.args.if_number, value)
	elseif type(value) == "table" and frame.args.if_empty ~= nil and table.empty(value) then
		return frame.args.if_empty
	elseif type(value) == "table" and (frame.args.delim or frame.args["foreach"] ~= nil) then
		local delim = frame.args.delim or ""
		if delim == "," then delim = delim .. " " end
		local result = ""
		local i = 1
		for key,elem in ipairs(value) do
			if i > 1 then
				result = result .. delim
			end
			if frame.args.subscript then
				elem = subscript(elem, string.split(frame.args.subscript, "."), 1)
			end
			if frame.args["foreach"] ~= nil then
				local args
				if type(elem) == "table" then
					args = table.clone(elem)
				else
					args = { elem }
				end
				if frame.args.key_param ~= nil then
					args[frame.args.key_param] = key
				end
				flatten_template_args(args)
				elem = frame:expandTemplate{title = frame.args["foreach"], args = args}
			else
				elem = textify(frame, elem)
			end
			result = result .. elem
			i = i + 1
		end
		return result
	end
	if type(frame.args.format) == "string" then
		mw.logObject(frame.args.format)
		value = string.format(frame.args.format, value)
	end
	if type(value) == "table" and value.name ~= nil then
		value = value.name
	end

	if value == nil or value == false then
		return ""
	elseif type(value) == "string" or type(value) == "number" or type(value) == "boolean" then
		return tostring(value)
	end
	return "&lt;" .. tostring(value) .. "&gt;"
end

-- Public functions for use with #invoke

function p.get_species(frame)
	local species = get_species(frame.args[1])
	return textify(frame, subscript(species, frame.args, 2))
end

function p.get_prev_species(frame)
	local species = get_species(frame.args[1])
	if species.bestiary_index ~= nil then
		species = get_species(species.bestiary_index - 1)
	end
	return textify(frame, subscript(species, frame.args, 2))
end

function p.get_next_species(frame)
	local species = get_species(frame.args[1])
	if species.bestiary_index ~= nil then
		species = get_species(species.bestiary_index + 1)
	end
	return textify(frame, subscript(species, frame.args, 2))
end

function p.species_has_family(frame)
	local species = get_species(frame.args[1])
	local result = not table.empty(species.evolves_from) or not table.empty(species.evolves_to)
	return textify(frame, subscript(result, frame.args, 2))
end

function p.get_species_compatible_moves(frame)
	local result_set = {}
	local species = get_species(frame.args[1])
	for _,tag in ipairs(species.moves.tags) do
		if db.moves.by_tag[tag] then
			for _,move in ipairs(db.moves.by_tag[tag]) do
				result_set[move.name] = move
			end
		end
	end
	local result = table.values(result_set)
	table.sort(result, cmp_moves)
	return textify(frame, subscript(result, frame.args, 2))
end

function p.get_species_list(frame)
	-- If frame.args includes either 'type' as a key, it returns only the
	-- species with that type. Otherwise it returns all species.
	-- Use this with foreach= and/or delim= in frame.args.
	local species_list
	if frame.args["type"] then
		species_list = table.values(db.species.by_type[frame.args["type"]] or {})
	else
		species_list = table.values(db.species.by_name)
	end
	table.sort(species_list, cmp_species)
	return textify(frame, subscript(result, frame.args, 2))
end

function p.get_move(frame)
	local move = get_move(frame.args[1])
	return textify(frame, subscript(move, frame.args, 2))
end

function p.get_move_compatible_species(frame)
	local result_set = {}
	local move = get_move(frame.args[1])
	for _,tag in ipairs(move.tags) do
		if db.species_by_tag[tag] then
			for _,species in ipairs(db.species.by_tag[tag]) do
				result_set[species.name] = true
			end
		end
	end
	local result = {}
	for species,_ in pairs(result_set) do
		table.insert(result, species)
	end
	table.sort(result)
	return textify(frame, subscript(result, frame.args, 2))
end

return p