Перейти к содержанию

Модуль:Проблемы с объектом

Материал из LemonCraft Wiki

Для документации этого модуля может быть создана страница Модуль:Проблемы с объектом/doc

local p = {}

p.i18n = {
	process_args_module = "Модуль:ProcessArgs",
	description_arg_prefix = "назв", -- specify a string if you want to have separate parameters for descriptions of searched terms.
	                              -- Useful as terms are necessarily English, and users may want to have localized descriptions
	term_wrap = '«%s»', -- a string wrapped into quotes, for text output; quotes may be language-specific
	pair_separator = ", ",
	final_or_separator = " и ",
	two_item_or_separator = " и ", -- the separators are used to concatenate tables to make strings like "A or B", "A, B, or C"
	message = 'Отчёты об ошибках, связанных с %s, поддерживаются в системе отслеживания ошибок [[Mojira]]. Сообщайте о найденных ошибках [https://bugs.mojang.com/issues/?jql=%s там].',
	message_dungeons = 'Отчёты об ошибках \'\'Minecraft Dungeons\'\', связанных с %s, с 28 сентября 2023 года не поддерживаются в системе отслеживания ошибок [[Mojira]]. Ошибки, обнаруженные до этой даты, можно увидеть [https://bugs.mojang.com/issues/?jql=%s здесь].',
	message_legends = 'Отчёты об ошибках \'\'Minecraft Legends\'\', связанных с %s, с 10 января 2024 года не поддерживаются в системе отслеживания ошибок [[Mojira]]. Ошибки, обнаруженные до этой даты, можно увидеть [https://bugs.mojang.com/issues/?jql=%s здесь].',
	namespace_dungeons = 10000,
	namespace_legends = 10006,
	release_mc = '1.21.4'
}

--[[
Concatenates a table using a set of separators. Behavior depends on which of the separators are provided.

Parameters:
 • some_table (sequence table):
     the table to concatenate; required
 • pair_sep (string; required), final_sep (string), two_sep (string):
     separators used in concatenation

If only `pair_sep` is provided, behaves like `table.concat(some_table, pair_sep)`.

If `final_sep` is also provided, it is used to separate the last pair of table entries.

If `two_sep` is also provided, it is used to separate the entries instead of `final_sep` if there are two of them.

Returns a string with the concatenated table.

Example:

```
local table1 = {"a", "b", "c"}
local table2 = {"a", "b"}

print( concat_table_3sep(table1, ", ") ) -- prints "a, b, c"
print( concat_table_3sep(table1, ", ", " and ") ) -- prints "a, b and c"
print( concat_table_3sep(table1, ", ", ", and ", " and ") ) -- prints "a, b, and c"

print( concat_table_3sep(table2, ", ") ) -- prints "a, b"
print( concat_table_3sep(table2, ", ", " and ") ) -- prints "a and b"
print( concat_table_3sep(table2, ", ", ", and ", " and ") ) -- prints "a and b"
```
]]
function p.concat_table_3sep(some_table, pair_sep, final_sep, two_sep)
	local length = #some_table
	
	if length < 3 then
		local sep = two_sep or final_sep or pair_sep
		return table.concat(some_table, sep)
	end

	local result_table = {}
	for index = 1, length - 2 do
		table.insert(result_table, some_table[index])
		table.insert(result_table, pair_sep)
	end
	table.insert(result_table, some_table[length - 1])
	table.insert(result_table, final_sep or pair_sep)
	table.insert(result_table, some_table[length])
	
	return table.concat(result_table)
end

--[[
Similar to ipairs, but iterates over string keys with a constant prefix and a
variable integer suffix.

Parameters:
 • some_table (table):
     the table to iterate over
 • prefix (string):
     the key prefix to use when iterating
 • allow_numberless (boolean, optional):
     if truthy, allows reading the key without an integer suffix as if it had the
     suffix 1. WARNING: behavior is unspecified if:
      1) allow_numberless is truthy;
      2) a key without an integer suffix is mapped to a value;
      3) a key with integer suffix 1 is also mapped to a value.
     In such cases, there is no guarantee on which of the two values is used.

Example:
```
local t = {
	a1 = 1,
	a2 = 4,
	a3 = 9
} 

for index, value in prefixed_ipairs(t, "a") do
	print(index, value)
end
```

This prints
```
1	1
2	4
3	9
```

With `allow_numberless = true`, the above example would also work identically
for the following table:
```
local t = {
	a = 1,
	a2 = 4,
	a3 = 9
}
```

However, with the following table, the example may print either 1 or 2 in the
first line, depending on how prefixed_ipairs is currently implemented:
```
local t = {
	a = 1,
	a1 = 2, -- Don't do this!
	a2 = 4,
	a3 = 9
}
```
]]
function p.prefixed_ipairs(some_table, prefix, allow_numberless)
	local i = 0
	return function()
		i = i + 1
		local value = some_table[prefix .. tostring(i)]
		if i == 1 and allow_numberless and some_table[prefix] ~= nil then
			value = some_table[prefix]
		end
		if value ~= nil then
			return i, value
		end
	end
end

-- Helper functions ------------------------------------------------------------
-- Returns the human-readable description of search terms.
local function get_subject_string(inclusions)
	local lang = mw.language.getContentLanguage()
	local wrapped_inclusions = {}
	for index, inclusion in ipairs(inclusions) do
		table.insert(wrapped_inclusions, p.i18n.term_wrap:format(lang:ucfirst(inclusion)))
	end
	return p.concat_table_3sep(
		wrapped_inclusions,
		p.i18n.pair_separator,
		p.i18n.final_or_separator,
		p.i18n.two_item_or_separator
	)
