Модуль:GetField: различия между версиями

Нет описания правки
Метка: отменено
мНет описания правки
 
(не показано 14 промежуточных версий этого же участника)
Строка 4: Строка 4:
local getArgs = require('Module:Arguments').getArgs
local getArgs = require('Module:Arguments').getArgs


local resolve_entry
function p.loadCachedData(pagePath)
local moduleApiCache = {}
local titleName = JsonPaths.get(pagePath)
 
if not titleName or titleName == "" then
local function load_module_api(moduleName)
return nil
if moduleApiCache[moduleName] ~= nil then
return moduleApiCache[moduleName]
end
end


local ok, loaded = pcall(require, moduleName)
local ok, data = pcall(mw.loadData, titleName)
if ok and type(loaded) == "table" then
if not ok or type(data) ~= "table" then
moduleApiCache[moduleName] = loaded
return nil
return loaded
end
 
moduleApiCache[moduleName] = false
return nil
end
 
local function load_cached_data(moduleName)
local ok, loaded = pcall(mw.loadData, moduleName)
if ok and type(loaded) == "table" and type(loaded.all) ~= "function" then
return loaded
end
 
local api = load_module_api(moduleName)
if api and type(api.all) == "function" then
return api.all()
end
 
return nil
end
 
local function load_entry_data(moduleName, id)
local api = load_module_api(moduleName)
if api and type(api.get) == "function" then
return api.get(id)
end
end


local data = load_cached_data(moduleName)
return data
return resolve_entry(data, id)
end
end


Строка 180: Строка 152:
end
end


resolve_entry = function(data, id)
function p.ucfirst(s)
if type(data) ~= "table" then
if not s or s == "" then return s end
return string.upper(s:sub(1, 1)) .. (s:sub(2) or "")
end
 
function p.normalizeComponentName(name)
if type(name) ~= "string" or name == "" then
return nil
end
name = mw.text.trim(name)
if name:sub(1, 5) == "type:" then
name = name:sub(6)
elseif name:sub(1, 6) == "!type:" then
name = name:sub(7)
end
if name == "" then
return nil
return nil
end
end
return name
end


if id and id ~= "" then
local function split_data_page_path(pagePath)
local direct = data[id]
local kind, name = string.match(pagePath or "", "^(component)/([^/]+)%.json$")
if direct ~= nil then
if not kind then
return direct
return nil, nil
end
end
return kind, p.ucfirst(name)
end


local idsTable = data.id
local function get_component_from_entity(entity, componentName)
if type(idsTable) == "table" then
if type(entity) ~= "table" or type(entity.components) ~= "table" then
local specific = idsTable[id]
return nil
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 specific
end
end
end
end


local base = data["default"]
return entity.components["type:" .. componentName]
if type(base) == "table" then
or entity.components[componentName]
return base
or entity.components["!type:" .. componentName]
end
 
local function load_specific_entry(pagePath, id, allowDefault)
local kind, name = split_data_page_path(pagePath)
if not kind or not id or id == "" then
return nil
end
end


return nil
if kind ~= "component" then
end
return nil
end


local function collect_id_keys(data)
local base
if type(data) ~= "table" then
local defaultData = p.loadCachedData(kind .. "/" .. name .. "/defaultFields.json")
return {}
if type(defaultData) == "table" then
base = deep_copy(defaultData)
end
end


local idsTable = data.id
local entityPage = "prototype/Entity/" .. id .. ".json"
local ids = {}
local entity = p.loadCachedData(entityPage)
local specific = get_component_from_entity(entity, name)


