Модуль:GetField: различия между версиями
Pok (обсуждение | вклад) Нет описания правки |
Pok (обсуждение | вклад) Нет описания правки |
||
| Строка 1145: | Строка 1145: | ||
if type(v) == "table" then | if type(v) == "table" then | ||
if is_array_of_primitives(v) then | if is_array_of_primitives(v) then | ||
local json = encode_nowiki_json(v) | |||
if json then | |||
parts[#parts + 1] = key .. "=" .. tostring(json) | |||
end | |||
elseif options.nestedKeyMode == "raw" then | |||
local json = encode_nowiki_json(v) | local json = encode_nowiki_json(v) | ||
if json then | if json then | ||
Версия от 12:48, 31 марта 2026
Модуль предназначен для получения данных из кэшированных JSON-страниц и их использования в шаблонах. С его помощью можно получить поле по пути, найти id по значению или сразу собрать вызов шаблона по найденным данным.
Поля берутся из json страниц
Участник:IanComradeBot/component/название(пример: Участник:IanComradeBot/component/access.json)Участник:IanComradeBot/prototype/название(пример: Участник:IanComradeBot/prototype/tag.json)
Для подпроектов
Участник:IanComradeBot/component/Проект/название(пример: Участник:IanComradeBot/Goob/component/access.json)Участник:IanComradeBot/prototype/Проект/название(пример: Участник:IanComradeBot/Goob/prototype/tag.json)
Основные функции
get
Возвращает запись целиком или отдельное поле по её id.
Использование:
- {{#invoke:GetField
|get |id |pagePath |keyPath}}
Простые значения возвращаются как текст, а таблицы в JSON-виде.
Например, json сущности MopItem из Участник:IanComradeBot/component/meleeWeapon.json выглядит так:
"MopItem": {
"damage": {
"types": {
"Blunt": 10
}
},
...
},
то мы можем получить значение как как:
- {{#invoke:GetField
|get |MopItem |component/meleeWeapon.json |damage}} -> "types": {"Blunt": 10}} - {{#invoke:GetField
|get |MopItem |component/meleeWeapon.json |damage.types}} -> {"Blunt": 10} - {{#invoke:GetField
|get |MopItem |component/meleeWeapon.json |damage.types.Blunt}} -> 10
| Параметр | Описание | Обязателен? |
|---|---|---|
|1 = | Id записи. | Да |
|2 = | Путь до JSON-страницы, например component/meleeWeapon.json. | Да |
|3 = | Путь до поля внутри записи. Поддерживает вложенность через точку и индексы вида field.1 | Да |
getTpl
Строит вызов шаблона для одного id передавая в него развёрнутые поля записи.
Использование:
- {{#invoke:GetField
|getTpl |id |pagePath |template}}
Шаблон вызывается в виде {{Имя шаблона|id=...|...поля записи...}}. Вложенные таблицы передаются как плоские параметры, а также в JSON-виде там, где это нужно для сохранения структуры.
Пример:
- {{#invoke:GetField
|getTpl |MopItem |entity/MopItem.json |Предмет}}
| Параметр | Описание | Обязателен? |
|---|---|---|
|1 = | Id записи. | Да |
|2 = | Путь до JSON-страницы. | Да |
|3 = | Имя шаблона, который будет вызван как {{Имя шаблона|id=...|...}}. | Да |
searchId / searchIdTpl
Обе функции ищут id по значению в указанном поле. Разница в результате:
searchIdвозвращает найденные id в виде JSON-массива.searchIdTplпо найденным id сразу вызывает шаблон.
Использование:
- {{#invoke:GetField
|searchId |searchValue |pagePath |keyPath}} - {{#invoke:GetField
|searchIdTpl |searchValue |pagePath |keyPath |template}} - {{#invoke:GetField
|searchIdTpl |pagePath |keyPath |template |searchType=path}}
Режимы поиска:
searchType=value— ищет id, у которых значение поля равно указанному значению. Это режим по умолчанию.searchType=key— ищет id, у которых в поле-таблице существует ключ с указанным именем.searchType=path— только дляsearchIdTpl; выводит все id, у которых поле по указанному пути существует и не пустое. В этом режиме первым параметром передаётсяpagePath, а не значение для поиска.
searchIdTpl вызывает шаблон в виде {{Имя шаблона|id=...|...поля записи...}}. Если найдено несколько id, вызовы собираются подряд через пробел.
Примеры:
- {{#invoke:GetField
|searchId |Elements |prototype/reaction.json |group}} -> ["LemonLime","IrishCoffee","DemonsBlood","SapBoiling","DevilsKiss","MoscowMule","OrangeLimeSoda","RedMead","Vodka","Tortuga","Daiquiri","Andalusia","DoctorsDelight","LongIslandIcedTea","BloodyMary","Lemonade","SpaceGlue","AmmoniaFromBlood"]
| Параметр | Описание | Обязателен? |
|---|---|---|
|1 = | Значение для поиска. | Да |
|2 = | Путь до JSON-страницы. | Да |
|3 = | Путь до поля внутри записи. Поддерживает вложенность через точку и индексы вида field.1 | Да |
|4 = | Имя шаблона, который будет вызван для каждого найденного id. | Только для searchIdTpl |
|searchType = | Режим поиска: value, key или path. | Нет; value |
hasComp
Проверяет, есть ли у сущности указанный компонент.
Использование:
- {{#invoke:GetField
|hasComp |entityId |componentName}}
Возвращает строку true или false.
Пример:
- {{#invoke:GetField
|hasComp |MopItem |Item}}
| Параметр | Описание | Обязателен? |
|---|---|---|
|1 = | Id сущности. | Да |
|2 = | Имя компонента для проверки. | Да |
searchStore / searchStoreTpl
Эти функции находят прототипы или компоненты содержащие указанный id, используя Участник:IanComradeBot/prototype_store.json или Участник:IanComradeBot/component_store.json.
Использование:
- {{#invoke:GetField
|searchStore |searchId |prototype |Название}} - {{#invoke:GetField
|searchStore |searchId |component |Название}} - {{#invoke:GetField
|searchStoreTpl |searchId |prototype |Название |Шаблон}}
searchStore возвращает JSON-массив id, найденных в компонентах/прототипах. searchStoreTpl по тем же id сразу вызывает шаблон, используя страницу вида prototype/Название.json или component/Название.json.
Примеры:
- {{#invoke:GetField
|searchStore |MopItem |component |itemBorgModule}} -> ["BorgModuleCleaning"] - {{#invoke:GetField
|searchStore |MopItem |prototype |latheRecipe}} -> ["MopItem"]
| Параметр | Описание | Обязателен? |
|---|---|---|
|1 = | Id, который ищется в хранилище. | Да |
|2 = | Тип: prototype или component. | Да |
|3 = | Имя хранилищя без .json. | Да |
|4 = | Имя шаблона, который будет вызван для каждого найденного id. | Только для searchStoreTpl |
getAll / getAllTpl
Эти функции получают все id прототипов или компонентов.
Использование:
getAll по умолчанию возвращает JSON-массив id.
getAllTpl вызывает шаблон для каждого id в виде {{Имя шаблона|id=...|...поля записи...}}.
Примеры:
- {{#invoke:GetField
|getAll |component/staticPrice.json}} -> выводит все id сущностей с этим компонентом в формате JSON - {{#invoke:GetField
|getAllTpl |component/staticPrice.json |component/staticPrice/wrapper}} -> выводит все id сущностей с этим компонентом обёрнутым в шаблон {{component/staticPrice/wrapper}}
| Параметр | Описание | Обязателен? |
|---|---|---|
|1 = | Путь до JSON-страницы. | Да |
|2 = | Имя шаблона для getAllTpl. | Только для getAllTpl |
|replace = | Строка замены для getAll. Если задана, результат выводится построчно вместо JSON-массива. | Нет |
jsonList
Преобразует JSON в список, перечисление или простой текст. Подходит для быстрого вывода данных без отдельного шаблона.
Использование:
- {{#invoke:GetField
|jsonList |["a","b","c"]}} - {{#invoke:GetField
|jsonList |{"MopItem":"Швабра"}}} - {{#invoke:GetField
|jsonList |{"MopItem":"Швабра"} |type=enum}}
Основные параметры:
type— формат вывода:list,enumилиnone.prefix— префикс строки для режима списка. По умолчанию*.sep— разделитель между ключом и значением. По умолчанию:.replace— дополнительная обработка регулярным выражением уже собранной строки.key_replace,value_replace— обработка ключей и значений регулярным выражением по отдельности.
Примеры:
- {{#invoke:GetField
|jsonList |["a","b","c"]}} - {{#invoke:GetField
|jsonList |{"MopItem":"Швабра"} |type=list}} - {{#invoke:GetField
|jsonList |{"MopItem":"Швабра"} |type=none |key_replace=<nowiki>[[\1]]</nowiki>}}
| Параметр | Описание | Обязателен? |
|---|---|---|
|1 = | JSON-строка. Можно также передать именованным параметром json. | Да |
|type = | Формат вывода: list, enum или none. | Нет; list |
|prefix = | Префикс строки для режима list. | Нет; * |
|sep = | Разделитель между ключом и значением. | Нет; : |
|key_replace = | Строка замены для ключей. | Нет; \1 |
|value_replace = | Строка замены для значений. | Нет; \1 |
|replace = | Строка замены для всего вывода. | Нет; \1 |
json
Преобразует JSON-объект или JSON-массив объектов в набор вызовов шаблона и сразу обрабатывает их.
Использование:
- {{#invoke:GetField
|json |{"MopItem":{"name":"Швабра"}} |Предмет}}
Если значение по id является объектом, его поля разворачиваются в параметры шаблона. Если значение простое, оно передаётся как value=....
Пример вызова, который будет собран функцией:
{{Предмет|id=MopItem|name=Швабра}}
| Параметр | Описание | Обязателен? |
|---|---|---|
|1 = | JSON-строка. | Да |
|2 = | Имя шаблона. | Да |
См. также
Примечания
- Если запись, поле или JSON-страница не найдены, функции обычно возвращают пустую строку.
- Функция
getвозвращает таблицы в JSON-виде. - В
searchIdиsearchIdTplзначения сравниваются как строки. - Параметр
keyPathподдерживает доступ к вложенным полям и индексам. getTplиsearchIdTplудобны, когда нужно не получить сырые данные, а сразу отрендерить карточку или другой шаблон.- Функции с
searchStoreработают только с генераторными страницами и хранилищами, где структура данных уже подготовлена под поиск по id. jsonиjsonListожидают корректный JSON; если строка не разбирается, результат будет пустым.
local p = {}
local JsonPaths = require('Module:JsonPaths')
local getArgs = require('Module:Arguments').getArgs
local function get_module_name(pagePath)
return JsonPaths.get(pagePath)
end
local function load_cached_data(moduleName)
local ok, loaded = pcall(mw.loadData, moduleName)
if not ok or not loaded then
return nil
end
return loaded
end
local function parse_indexed_part(part)
local key, idx = string.match(part, "^(.-)%[(%d+)%]$")
if key then
return key, tonumber(idx)
end
local num = tonumber(part)
if num then
return nil, num
end
return part, nil
end
local function parse_path(path)
if not path or path == "" then
return nil
end
local parsed = {}
for part in string.gmatch(path, "([^%.]+)") do
parsed[#parsed + 1] = { parse_indexed_part(part) }
end
return parsed
end
local function get_by_parsed_path(tbl, parsedPath)
if not tbl or not parsedPath then
return nil
end
local cur = tbl
for i = 1, #parsedPath do
local token = parsedPath[i]
local key = token[1]
local idx = token[2]
if key and key ~= "" then
if type(cur) ~= "table" then
return nil
end
local nextCur = cur[key]
if nextCur == nil then
nextCur = cur["!type:" .. key]
end
cur = nextCur
end
if idx then
if type(cur) ~= "table" then
return nil
end
cur = cur[idx]
end
if cur == nil then
return nil
end
end
return cur
end
local function get_by_path(tbl, path)
return get_by_parsed_path(tbl, parse_path(path))
end
local function format_value(v)
local okJson, json = pcall(mw.text.jsonEncode, v)
if okJson and json == "null" then
return "null"
end
if v == nil then
return ""
end
local t = type(v)
if t == "string" or t == "number" or t == "boolean" then
return tostring(v)
elseif t == "table" then
local ok, json2 = pcall(mw.text.jsonEncode, v)
if ok and json2 then
return json2
end
return ""
else
return tostring(v)
end
end
local function to_nowiki(v)
return "<nowiki>" .. v .. "</nowiki>"
end
local function is_array(tbl)
local max = 0
local count = 0
for k in pairs(tbl) do
if type(k) ~= "number" then
return false
end
if k > max then
max = k
end
count = count + 1
end
return count > 0 and max == count
end
local function deep_copy(src)
local dst = {}
for k, v in pairs(src) do
if type(v) == "table" then
dst[k] = deep_copy(v)
else
dst[k] = v
end
end
return dst
end
local function deep_merge(dst, src)
for k, v in pairs(src) do
if type(v) == "table" and type(dst[k]) == "table" then
deep_merge(dst[k], v)
elseif type(v) == "table" then
dst[k] = deep_copy(v)
else
dst[k] = v
end
end
end
local function resolve_entry(data, id)
if type(data) ~= "table" then
return nil
end
if id and id ~= "" then
local direct = data[id]
if direct ~= nil then
return direct
end
local idsTable = data.id
if type(idsTable) == "table" then
local specific = idsTable[id]
if type(specific) == "table" then
local base = data["default"]
if type(base) == "table" then
local merged = deep_copy(base)
deep_merge(merged, specific)
return merged
end
return deep_copy(specific)
end
end
end
local base = data["default"]
if type(base) == "table" then
return deep_copy(base)
end
return nil
end
local function resolve_path_value(data, id, parsedPath)
if type(data) ~= "table" or not parsedPath or not id or id == "" then
return nil
end
local direct = data[id]
if direct ~= nil then
local value = get_by_parsed_path(direct, parsedPath)
if value ~= nil then
return value
end
end
local idsTable = data.id
if type(idsTable) == "table" then
local specific = idsTable[id]
if type(specific) == "table" then
local value = get_by_parsed_path(specific, parsedPath)
if value ~= nil then
return value
end
end
end
local base = data["default"]
if type(base) == "table" then
return get_by_parsed_path(base, parsedPath)
end
return nil
end
local function resolve_entry_path_value(data, id, parsedPath)
if type(data) ~= "table" or not parsedPath or not id or id == "" then
return nil
end
local entry = resolve_entry(data, id)
if type(entry) ~= "table" then
return nil
end
return get_by_parsed_path(entry, parsedPath)
end
local function collect_id_keys(data)
if type(data) ~= "table" then
return {}
end
local idsTable = data.id
local ids = {}
if type(idsTable) == "table" then
for k in pairs(idsTable) do
ids[#ids + 1] = k
end
return ids
end
for k in pairs(data) do
if k ~= "default" and k ~= "id" then
ids[#ids + 1] = k
end
end
return ids
end
local function contains_target(v, target)
if type(v) == "table" then
if is_array(v) then
for _, item in ipairs(v) do
if tostring(item) == target then
return true
end
end
return false
end
for _, item in pairs(v) do
if tostring(item) == target then
return true
end
end
return false
end
return tostring(v) == target
end
local function is_nonempty_value(v)
if v == nil then
return false
end
if type(v) == "table" then
return next(v) ~= nil
end
return true
end
local function find_matching_ids(idsTable, keyPath, searchValue)
local parsedPath = parse_path(keyPath)
local target = tostring(searchValue)
local matches = {}
for idKey, entry in pairs(idsTable) do
if type(entry) == "table" then
local v = get_by_parsed_path(entry, parsedPath)
if v ~= nil and contains_target(v, target) then
matches[#matches + 1] = idKey
end
end
end
return matches
end
local function preprocess_or_return(frame, text)
if type(frame) == "table" and type(frame.preprocess) == "function" then
return frame:preprocess(text)
end
return text
end
local function get_field_loose(entry, fieldId)
local value = entry[fieldId]
if value ~= nil then
return value
end
if fieldId == "" then
return nil
end
local first = string.sub(fieldId, 1, 1)
local tail = string.sub(fieldId, 2)
value = entry[string.lower(first) .. tail]
if value ~= nil then
return value
end
return entry[string.upper(first) .. tail]
end
local function apply_pattern(s, pattern, repl)
if not pattern or pattern == "" or not s then
return s
end
local text = tostring(s)
local replacement
if repl and repl ~= "" then
replacement = tostring(repl)
replacement = replacement:gsub("\\(%d)", "%%%1")
else
replacement = "%1"
end
local patt = pattern
if not patt:find("%^") and not patt:find("%$") then
patt = "^" .. patt .. "$"
end
return (text:gsub(patt, replacement))
end
local function flatten_parts(entry)
if type(entry) ~= "table" then
return {}
end
local parts = {}
local function append_table_json(key, value)
local ok, json = pcall(mw.text.jsonEncode, value)
if ok and json then
parts[#parts + 1] = key .. "=" .. to_nowiki(json)
end
end
local function walk(tbl, prefix)
local keys = {}
for k in pairs(tbl) do
keys[#keys + 1] = k
end
table.sort(keys, function(a, b)
return tostring(a) < tostring(b)
end)
for _, k in ipairs(keys) do
local v = tbl[k]
local kStr = tostring(k)
local key = (prefix == "" and kStr or prefix .. "." .. kStr)
if type(v) == "table" then
if next(v) ~= nil then
append_table_json(key, v)
if is_array(v) then
local first = v[1]
if type(first) == "table" then
walk(first, key)
end
else
walk(v, key)
end
end
else
parts[#parts + 1] = key .. "=" .. tostring(v)
end
end
end
walk(entry, "")
return parts
end
local function flatten_entry(entry)
local parts = flatten_parts(entry)
if #parts == 0 then
return ""
end
return table.concat(parts, "|")
end
local function append_flattened_part(parts, key, value)
if value == nil then
return
end
if type(value) == "table" then
if next(value) == nil then
return
end
local ok, json = pcall(mw.text.jsonEncode, value)
if ok and json then
parts[#parts + 1] = key .. "=" .. to_nowiki(json)
end
return
end
parts[#parts + 1] = key .. "=" .. tostring(value)
end
local function flatten_selected_parts(entry, keys)
if type(entry) ~= "table" or type(keys) ~= "table" then
return {}
end
local parts = {}
local seen = {}
for i = 1, #keys do
local key = keys[i]
if type(key) == "string" and key ~= "" and not seen[key] then
seen[key] = true
append_flattened_part(parts, key, get_by_path(entry, key))
end
end
return parts
end
local function resolve_template_path(tplPath)
local templatePath = tplPath
local project = JsonPaths.project()
if project ~= nil and project ~= "" then
templatePath = tplPath .. "/" .. project
templatePath = "{{#ifexist:Шаблон:" .. templatePath .. "|" .. templatePath .. "|" .. tplPath .. "}}"
end
return templatePath
end
local function split_template_spec(tplPath, tplArgs)
tplPath = mw.text.unstripNoWiki(tplPath or "")
tplArgs = mw.text.unstripNoWiki(tplArgs or "")
if tplArgs ~= "" and string.sub(tplArgs, 1, 1) == "|" then
tplArgs = string.sub(tplArgs, 2)
end
if tplArgs == "" then
local pipePos = string.find(tplPath, "|", 1, true)
if pipePos then
tplArgs = mw.text.unstripNoWiki(string.sub(tplPath, pipePos + 1))
if tplArgs ~= "" and string.sub(tplArgs, 1, 1) == "|" then
tplArgs = string.sub(tplArgs, 2)
end
tplPath = string.sub(tplPath, 1, pipePos - 1)
end
end
return tplPath, tplArgs
end
local function build_tpl(id, pagePath, tplPath, data, tplArgs)
if id == "" or pagePath == "" or tplPath == "" then
return ""
end
local moduleName = get_module_name(pagePath)
data = data or load_cached_data(moduleName)
if not data then
return ""
end
local entry = resolve_entry(data, id)
local extra = flatten_entry(entry)
local extraTplArgs = tplArgs or ""
local templatePath = resolve_template_path(tplPath)
local tplStr = "{{Шаблон:" .. templatePath
if extraTplArgs ~= "" then
tplStr = tplStr .. "|" .. extraTplArgs
end
tplStr = tplStr .. "|id=" .. tostring(id)
if extra ~= "" then
tplStr = tplStr .. "|" .. extra
end
tplStr = tplStr .. "}}"
return tplStr
end
function p.findInGenerator(frame)
local args = frame.args or {}
local searchId = args[1] or ""
local kind = (args[2] or ""):lower()
local fieldId = args[3] or ""
if searchId == "" or fieldId == "" then
return ""
end
if kind ~= "prototype" and kind ~= "component" then
return ""
end
local storeName = (kind == "prototype") and "prototype_store.json" or "component_store.json"
local moduleName = get_module_name(storeName)
local data = load_cached_data(moduleName)
if not data then
return ""
end
local entry = data[searchId]
if type(entry) ~= "table" then
return ""
end
local value = get_field_loose(entry, fieldId)
if value == nil then
return ""
end
local out = {}
local t = type(value)
if t == "table" then
for _, v in ipairs(value) do
out[#out + 1] = v
end
else
out[1] = value
end
return mw.text.jsonEncode(out)
end
function p.flattenField(frame)
local args = frame.args or {}
local id = args[1] or ""
local pagePath = args[2] or ""
if id == "" or pagePath == "" then
return ""
end
local moduleName = get_module_name(pagePath)
local data = load_cached_data(moduleName)
if not data then
return ""
end
local entry = resolve_entry(data, id) or {}
return flatten_entry(entry)
end
function p.flattenFieldSelective(frame)
local args = frame.args or {}
local id = args[1] or ""
local pagePath = args[2] or ""
local keysJson = mw.text.unstripNoWiki(args[3] or args.keys or "")
if id == "" or pagePath == "" or keysJson == "" then
return ""
end
local okKeys, keys = pcall(mw.text.jsonDecode, keysJson)
if not okKeys or type(keys) ~= "table" or #keys == 0 then
return ""
end
local moduleName = get_module_name(pagePath)
local data = load_cached_data(moduleName)
if not data then
return ""
end
local entry = resolve_entry(data, id) or {}
local parts = flatten_selected_parts(entry, keys)
if #parts == 0 then
return ""
end
return table.concat(parts, "|")
end
function p.get(frame)
local args = getArgs(frame, { removeBlanks = false })
local id = args[1] or ""
local pagePath = args[2] or ""
local keyPath = args[3] or ""
if pagePath == "" then
return ""
end
local moduleName = get_module_name(pagePath)
local data = load_cached_data(moduleName)
if not data then
return ""
end
local entry = resolve_entry(data, id)
if entry == nil then
return ""
end
if keyPath == "" then
return format_value(entry)
end
local value = get_by_path(entry, keyPath)
return format_value(value)
end
function p.searchId(frame)
local args = getArgs(frame, { removeBlanks = false })
local searchValue = args[1] or ""
local pagePath = args[2] or ""
local keyPath = args[3] or ""
local searchType = (args.searchType or ""):lower()
if searchValue == "" or pagePath == "" or keyPath == "" then
return ""
end
if searchType == "" then
searchType = "value"
end
local moduleName = get_module_name(pagePath)
local data = load_cached_data(moduleName)
if not data then
return "[]"
end
local parsedPath = parse_path(keyPath)
if not parsedPath then
return ""
end
local idsTable = type(data.id) == "table" and data.id or data
local ids = collect_id_keys(data)
if #ids == 0 then
return ""
end
local matches
if searchType == "key" then
local target = tostring(searchValue)
matches = {}
for _, idKey in ipairs(ids) do
local v = resolve_path_value(data, idKey, parsedPath)
if type(v) == "table" and v[target] ~= nil then
matches[#matches + 1] = idKey
end
end
else
local target = tostring(searchValue)
matches = {}
if idsTable == data then
matches = find_matching_ids(idsTable, keyPath, searchValue)
else
for _, idKey in ipairs(ids) do
local v = resolve_path_value(data, idKey, parsedPath)
if v ~= nil and contains_target(v, target) then
matches[#matches + 1] = idKey
end
end
end
end
if #matches == 0 then
return ""
end
local ok, json = pcall(mw.text.jsonEncode, matches)
if ok and json then
return json
end
return ""
end
function p.searchIdTpl(frame)
local args = getArgs(frame, { removeBlanks = false })
local searchValue = args[1] or ""
local pagePath = args[2] or ""
local keyPath = args[3] or ""
local tplPath = mw.text.unstripNoWiki(args[4] or "")
local tplArgs = args.tplArgs or args.templateArgs or ""
local searchType = (args.searchType or ""):lower()
if searchType == "" then
searchType = "value"
end
if searchType == "path" then
searchValue = ""
pagePath = args[1] or ""
keyPath = args[2] or ""
tplPath = mw.text.unstripNoWiki(args[3] or "")
tplArgs = args[4] or args.tplArgs or args.templateArgs or ""
end
tplPath, tplArgs = split_template_spec(tplPath, tplArgs)
if pagePath == "" or keyPath == "" or tplPath == "" then
return ""
end
if searchType ~= "path" and searchValue == "" then
return ""
end
local moduleName = get_module_name(pagePath)
local data = load_cached_data(moduleName)
if not data then
return ""
end
local parsedPath = parse_path(keyPath)
if not parsedPath then
return ""
end
local ids = collect_id_keys(data)
if #ids == 0 then
return ""
end
local matches
if searchType == "path" then
matches = {}
for _, idKey in ipairs(ids) do
local v = resolve_entry_path_value(data, idKey, parsedPath)
if is_nonempty_value(v) then
matches[#matches + 1] = idKey
end
end
elseif searchType == "key" then
local target = tostring(searchValue)
matches = {}
for _, idKey in ipairs(ids) do
local v = resolve_path_value(data, idKey, parsedPath)
if type(v) == "table" and v[target] ~= nil then
matches[#matches + 1] = idKey
end
end
else
local idsTable = data.id
if type(idsTable) == "table" then
local directMatches = find_matching_ids(idsTable, keyPath, searchValue)
local defaultValue = nil
local includeDefault = false
local base = data["default"]
if type(base) == "table" then
defaultValue = get_by_parsed_path(base, parsedPath)
includeDefault = defaultValue ~= nil and contains_target(defaultValue, tostring(searchValue))
end
if includeDefault and #directMatches < #ids then
local seen = {}
for i = 1, #directMatches do
seen[directMatches[i]] = true
end
matches = {}
for _, idKey in ipairs(directMatches) do
matches[#matches + 1] = idKey
end
for _, idKey in ipairs(ids) do
if not seen[idKey] then
local specific = idsTable[idKey]
local specificValue = type(specific) == "table" and get_by_parsed_path(specific, parsedPath) or
nil
if specificValue == nil then
matches[#matches + 1] = idKey
end
end
end
else
matches = directMatches
end
else
local target = tostring(searchValue)
matches = {}
for _, idKey in ipairs(ids) do
local v = resolve_path_value(data, idKey, parsedPath)
if v ~= nil and contains_target(v, target) then
matches[#matches + 1] = idKey
end
end
end
end
if #matches == 0 then
return ""
end
local out = {}
for _, idKey in ipairs(matches) do
local tpl = build_tpl(idKey, pagePath, tplPath, data, tplArgs)
if tpl ~= "" then
out[#out + 1] = tpl
end
end
if #out == 0 then
return ""
end
local result = table.concat(out, " ")
return preprocess_or_return(frame, result)
end
function p.getTpl(frame)
local args = getArgs(frame, { removeBlanks = false })
local id = args[1] or ""
local pagePath = args[2] or ""
local tplPath = mw.text.unstripNoWiki(args[3] or "")
local tplArgs = args[4] or args.tplArgs or args.templateArgs or ""
tplPath, tplArgs = split_template_spec(tplPath, tplArgs)
if id == "" or pagePath == "" or tplPath == "" then
return ""
end
local moduleName = get_module_name(pagePath)
local data = frame.data
if not data then
data = load_cached_data(moduleName)
end
if not data then
return ""
end
local tplStr = build_tpl(id, pagePath, tplPath, data, tplArgs)
return preprocess_or_return(frame, tplStr)
end
function p.searchStoreTpl(frame)
local args = getArgs(frame, { removeBlanks = false })
local searchId = args[1] or ""
local kind = (args[2] or ""):lower()
local generatorId = args[3] or ""
local tplPath = mw.text.unstripNoWiki(args[4] or "")
local tplArgs = args[5] or args.tplArgs or args.templateArgs or ""
tplPath, tplArgs = split_template_spec(tplPath, tplArgs)
if searchId == "" or generatorId == "" or tplPath == "" then
return ""
end
if kind ~= "prototype" and kind ~= "component" then
return ""
end
local dir = (kind == "prototype") and "prototype/" or "component/"
local pagePath = dir .. generatorId .. ".json"
local idsJson = p.findInGenerator({ args = { searchId, kind, generatorId } })
local ok, ids = pcall(mw.text.jsonDecode, idsJson or "")
if not ok or type(ids) ~= "table" or #ids == 0 then
return ""
end
local moduleName = get_module_name(pagePath)
local data = load_cached_data(moduleName)
if not data then
return ""
end
local out = {}
for _, id in ipairs(ids) do
local tpl = build_tpl(id, pagePath, tplPath, data, tplArgs)
if tpl ~= "" then
out[#out + 1] = tpl
end
end
local result = table.concat(out, " ")
return preprocess_or_return(frame, result)
end
function p.flattenParams(entry)
return flatten_parts(entry)
end
function p.searchStore(frame)
local args = getArgs(frame, { removeBlanks = false })
local searchId = args[1] or ""
local kind = (args[2] or ""):lower()
local generatorId = args[3] or ""
if searchId == "" or generatorId == "" then
return ""
end
if kind ~= "prototype" and kind ~= "component" then
return ""
end
local idsJson = p.findInGenerator({ args = { searchId, kind, generatorId } })
local ok, ids = pcall(mw.text.jsonDecode, idsJson or "")
if not ok or type(ids) ~= "table" or #ids == 0 then
return ""
end
local okOut, outJson = pcall(mw.text.jsonEncode, ids)
if okOut and outJson then
return outJson
end
return ""
end
function p.hasComp(frame)
local args = getArgs(frame, { removeBlanks = false })
local entityId = args[1] or ""
local compName = args[2] or ""
if entityId == "" or compName == "" then
return "false"
end
local moduleName = get_module_name("component.json")
local data = load_cached_data(moduleName)
if not data then
return "false"
end
if type(data) ~= "table" then
return "false"
end
local entry = data[entityId]
if type(entry) ~= "table" then
return "false"
end
local target = tostring(compName)
for _, v in ipairs(entry) do
if tostring(v) == target then
return "true"
end
end
return "false"
end
function p.getAll(frame)
local args = getArgs(frame, { removeBlanks = false })
local pagePath = args[1] or ""
local replace = mw.text.unstripNoWiki(args.replace or "")
local pattern = mw.text.unstripNoWiki(args.pattern or "(.*)")
if pagePath == "" then
return ""
end
local moduleName = get_module_name(pagePath)
local data = load_cached_data(moduleName)
if not data then
return ""
end
local idsTable = data.id
if type(idsTable) ~= "table" then
return ""
end
local ids = {}
for k in pairs(idsTable) do
ids[#ids + 1] = k
end
table.sort(ids)
if replace ~= "" then
local out = {}
for _, id in ipairs(ids) do
local text = apply_pattern(id, pattern, replace)
if text ~= "" then
out[#out + 1] = text
end
end
if #out == 0 then
return ""
end
return preprocess_or_return(frame, table.concat(out, "\n"))
end
local ok, json = pcall(mw.text.jsonEncode, ids)
if ok and json then
return json
end
return ""
end
function p.getAllTpl(frame)
local args = getArgs(frame, { removeBlanks = false })
local pagePath = args[1] or ""
local tplPath = args[2] or ""
local tplArgs = args[3] or args.tplArgs or args.templateArgs or ""
tplPath, tplArgs = split_template_spec(tplPath, tplArgs)
if pagePath == "" or tplPath == "" then
return ""
end
local moduleName = get_module_name(pagePath)
local data = load_cached_data(moduleName)
if not data then
return ""
end
local idsTable = data.id
if type(idsTable) ~= "table" then
return ""
end
local out = {}
for idKey in pairs(idsTable) do
local tpl = build_tpl(idKey, pagePath, tplPath, data, tplArgs)
if tpl ~= "" then
out[#out + 1] = tpl
end
end
table.sort(out)
local result = table.concat(out, " ")
return preprocess_or_return(frame, result)
end
local function encode_nowiki_json(value)
local ok, json = pcall(mw.text.jsonEncode, value)
if ok and json then
return to_nowiki(json)
end
return nil
end
local function collect_sorted_keys(tbl, stringOnly)
local keys = {}
for k in pairs(tbl) do
if not stringOnly or type(k) == "string" then
keys[#keys + 1] = k
end
end
table.sort(keys, function(a, b)
return tostring(a) < tostring(b)
end)
return keys
end
local function choose_id_key(obj)
local keys = {}
for k in pairs(obj) do
if type(k) == "string" then
keys[#keys + 1] = k
end
end
if #keys == 0 then
return nil
end
table.sort(keys, function(a, b)
local av = obj[a]
local bv = obj[b]
local aPrimitive = type(av) ~= "table"
local bPrimitive = type(bv) ~= "table"
if aPrimitive ~= bPrimitive then
return aPrimitive
end
return tostring(a) < tostring(b)
end)
return keys[1]
end
local function is_array_of_primitives(tbl)
if type(tbl) ~= "table" or not is_array(tbl) then
return false
end
for _, v in ipairs(tbl) do
if type(v) == "table" then
return false
end
end
return true
end
local function append_table_fields(parts, value, options, prefix)
if type(value) ~= "table" or next(value) == nil then
return
end
if options.skipPrimitiveRoot and is_array_of_primitives(value) then
return
end
if prefix and options.includeJsonAtPrefix then
local json = encode_nowiki_json(value)
if json then
parts[#parts + 1] = prefix .. "=" .. tostring(json)
end
end
local keys = collect_sorted_keys(value, false)
for _, k in ipairs(keys) do
local v = value[k]
local key
if prefix and options.nestedKeyMode == "prefixed" then
key = prefix .. "." .. tostring(k)
else
key = tostring(k)
end
if type(v) == "table" then
if is_array_of_primitives(v) then
local json = encode_nowiki_json(v)
if json then
parts[#parts + 1] = key .. "=" .. tostring(json)
end
elseif options.nestedKeyMode == "raw" then
local json = encode_nowiki_json(v)
if json then
parts[#parts + 1] = key .. "=" .. tostring(json)
end
end
if next(v) ~= nil then
local childPrefix = (options.nestedKeyMode == "prefixed") and key or nil
append_table_fields(parts, v, options, childPrefix)
end
else
parts[#parts + 1] = key .. "=" .. tostring(v)
end
end
end
function p.json(frame)
local args = getArgs(frame, { removeBlanks = false })
local jsonStr = mw.text.unstripNoWiki(args[1] or args.json or "")
local tplPath = mw.text.unstripNoWiki(args[2] or args.template or "")
local tplArgs = args[3] or args.tplArgs or args.templateArgs or ""
tplPath, tplArgs = split_template_spec(tplPath, tplArgs)
if jsonStr == "" or tplPath == "" then
return ""
end
local ok, data = pcall(mw.text.jsonDecode, jsonStr)
if not ok or type(data) ~= "table" then
return ""
end
local calls = {}
local nestedOptions = {
includeJsonAtPrefix = true,
nestedKeyMode = "prefixed",
skipPrimitiveRoot = false,
}
local rawTypeOptions = {
includeJsonAtPrefix = false,
nestedKeyMode = "raw",
skipPrimitiveRoot = true,
}
local function makeCall(obj)
if type(obj) ~= "table" then
return
end
local idKey = choose_id_key(obj)
if not idKey then
return
end
local parts = { "{{Шаблон:" .. resolve_template_path(tplPath) }
if tplArgs ~= "" then
parts[#parts + 1] = tplArgs
end
parts[#parts + 1] = tostring(idKey)
local keys = collect_sorted_keys(obj, true)
for _, k in ipairs(keys) do
local v = obj[k]
if k == idKey then
if type(k) == "string" and string.sub(k, 1, 6) == "!type:" then
if type(v) == "table" then
append_table_fields(parts, v, rawTypeOptions, nil)
elseif v ~= nil then
parts[#parts + 1] = "value=" .. tostring(v)
end
elseif type(v) == "table" then
if is_array_of_primitives(v) then
local json = encode_nowiki_json(v)
if json then
parts[#parts + 1] = "value=" .. tostring(json)
end
end
if next(v) ~= nil then
append_table_fields(parts, v, nestedOptions, k)
end
elseif v ~= nil then
parts[#parts + 1] = "value=" .. tostring(v)
end
else
if type(v) == "table" then
if next(v) ~= nil then
append_table_fields(parts, v, nestedOptions, k)
end
elseif v ~= nil then
parts[#parts + 1] = k .. "=" .. tostring(v)
end
end
end
parts[#parts + 1] = "}}"
calls[#calls + 1] = table.concat(parts, "|")
end
if is_array(data) then
for _, item in ipairs(data) do
makeCall(item)
end
else
makeCall(data)
end
if #calls == 0 then
return ""
end
return frame:preprocess(table.concat(calls, " "))
end
function p.jsonList(frame)
local args = getArgs(frame, { removeBlanks = false })
local jsonStr = mw.text.unstripNoWiki(args[1] or args.json or "")
if jsonStr == "" then
return ""
end
local ok, data = pcall(mw.text.jsonDecode, jsonStr)
if not ok or type(data) ~= "table" then
return ""
end
local outputType = (args.type or "list"):lower()
local bullet = mw.text.unstripNoWiki(args.prefix or "* ")
local sep = mw.text.unstripNoWiki(args.sep or ": ")
if outputType == "none" then
bullet = ""
sep = ""
end
local keyPattern = mw.text.unstripNoWiki(args.key_pattern or "(.*)")
local keyReplace = mw.text.unstripNoWiki(args.key_replace or "\\1")
local valuePattern = mw.text.unstripNoWiki(args.value_pattern or "(.*)")
local valueReplace = mw.text.unstripNoWiki(args.value_replace or "\\1")
local pairPattern = mw.text.unstripNoWiki(args.pattern or "(.*)")
local pairReplace = mw.text.unstripNoWiki(args.replace or "\\1")
local out = {}
if is_array(data) then
for _, v in ipairs(data) do
local text = ""
if type(v) == "table" then
if is_array(v) then
text = table.concat(v, ", ")
else
local okJson, jsonVal = pcall(mw.text.jsonEncode, v)
if okJson and jsonVal then
text = jsonVal
end
end
else
text = tostring(v)
end
if text ~= "" then
local patt = valuePattern ~= "" and valuePattern or keyPattern
local repl = valueReplace ~= "" and valueReplace or keyReplace
text = apply_pattern(text, patt, repl)
local line
if outputType == "enum" then
line = text
else
line = bullet .. text
end
if pairPattern ~= "" then
line = apply_pattern(line, pairPattern, pairReplace)
end
table.insert(out, line)
end
end
else
local keys = {}
for k in pairs(data) do
keys[#keys + 1] = k
end
table.sort(keys, function(a, b)
return tostring(a) < tostring(b)
end)
for _, k in ipairs(keys) do
local v = data[k]
local vStr
if type(v) == "table" then
local okJson, jsonVal = pcall(mw.text.jsonEncode, v)
if okJson and jsonVal then
vStr = jsonVal
else
vStr = ""
end
else
vStr = tostring(v)
end
local baseKey = apply_pattern(tostring(k), keyPattern, "\\1")
local MARK_KEY = "\31KEY\31"
local vRepl = (valueReplace or "\\1"):gsub("\\2", MARK_KEY)
local vStr0 = apply_pattern(vStr, valuePattern, vRepl)
vStr0 = tostring(vStr0):gsub(MARK_KEY, baseKey)
local MARK_VAL = "\31VAL\31"
local kRepl = (keyReplace or "\\1"):gsub("\\2", MARK_VAL)
local keyStr0 = apply_pattern(tostring(k), keyPattern, kRepl)
local keyStr = tostring(keyStr0):gsub(MARK_VAL, vStr0)
vStr = vStr0
if vStr ~= "" then
local line
if outputType == "enum" then
line = vStr .. " " .. keyStr
else
line = bullet .. keyStr .. sep .. vStr
end
if pairPattern ~= "" then
line = apply_pattern(line, pairPattern, pairReplace)
end
table.insert(out, line)
end
end
end
if outputType == "enum" then
return frame:preprocess(table.concat(out, ", "))
elseif outputType == "list" then
return frame:preprocess(table.concat(out, "\n"))
else
return frame:preprocess(table.concat(out, " "))
end
end
return p