end

-- Returns the part of the query string that lists what items to query.
local function get_query_string_inclusions(inclusions)
	local query_inclusions = {}
	for index, inclusion in ipairs(inclusions) do
		table.insert(query_inclusions, 'summary ~ "' .. inclusion .. '"')
	end
	return "(" .. table.concat(query_inclusions, ' OR ') .. ")"
end

-- Returns the part of the query string that removes certain items from the output.
local function get_query_string_exclusions(exclusions)
	if #exclusions == 0 then
		return ""
	end
	
	local negated_exclusions = {}
	for index, exclusion in ipairs(exclusions) do
		table.insert(negated_exclusions, "-" .. exclusion)
	end
	local exclusions_string = table.concat(negated_exclusions, " ")
	return (' AND summary ~ "%s"'):format(exclusions_string)
end

--[[
Returns the link query string for Mojira.

Note about numbers for resolution: the JIRA manual recommends
searching resolution IDs instead of names. Here's Mojira's IDs:
	1 = fixed
	2 = won't fix
	3 = duplicate
	4 = incomplete
	5 = cannot reproduce
	6 = works as intended
	7 = invalid
"Unresolved" is seen as "empty" in JIRA.
]]
local function get_link_query_string(projects, inclusions_string, exclusions_string)
	local query_string = 'project in ('
		.. projects
		.. ') AND (resolution is EMPTY OR resolution in (1, 2, 6))'
		.. exclusions_string
		.. " AND "
		.. inclusions_string
		.. " ORDER BY resolution DESC"
	return mw.uri.encode(query_string, "PATH")
end

--[[
Returns the api query strings for Mojira.
]]
local function get_api_query_strings(api_list, projects, inclusions_string, exclusions_string)
	for project in mw.text.gsplit( projects, '%s*,%s*' ) do
		if project == 'MC' then
			api_list:attr( 'data-mc',
				-- No project needed due to post-migration API quirks
				-- 'project = MC'
				'"Confirmation Status" != Unconfirmed'
				.. ' AND ('
				.. 'resolution is EMPTY'
				.. ' OR ('
				.. 'resolution = "Fixed"'
				.. ' AND affectedVersion = "' .. p.i18n.release_mc .. '"'
				.. '))'
				.. exclusions_string
				.. ' AND '
				.. inclusions_string
				.. ' ORDER BY '
				.. 'resolution ASC, '
				.. '"Mojang Priority" ASC, '
				.. 'votes DESC, '
				.. 'key ASC'
			)
		elseif project == 'MCD' or project == 'MCLG' then
			api_list:attr( 'data-' .. project:lower(),
				-- 'project = ' .. project
				'"Confirmation Status" != Unconfirmed'
				.. ' AND (resolution is EMPTY OR resolution = "Won\'t Fix")'
				.. exclusions_string
				.. ' AND '
				.. inclusions_string
				.. ' ORDER BY '
				.. 'votes DESC, '
				.. 'key ASC'
			)
		else
			api_list:attr( 'data-' .. project:lower(),
				-- 'project = ' .. project
				'"Confirmation Status" != Unconfirmed'
				.. ' AND resolution is EMPTY'
				.. exclusions_string
				.. ' AND '
				.. inclusions_string
				.. ' ORDER BY '
				.. 'votes DESC, '
				.. 'key ASC'
			)
		end
	end
end

-- Main functions and entry points ---------------------------------------------
p.issue_list = function(descriptions, projects, inclusions, exclusions)
	local subject_string = get_subject_string(descriptions or inclusions)
	local inclusions_string = get_query_string_inclusions(inclusions)
	local exclusions_string = get_query_string_exclusions(exclusions)
	local link_query_string = get_link_query_string(projects, inclusions_string, exclusions_string)
	
	local message = p.i18n.message
	if mw.title.getCurrentTitle().namespace == p.i18n.namespace_dungeons then
		message = p.i18n.message_dungeons
	elseif mw.title.getCurrentTitle().namespace == p.i18n.namespace_legends then
		message = p.i18n.message_legends
	end
	message = message:format(subject_string, link_query_string)
	
	local api_list = mw.html.create( 'div' )
	api_list:addClass( 'issue-list' )
	get_api_query_strings(api_list, projects, inclusions_string, exclusions_string)
	
	return message .. '\n' .. tostring( api_list )
end

-- Entry point for [[Template:Issue list]].
p.issue_list_entry = function(frame)
	local args = require(p.i18n.process_args_module).norm()
	
	local inclusions = {}
	local exclusions = {}
	for _, arg in ipairs(args) do
		if arg:find("^-") then
			table.insert(exclusions, (arg:gsub("-", "")))
		else
			table.insert(inclusions, arg)
		end
	end
	
	if #inclusions == 0 then
		inclusions[1] = mw.title.getCurrentTitle().text
	end
	
	local projects = args.projects or nil
	if projects == nil then
		if mw.title.getCurrentTitle().namespace == p.i18n.namespace_dungeons then
			projects = 'MCD'
		elseif mw.title.getCurrentTitle().namespace == p.i18n.namespace_legends then
			projects = 'MCLG'
		else
			projects = 'MC, MCPE'
		end
	end
	
	local descriptions
	if p.i18n.description_arg_prefix then
		descriptions = {}
		for _, arg in p.prefixed_ipairs(args, p.i18n.description_arg_prefix, true) do
			table.insert(descriptions, arg)
		end
		if #descriptions == 0 then
			table.insert(descriptions, mw.title.getCurrentTitle().text)
		end
	end
		
	return p.issue_list(descriptions, projects, inclusions, exclusions)
end

return p