if type(idsTable) == "table" then
if type(specific) ~= "table" then
for k in pairs(idsTable) do
if allowDefault == false then
ids[#ids + 1] = k
return nil
end
end
return ids
return base
end
end


for k in pairs(data) do
if base then
if k ~= "default" and k ~= "id" then
deep_merge(base, specific)
ids[#ids + 1] = k
return base
end
end
end


return ids
return specific
end
end


local function contains_target(v, target)
local function resolve_entry_from_data(data, id, allowDefault)
if type(v) == "string" then
if type(data) ~= "table" then
return v == target
return nil
end
end
if type(v) == "table" then
 
if is_array(v) then
if id and id ~= "" then
for _, item in ipairs(v) do
local direct = data[id]
if tostring(item) == target then
if direct ~= nil then
return true
return direct
end
end
return false
end
end


for _, item in pairs(v) do
local idsTable = data.id
if tostring(item) == target then
if type(idsTable) == "table" then
return true
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 specific
end
end
end
end
return false
end
end


return tostring(v) == target
if allowDefault == false then
return nil
end
 
local base = data["default"]
if type(base) == "table" then
return base
end
 
return nil
end
end


local function is_nonempty_value(v)
local function load_entry(id, pagePath, data, allowDefault)
if v == nil then
local specificPageEntry = load_specific_entry(pagePath, id, allowDefault)
return false
if specificPageEntry ~= nil then
return specificPageEntry
end
end
if type(v) == "table" then
 
return next(v) ~= nil
data = data or p.loadCachedData(pagePath)
if allowDefault == nil then
allowDefault = true
end
end
return true
return resolve_entry_from_data(data, id, allowDefault)
end
end


local function preprocess_or_return(frame, text)
local function collect_id_keys(data)
if type(frame) == "table" and type(frame.preprocess) == "function" then
if type(data) ~= "table" then
return frame:preprocess(text)
return {}
end
end
return text
end


local function get_field_loose(entry, fieldId)
local idsTable = data.id
local value = entry[fieldId]
local ids = {}
if value ~= nil then
 
return value
if type(idsTable) == "table" then
end
for k in pairs(idsTable) do
if fieldId == "" then
ids[#ids + 1] = k
return nil
end
return ids
end
end


local first = string.sub(fieldId, 1, 1)
for k in pairs(data) do
local tail = string.sub(fieldId, 2)
if k ~= "default" and k ~= "id" then
 
ids[#ids + 1] = k
value = entry[string.lower(first) .. tail]
end
if value ~= nil then
return value
end
end


return entry[string.upper(first) .. tail]
return ids
end
end


local function apply_pattern(s, pattern, repl)
local function contains_target(v, target)
if not pattern or pattern == "" or not s then
if type(v) == "string" then
return s
return v == target
end
end
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


local text = tostring(s)
for _, item in pairs(v) do
local replacement
if tostring(item) == target then
if repl and repl ~= "" then
return true
replacement = tostring(repl)
end
replacement = replacement:gsub("\\(%d)", "%%%1")
end
else
return false
replacement = "%1"
end
end


local patt = pattern
return tostring(v) == target
if not patt:find("%^") and not patt:find("%$") then
end
patt = "^" .. patt .. "$"
 
local function is_nonempty_value(v)
if v == nil then
return false
end
if type(v) == "table" then
return next(v) ~= nil
end
end
 
return true
return (text:gsub(patt, replacement))
end
end


local function flatten_parts(entry)
local function preprocess_or_return(frame, text)
if type(entry) ~= "table" then
if type(frame) == "table" and type(frame.preprocess) == "function" then
return {}
return frame:preprocess(text)
end
end
return text
end


local parts = {}
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 function walk(tbl, prefix)
local first = string.sub(fieldId, 1, 1)
local keys = {}
local tail = string.sub(fieldId, 2)
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
value = entry[string.lower(first) .. tail]
local v = tbl[k]
if value ~= nil then
local kStr = tostring(k)
return value
local key = (prefix == "" and kStr or prefix .. "." .. kStr)
end


if type(v) == "table" then
return entry[string.upper(first) .. tail]
if next(v) ~= nil then
local ok, json = pcall(mw.text.jsonEncode, v)
if ok and json then
parts[#parts + 1] = key .. "=<nowiki>" .. json .. "</nowiki>"
end
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
end


local function flatten_entry(entry)
local function apply_pattern(s, pattern, repl)
local parts = flatten_parts(entry)
if not pattern or pattern == "" or not s then
if #parts == 0 then
return s
return ""
end
end
return table.concat(parts, "|")
end


local function append_flattened_part(parts, key, value)
local text = tostring(s)
if value == nil then
local replacement
return
if repl and repl ~= "" then
replacement = tostring(repl)
replacement = replacement:gsub("\\(%d)", "%%%1")
else
replacement = "%1"
end
end


if type(value) == "table" then
local patt = pattern
if next(value) == nil then
if not patt:find("%^") and not patt:find("%$") then
return
patt = "^" .. patt .. "$"
end
end


local ok, json = pcall(mw.text.jsonEncode, value)
return (text:gsub(patt, replacement))
if ok and json then
parts[#parts + 1] = key .. "=<nowiki>" .. json .. "</nowiki>"
end
return
end
 
parts[#parts + 1] = key .. "=" .. tostring(value)
end
end


local function flatten_selected_parts(entry, keys)
local function flatten_parts(entry)
if type(entry) ~= "table" or type(keys) ~= "table" then
if type(entry) ~= "table" then
return {}
return {}
end
end


local parts = {}
local parts = {}
local seen = {}


for i = 1, #keys do
local function walk(tbl, prefix)
local key = keys[i]
local keys = {}
if type(key) == "string" and key ~= "" and not seen[key] then
for k in pairs(tbl) do
seen[key] = true
keys[#keys + 1] = k
append_flattened_part(parts, key, navigate_path(entry, key))
end
end
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
local ok, json = pcall(mw.text.jsonEncode, v)
if ok and json then
parts[#parts + 1] = key .. "=<nowiki>" .. json .. "</nowiki>"
end
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
return parts
end
end


local function resolve_template_path(tplPath)
local function flatten_entry(entry)
local templatePath = tplPath
local parts = flatten_parts(entry)
local project = JsonPaths.project()
if #parts == 0 then
if project ~= nil and project ~= "" then
return ""
templatePath = tplPath .. "/" .. project
templatePath = "{{#ifexist:Шаблон:" .. templatePath .. "|" .. templatePath .. "|" .. tplPath .. "}}"
end
end
 
return table.concat(parts, "|")
return templatePath
end
end


local function split_template_spec(tplPath, tplArgs)
local function append_flattened_part(parts, key, value)
tplPath = mw.text.unstripNoWiki(tplPath or "")
if value == nil then
tplArgs = mw.text.unstripNoWiki(tplArgs or "")
return
if tplArgs ~= "" and string.sub(tplArgs, 1, 1) == "|" then
tplArgs = string.sub(tplArgs, 2)
end
end


if tplArgs == "" then
if type(value) == "table" then
local pipePos = string.find(tplPath, "|", 1, true)
if next(value) == nil then
if pipePos then
return
tplArgs = mw.text.unstripNoWiki(string.sub(tplPath, pipePos + 1))
end
if tplArgs ~= "" and string.sub(tplArgs, 1, 1) == "|" then
 
tplArgs = string.sub(tplArgs, 2)
local ok, json = pcall(mw.text.jsonEncode, value)
end
if ok and json then
tplPath = string.sub(tplPath, 1, pipePos - 1)
parts[#parts + 1] = key .. "=<nowiki>" .. json .. "</nowiki>"
end
end
return
end
end


return tplPath, tplArgs
parts[#parts + 1] = key .. "=" .. tostring(value)
end
end


local function build_tpl(id, pagePath, tplPath, data, tplArgs, preEntry)
local function flatten_selected_parts(entry, keys)
if id == "" or pagePath == "" or tplPath == "" then
if type(entry) ~= "table" or type(keys) ~= "table" then
return ""
return {}
end
end


local moduleName = JsonPaths.get(pagePath)
local parts = {}
local entry = preEntry
local seen = {}
if not entry then
 
if data then
for i = 1, #keys do
entry = resolve_entry(data, id)
local key = keys[i]
else
if type(key) == "string" and key ~= "" and not seen[key] then
entry = load_entry_data(moduleName, id)
seen[key] = true
end
append_flattened_part(parts, key, navigate_path(entry, key))
end
end
if not entry then
return ""
end
end
local extra = flatten_entry(entry)
local extraTplArgs = tplArgs or ""


local templatePath = resolve_template_path(tplPath)
return parts
end


local tplStr = "{{Шаблон:" .. templatePath
local function resolve_template_path(tplPath)
if extraTplArgs ~= "" then
local templatePath = tplPath
tplStr = tplStr .. "|" .. extraTplArgs
local project = JsonPaths.project()
if project ~= nil and project ~= "" then
templatePath = tplPath .. "/" .. project
templatePath = "{{#ifexist:Шаблон:" .. templatePath .. "|" .. templatePath .. "|" .. tplPath .. "}}"
end
end
tplStr = tplStr .. "|id=" .. tostring(id)
if extra ~= "" then
tplStr = tplStr .. "|" .. extra
end
tplStr = tplStr .. "}}"


return tplStr
return templatePath
end
end


function p.findInGenerator(frame)
local function split_template_spec(tplPath, tplArgs)
local args = frame.args or {}
tplPath = mw.text.unstripNoWiki(tplPath or "")
local searchId = args[1] or ""
tplArgs = mw.text.unstripNoWiki(tplArgs or "")
local kind = (args[2] or ""):lower()
if tplArgs ~= "" and string.sub(tplArgs, 1, 1) == "|" then
local fieldId = args[3] or ""
tplArgs = string.sub(tplArgs, 2)
end


if searchId == "" or fieldId == "" then
if tplArgs == "" then
return ""
local pipePos = string.find(tplPath, "|", 1, true)
end
if pipePos then
if kind ~= "prototype" and kind ~= "component" then
tplArgs = mw.text.unstripNoWiki(string.sub(tplPath, pipePos + 1))
return ""
if tplArgs ~= "" and string.sub(tplArgs, 1, 1) == "|" then
tplArgs = string.sub(tplArgs, 2)
end
tplPath = string.sub(tplPath, 1, pipePos - 1)
end
end
end


local storeName = (kind == "prototype") and "prototype_store.json" or "component_store.json"
return tplPath, tplArgs
local moduleName = JsonPaths.get(storeName)
end
local data = load_cached_data(moduleName)
 
if not data then
local function build_tpl(id, pagePath, tplPath, data, tplArgs, preEntry, strictExact)
if id == "" or pagePath == "" or tplPath == "" then
return ""
return ""
end
end


local entry = data[searchId]
local entry
if type(entry) ~= "table" then
if strictExact then
return ""
entry = load_entry(id, pagePath, data, false)
else
entry = preEntry or load_entry(id, pagePath, data)
end
end


local value = get_field_loose(entry, fieldId)
if entry == nil then
if value == nil then
return ""
return ""
end
end


local out = {}
local extra = flatten_entry(entry)
local t = type(value)
local extraTplArgs = tplArgs or ""
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)
local templatePath = resolve_template_path(tplPath)
end


function p.flattenField(frame)
local tplStr = "{{Шаблон:" .. templatePath
local args = frame.args or {}
if extraTplArgs ~= "" then
local id = args[1] or ""
tplStr = tplStr .. "|" .. extraTplArgs
local pagePath = args[2] or ""
end
if id == "" or pagePath == "" then
tplStr = tplStr .. "|id=" .. tostring(id)
return ""
if extra ~= "" then
tplStr = tplStr .. "|" .. extra
end
end
tplStr = tplStr .. "}}"


local moduleName = JsonPaths.get(pagePath)
return tplStr
local entry = load_entry_data(moduleName, id) or {}
return flatten_entry(entry)
end
end


function p.flattenFieldSelective(frame)
function p.findInGenerator(frame)
local args = frame.args or {}
local args = frame.args or {}
local id = args[1] or ""
local searchId = args[1] or ""
local pagePath = args[2] or ""
local kind = (args[2] or ""):lower()
local keysJson = mw.text.unstripNoWiki(args[3] or args.keys or "")
local fieldId = p.ucfirst(args[3] or "")
if id == "" or pagePath == "" or keysJson == "" then
 
return ""
if searchId == "" or fieldId == "" then
return ""
end
end
 
if kind ~= "prototype" and kind ~= "component" then
local okKeys, keys = pcall(mw.text.jsonDecode, keysJson)
if not okKeys or type(keys) ~= "table" or #keys == 0 then
return ""
return ""
end
end


local moduleName = JsonPaths.get(pagePath)
local storeName = (kind == "prototype") and "prototype_store.json" or "component_store.json"
local data = load_cached_data(moduleName)
local data = p.loadCachedData(storeName)
if not data then
if not data then
return ""
return ""
end
end


local entry = resolve_entry(data, id) or {}
local entry = data[searchId]
local parts = flatten_selected_parts(entry, keys)
if type(entry) ~= "table" then
if #parts == 0 then
return ""
return ""
end
end


return table.concat(parts, "|")
local value = get_field_loose(entry, fieldId)
end
if value == nil then
 
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 ""
return ""
end
end


local moduleName = JsonPaths.get(pagePath)
local out = {}
local data = load_cached_data(moduleName)
local t = type(value)
if not data then
if t == "table" then
return ""
for _, v in ipairs(value) do
out[#out + 1] = v
end
else
out[1] = value
end
end


local entry = resolve_entry(data, id)
return mw.text.jsonEncode(out)
if entry == nil then
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 ""
return ""
end
end


if keyPath == "" then
local entry = load_entry(id, pagePath) or {}
return format_value(entry)
return flatten_entry(entry)
end
 
return format_value(navigate_path(entry, keyPath))
end
end


local function collect_by_parsed_path(tbl, parsedPath, pos, out)
function p.flattenFieldSelective(frame)
if pos > #parsedPath then
local args = frame.args or {}
out[#out + 1] = tbl
local id = args[1] or ""
return
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
end


if type(tbl) ~= "table" then
local okKeys, keys = pcall(mw.text.jsonDecode, keysJson)
return
if not okKeys or type(keys) ~= "table" or #keys == 0 then
return ""
end
end


local token = parsedPath[pos]
local entry = load_entry(id, pagePath) or {}
local key = token[1]
local parts = flatten_selected_parts(entry, keys)
local idx = token[2]
if #parts == 0 then
return ""
end


if key == "*" then
return table.concat(parts, "|")
for _, child in pairs(tbl) do
end
local nextCur = child


if idx then
function p.get(frame)
if type(nextCur) ~= "table" then
local args = getArgs(frame, { removeBlanks = false })
nextCur = nil
local id = args[1] or ""
else
local pagePath = args[2] or ""
nextCur = nextCur[idx]
local keyPath = args[3] or ""
end
end


collect_by_parsed_path(nextCur, parsedPath, pos + 1, out)
if pagePath == "" then
end
return ""
return
end
end


local nextCur
local entry = load_entry(id, pagePath)
 
if entry == nil then
if key and key ~= "" then
return ""
nextCur = tbl[key]
if nextCur == nil then nextCur = tbl["!type:" .. key] end
else
nextCur = tbl
end
end


if idx then
if keyPath == "" then
if type(nextCur) ~= "table" then
return format_value(entry)
return
end
nextCur = nextCur[idx]
end
end


collect_by_parsed_path(nextCur, parsedPath, pos + 1, out)
return format_value(navigate_path(entry, keyPath))
end
end


local function get_by_parsed_path_multi(tbl, parsedPath)
local function collect_by_parsed_path(tbl, parsedPath, pos, out)
local out = {}
if pos > #parsedPath then
collect_by_parsed_path(tbl, parsedPath, 1, out)
out[#out + 1] = tbl
return out
return
end
end


local function entry_matches_path(entry, parsedPath, searchValue, searchType)
if type(tbl) ~= "table" then
local values = get_by_parsed_path_multi(entry, parsedPath)
return
local target = tostring(searchValue)
end


for _, v in ipairs(values) do
local token = parsedPath[pos]
if searchType == "key" then
local key = token[1]
if type(v) == "table" and v[target] ~= nil then
local idx = token[2]
return true
 
end
if key == "*" then
else
for _, child in pairs(tbl) do
if contains_target(v, target) then
local nextCur = child
return true
 
if idx then
if type(nextCur) ~= "table" then
nextCur = nil
else
nextCur = nextCur[idx]
end
end
end
collect_by_parsed_path(nextCur, parsedPath, pos + 1, out)
end
end
return
end
end


return false
local nextCur
end


local function entry_has_any_nonempty_path(entry, parsedPath)
if key and key ~= "" then
local values = get_by_parsed_path_multi(entry, parsedPath)
nextCur = tbl[key]
if nextCur == nil then nextCur = tbl["!type:" .. key] end
else
nextCur = tbl
end


for _, v in ipairs(values) do
if idx then
if is_nonempty_value(v) then
if type(nextCur) ~= "table" then
return true
return
end
end
nextCur = nextCur[idx]
end
end


return false
collect_by_parsed_path(nextCur, parsedPath, pos + 1, out)
end
end


local function split_csv_list(text)
local function get_by_parsed_path_multi(tbl, parsedPath)
text = mw.text.unstripNoWiki(text or "")
local out = {}
local out = {}
collect_by_parsed_path(tbl, parsedPath, 1, out)
return out
end


for part in string.gmatch(text, "([^,]+)") do
local function entry_matches_path(entry, parsedPath, searchValue, searchType)
part = mw.text.trim(part)
local values = get_by_parsed_path_multi(entry, parsedPath)
if part ~= "" then
local target = tostring(searchValue)
out[#out + 1] = part
 
for _, v in ipairs(values) do
if searchType == "key" then
if type(v) == "table" and v[target] ~= nil then
return true
end
else
if contains_target(v, target) then
return true
end
end
end
end
end


return out
return false
end
end


function p.searchId(frame)
local function entry_has_any_nonempty_path(entry, parsedPath)
local args = getArgs(frame, { removeBlanks = false })
local values = get_by_parsed_path_multi(entry, parsedPath)
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
for _, v in ipairs(values) do
return ""
if is_nonempty_value(v) then
end
return true
if searchType == "" then
end
searchType = "value"
end
end


local moduleName = JsonPaths.get(pagePath)
return false
local data = load_cached_data(moduleName)
end
if not data then
 
return "[]"
local function split_csv_list(text)
end
text = mw.text.unstripNoWiki(text or "")
local out = {}


local parsedPath = parse_path(keyPath)
for part in string.gmatch(text, "([^,]+)") do
if not parsedPath then
part = mw.text.trim(part)
return ""
if part ~= "" then
out[#out + 1] = part
end
end
end


local ids = collect_id_keys(data)
return out
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 data = p.loadCachedData(pagePath)
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
if #ids == 0 then
return ""
return ""
Строка 730: Строка 777:


for _, idKey in ipairs(ids) do
for _, idKey in ipairs(ids) do
local entry = resolve_entry(data, idKey)
local entry = resolve_entry_from_data(data, idKey)
if type(entry) == "table" and entry_matches_path(entry, parsedPath, target, searchType) then
if type(entry) == "table" and entry_matches_path(entry, parsedPath, target, searchType) then
matches[#matches + 1] = idKey
matches[#matches + 1] = idKey
Строка 746: Строка 793:


return ""
return ""
end
local function collect_ids_from_entry(entry)
local out = {}
local seen = {}
local function add(v)
if v == nil then
return
end
local s = tostring(v)
if s ~= "" and not seen[s] then
seen[s] = true
out[#out + 1] = s
end
end
if type(entry) == "table" then
if is_array(entry) then
for _, v in ipairs(entry) do
add(v)
end
else
for _, v in pairs(entry) do
if type(v) == "table" and is_array(v) then
for _, item in ipairs(v) do
add(item)
end
elseif type(v) ~= "table" then
add(v)
end
end
end
else
add(entry)
end
return out
end
end


Строка 754: Строка 839:


if mode ~= "component" and mode ~= "tag" then
if mode ~= "component" and mode ~= "tag" then
return "[]"
return ""
end
end


local targets = split_csv_list(rawList)
local targets = split_csv_list(rawList)
if #targets == 0 then
if #targets == 0 then
return "[]"
return ""
end
end


local pagePath
local pagePath = (mode == "component") and "component.json" or "tag.json"
if mode == "component" then
local data = p.loadCachedData(pagePath)
pagePath = "component.json"
if not data then
else
return ""
pagePath = "component/tag.json"
end
end


local moduleName = JsonPaths.get(pagePath)
local matches = {}
local data = load_cached_data(moduleName)
local seen = {}
if not data or type(data) ~= "table" then
 
return "[]"
local function add_id(id)
local s = tostring(id)
if s ~= "" and not seen[s] then
seen[s] = true
matches[#matches + 1] = s
end
end
end


local ids = collect_id_keys(data)
for _, key in ipairs(targets) do
if #ids == 0 then
local entry = resolve_entry_from_data(data, key)
return "[]"
if entry ~= nil then
local ids = collect_ids_from_entry(entry)
for _, id in ipairs(ids) do
add_id(id)
end
end
end
end


table.sort(ids, function(a, b)
table.sort(matches, function(a, b)
return tostring(a) < tostring(b)
return tostring(a) < tostring(b)
end)
end)


local function entry_has_all_targets_component(entry, wanted)
local ok, json = pcall(mw.text.jsonEncode, matches)
if type(entry) ~= "table" then
if ok and json then
return false
return json
end
end


local set = {}
return ""
for _, v in ipairs(entry) do
end
set[tostring(v)] = true
end


for i = 1, #wanted do
function p.searchIdTpl(frame)
if not set[wanted[i]] then
local args = getArgs(frame, { removeBlanks = false })
return false
local searchValue = args[1] or ""
end
local pagePath = args[2] or ""
end
local keyPath = args[3] or ""
 
local tplPath = mw.text.unstripNoWiki(args[4] or "")
return true
local tplArgs = args.tplArgs or args.templateArgs or ""
local searchType = (args.searchType or ""):lower()
 
if searchType == "" then
searchType = "value"
end
end


local function entry_has_all_targets_tag(entry, wanted)
if searchType == "path" then
if type(entry) ~= "table" then
searchValue = ""
return false
pagePath = args[1] or ""
end
keyPath = args[2] or ""
tplPath = mw.text.unstripNoWiki(args[3] or "")
tplArgs = args[4] or args.tplArgs or args.templateArgs or ""
end


local tags = entry.tags or entry.tag
tplPath, tplArgs = split_template_spec(tplPath, tplArgs)
if type(tags) ~= "table" then
return false
end


local set = {}
if pagePath == "" or keyPath == "" or tplPath == "" then
if is_array(tags) then
return ""
for _, v in ipairs(tags) do
end
set[tostring(v)] = true
if searchType ~= "path" and searchValue == "" then
end
return ""
else
end
for _, v in pairs(tags) do
 
set[tostring(v)] = true
local data = p.loadCachedData(pagePath)
end
if not data then
end
return ""
end


for i = 1, #wanted do
local parsedPath = parse_path(keyPath)
if not set[wanted[i]] then
if not parsedPath then
return false
return ""
end
end
end


return true
local ids = collect_id_keys(data)
if #ids == 0 then
return ""
end
end


local matches = {}
local matches = {}
local entryCache = {}


for _, idKey in ipairs(ids) do
if searchType == "path" then
local entry = resolve_entry(data, idKey)
for _, idKey in ipairs(ids) do
if mode == "component" then
local entry = resolve_entry_from_data(data, idKey)
if entry_has_all_targets_component(entry, targets) then
entryCache[idKey] = entry
if type(entry) == "table" and entry_has_any_nonempty_path(entry, parsedPath) then
matches[#matches + 1] = idKey
matches[#matches + 1] = idKey
end
end
else
end
if entry_has_all_targets_tag(entry, targets) then
else
local target = tostring(searchValue)
for _, idKey in ipairs(ids) do
local entry = resolve_entry_from_data(data, idKey)
entryCache[idKey] = entry
if type(entry) == "table" and entry_matches_path(entry, parsedPath, target, searchType) then
matches[#matches + 1] = idKey
matches[#matches + 1] = idKey
end
end
Строка 848: Строка 953:
end
end


local ok, json = pcall(mw.text.jsonEncode, matches)
if #matches == 0 then
if ok and json then
return ""
return json
end
 
local out = {}
for _, idKey in ipairs(matches) do
local tpl = build_tpl(idKey, pagePath, tplPath, data, tplArgs, entryCache[idKey], true)
if tpl ~= "" then
out[#out + 1] = tpl
end
end
 
if #out == 0 then
return ""
end
end


return "[]"
local result = table.concat(out, " ")
return preprocess_or_return(frame, result)
end
end


function p.searchIdTpl(frame)
function p.getTpl(frame)
local args = getArgs(frame, { removeBlanks = false })
local args = getArgs(frame, { removeBlanks = false })
local searchValue = args[1] or ""
local id = args[1] or ""
local pagePath = args[2] or ""
local pagePath = args[2] or ""
local keyPath = args[3] or ""
local tplPath = mw.text.unstripNoWiki(args[3] or "")
local tplPath = mw.text.unstripNoWiki(args[4] or "")
local tplArgs = args[4] or args.tplArgs or args.templateArgs 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)
tplPath, tplArgs = split_template_spec(tplPath, tplArgs)


if pagePath == "" or keyPath == "" or tplPath == "" then
if id == "" or pagePath == "" or tplPath == "" then
return ""
end
if searchType ~= "path" and searchValue == "" then
return ""
return ""
end
end


local moduleName = JsonPaths.get(pagePath)
local data = frame.data
local data = load_cached_data(moduleName)
local tplStr = build_tpl(id, pagePath, tplPath, data, tplArgs, nil, true)
if not data then
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 = p.ucfirst(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 ""
return ""
end
end
 
if kind ~= "prototype" and kind ~= "component" then
local parsedPath = parse_path(keyPath)
if not parsedPath then
return ""
return ""
end
end


local ids = collect_id_keys(data)
local dir = (kind == "prototype") and "prototype/" or "component/"
if #ids == 0 then
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 ""
return ""
end
end


local matches = {}
local data = p.loadCachedData(pagePath)
local entryCache = {}
if not data then
return ""
end


if searchType == "path" then
local out = {}
for _, idKey in ipairs(ids) do
for _, id in ipairs(ids) do
local entry = resolve_entry(data, idKey)
local entry = resolve_entry_from_data(data, id)
entryCache[idKey] = entry
if entry ~= nil then
if type(entry) == "table" and entry_has_any_nonempty_path(entry, parsedPath) then
local tpl = build_tpl(id, pagePath, tplPath, data, tplArgs, entry, true)
matches[#matches + 1] = idKey
if tpl ~= "" then
end
out[#out + 1] = tpl
end
else
local target = tostring(searchValue)
for _, idKey in ipairs(ids) do
local entry = resolve_entry(data, idKey)
entryCache[idKey] = entry
if type(entry) == "table" and entry_matches_path(entry, parsedPath, target, searchType) then
matches[#matches + 1] = idKey
end
end
end
end
end
end


if #matches == 0 then
local result = table.concat(out, " ")
return ""
return preprocess_or_return(frame, result)
end
end


local out = {}
function p.flattenParams(entry)
for _, idKey in ipairs(matches) do
return flatten_parts(entry)
local tpl = build_tpl(idKey, pagePath, tplPath, data, tplArgs, entryCache[idKey])
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
end


function p.getTpl(frame)
function p.searchStore(frame)
local args = getArgs(frame, { removeBlanks = false })
local args = getArgs(frame, { removeBlanks = false })
local id = args[1] or ""
local searchId = args[1] or ""
local pagePath = args[2] or ""
local kind = (args[2] or ""):lower()
local tplPath = mw.text.unstripNoWiki(args[3] or "")
local generatorId = p.ucfirst(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
if searchId == "" or generatorId == "" then
return ""
end
 
local moduleName = JsonPaths.get(pagePath)
local data = frame.data
local entry
if data then
entry = resolve_entry(data, id)
else
entry = load_entry_data(moduleName, id)
end
if not entry then
return ""
end
 
local tplStr = build_tpl(id, pagePath, tplPath, data, tplArgs, entry)
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 ""
return ""
end
end
Строка 987: Строка 1051:
return ""
return ""
end
end
local dir = (kind == "prototype") and "prototype/" or "component/"
local pagePath = dir .. generatorId .. ".json"


local idsJson = p.findInGenerator({ args = { searchId, kind, generatorId } })
local idsJson = p.findInGenerator({ args = { searchId, kind, generatorId } })
Строка 997: Строка 1058:
end
end


local moduleName = JsonPaths.get(pagePath)
local okOut, outJson = pcall(mw.text.jsonEncode, ids)
local data = load_cached_data(moduleName)
if okOut and outJson then
if not data then
return outJson
return ""
end
end


local out = {}
return ""
for _, id in ipairs(ids) do
end
local tpl = build_tpl(id, pagePath, tplPath, data, tplArgs)
 
if tpl ~= "" then
function p.loadEntityData(entityId)
out[#out + 1] = tpl
if entityId == "" then
end
return nil
end
end


local result = table.concat(out, " ")
local pagePath = "prototype/Entity/" .. entityId .. ".json"
return preprocess_or_return(frame, result)
return p.loadCachedData(pagePath)
end
end


function p.flattenParams(entry)
function p.entityHasComponent(entity, compName)
return flatten_parts(entry)
if type(entity) ~= "table" or type(entity.components) ~= "table" then
return false
end
 
return entity.components[compName] ~= nil
or entity.components["type:" .. compName] ~= nil
or entity.components["!type:" .. compName] ~= nil
end
end


function p.searchStore(frame)
function p.collectEntityComponents(entity)
local args = getArgs(frame, { removeBlanks = false })
if type(entity) ~= "table" or type(entity.components) ~= "table" then
local searchId = args[1] or ""
return {}
local kind = (args[2] or ""):lower()
end
local generatorId = args[3] or ""
 
local out = {}
local seen = {}
local comps = entity.components


if searchId == "" or generatorId == "" then
if #comps > 0 then
return ""
for _, v in ipairs(comps) do
end
local name = p.normalizeComponentName(v)
if kind ~= "prototype" and kind ~= "component" then
if name and not seen[name] then
return ""
seen[name] = true
out[#out + 1] = name
end
end
else
for k in pairs(comps) do
local name = p.normalizeComponentName(k)
if name and not seen[name] then
seen[name] = true
out[#out + 1] = name
end
end
end
end


local idsJson = p.findInGenerator({ args = { searchId, kind, generatorId } })
table.sort(out)
local ok, ids = pcall(mw.text.jsonDecode, idsJson or "")
return out
if not ok or type(ids) ~= "table" or #ids == 0 then
end
return ""
end


local okOut, outJson = pcall(mw.text.jsonEncode, ids)
function p.hasComp(frame)
if okOut and outJson then
local args = getArgs(frame, { removeBlanks = false })
return outJson
local entityId = args[1] or ""
end
 
return ""
end
 
function p.hasComp(frame)
local args = getArgs(frame, { removeBlanks = false })
local entityId = args[1] or ""
local compName = args[2] or ""
local compName = args[2] or ""


Строка 1055: Строка 1125:
end
end


local moduleName = JsonPaths.get("component.json")
local entity = p.loadEntityData(entityId)
local data = load_cached_data(moduleName)
if not entity then
if not data then
return "false"
return "false"
end
end


if type(data) ~= "table" then
return p.entityHasComponent(entity, compName) and "true" or "false"
return "false"
end
 
local entry = data[entityId]
if type(entry) ~= "table" then
return "false"
end
 
for _, v in ipairs(entry) do
if tostring(v) == compName then
return "true"
end
end
 
return "false"
end
end


Строка 1084: Строка 1138:


if entityId == "" then
if entityId == "" then
return "[]"
return ""
end
end


local moduleName = JsonPaths.get("component.json")
local entity = p.loadEntityData(entityId)
local data = load_cached_data(moduleName)
if not entity then
if not data or type(data) ~= "table" then
return ""
return "[]"
end
 
local entry = data[entityId]
if type(entry) ~= "table" then
return "[]"
end
 
local out = {}
for _, v in ipairs(entry) do
if v ~= nil and v ~= "" then
out[#out + 1] = v
end
end
end


local out = p.collectEntityComponents(entity)
local ok, json = pcall(mw.text.jsonEncode, out)
local ok, json = pcall(mw.text.jsonEncode, out)
if ok and json then
if ok and json then
Строка 1110: Строка 1152:
end
end


return "[]"
return ""
end
end


Строка 1123: Строка 1165:
end
end


local moduleName = JsonPaths.get(pagePath)
local data = p.loadCachedData(pagePath)
local data = load_cached_data(moduleName)
if not data then
if not data then
return ""
return ""
end
end


local idsTable = data.id
local idsTable = data.id or data
if type(idsTable) ~= "table" then
if type(idsTable) ~= "table" then
return ""
return ""
Строка 1174: Строка 1215:
end
end


local moduleName = JsonPaths.get(pagePath)
local data = p.loadCachedData(pagePath)
local data = load_cached_data(moduleName)
if not data then
if not data then
return ""
return ""
end
end


local idsTable = data.id
local idsTable = data.id or data
if type(idsTable) ~= "table" then
if type(idsTable) ~= "table" then
return ""
return ""
Строка 1188: Строка 1228:


for idKey in pairs(idsTable) do
for idKey in pairs(idsTable) do
local tpl = build_tpl(idKey, pagePath, tplPath, data, tplArgs)
local entry = resolve_entry_from_data(data, idKey)
if tpl ~= "" then
if entry ~= nil then
out[#out + 1] = tpl
local tpl = build_tpl(idKey, pagePath, tplPath, data, tplArgs, entry, true)
if tpl ~= "" then
out[#out + 1] = tpl
end
end
end
end
end
Строка 1624: Строка 1667:
end
end


local moduleName = JsonPaths.get(dataPage)
local entry = load_entry(id, dataPage) or {}
local entry = load_entry_data(moduleName, id) or {}
local parts = flatten_selected_parts(entry, paramNames)
local parts = flatten_selected_parts(entry, paramNames)
if #parts == 0 then
if #parts == 0 then