Модуль:GetField: различия между версиями
Pok (обсуждение | вклад) м Замена текста — «getId» на «searchId» |
Pok (обсуждение | вклад) мНет описания правки |
||
| (не показаны 53 промежуточные версии этого же участника) | |||
| Строка 3: | Строка 3: | ||
local JsonPaths = require('Module:JsonPaths') | local JsonPaths = require('Module:JsonPaths') | ||
local getArgs = require('Module:Arguments').getArgs | local getArgs = require('Module:Arguments').getArgs | ||
local function load_cached_data(moduleName) | local function load_cached_data(moduleName) | ||
| Строка 16: | Строка 12: | ||
end | end | ||
local function | local function parse_path(path) | ||
if not path or path == "" then | |||
return nil | |||
return | |||
end | end | ||
local num = tonumber(part) | local parsed = {} | ||
for part in string.gmatch(path, "([^%.]+)") do | |||
local key, idxStr = string.match(part, "^(.-)%[(%d+)%]$") | |||
if key then | |||
parsed[#parsed + 1] = { key, tonumber(idxStr) } | |||
else | |||
local num = tonumber(part) | |||
if num then | |||
parsed[#parsed + 1] = { nil, num } | |||
else | |||
parsed[#parsed + 1] = { part, nil } | |||
end | |||
end | |||
end | end | ||
return | return parsed | ||
end | end | ||
local function | local function navigate_path(tbl, path) | ||
if not tbl or path == "" then | if not tbl then return nil end | ||
if not path or path == "" then return tbl end | |||
local cur = tbl | local cur = tbl | ||
local start = 1 | |||
local key, idx = | local len = #path | ||
while start <= len do | |||
local dotPos = string.find(path, ".", start, true) | |||
local part | |||
if dotPos then | |||
part = string.sub(path, start, dotPos - 1) | |||
start = dotPos + 1 | |||
else | |||
part = string.sub(path, start) | |||
start = len + 1 | |||
end | |||
if type(cur) ~= "table" then return nil end | |||
local bracketPos = string.find(part, "[", 1, true) | |||
if bracketPos then | |||
local key = string.sub(part, 1, bracketPos - 1) | |||
local idx = tonumber(string.sub(part, bracketPos + 1, #part - 1)) | |||
if key ~= "" then | |||
local val = cur[key] | |||
if val == nil then val = cur["!type:" .. key] end | |||
cur = val | |||
end | end | ||
if idx then | |||
if type(cur) ~= "table" then return nil end | |||
cur = cur[idx] | |||
end | end | ||
cur = | else | ||
local num = tonumber(part) | |||
if num then | |||
cur = cur[num] | |||
else | |||
local val = cur[part] | |||
if val == nil then val = cur["!type:" .. part] end | |||
cur = val | |||
end | end | ||
end | end | ||
if cur == nil then | if cur == nil then return nil end | ||
end | end | ||
| Строка 67: | Строка 89: | ||
local function format_value(v) | local function format_value(v) | ||
if v == nil then | |||
return "null" | return "null" | ||
end | end | ||
| Строка 80: | Строка 97: | ||
return tostring(v) | return tostring(v) | ||
elseif t == "table" then | elseif t == "table" then | ||
local ok, | local ok, json = pcall(mw.text.jsonEncode, v) | ||
if ok and | if ok and json then | ||
return | return json | ||
end | end | ||
return "" | return "" | ||
| Строка 88: | Строка 105: | ||
return tostring(v) | return tostring(v) | ||
end | end | ||
end | end | ||
| Строка 154: | Строка 167: | ||
return merged | return merged | ||
end | end | ||
return | return specific | ||
end | end | ||
end | end | ||
| Строка 161: | Строка 174: | ||
local base = data["default"] | local base = data["default"] | ||
if type(base) == "table" then | if type(base) == "table" then | ||
return | return base | ||
end | end | ||
| Строка 192: | Строка 205: | ||
local function contains_target(v, target) | local function contains_target(v, target) | ||
if type(v) == "string" then | |||
return v == target | |||
end | |||
if type(v) == "table" then | if type(v) == "table" then | ||
if is_array(v) then | if is_array(v) then | ||
| Строка 221: | Строка 237: | ||
end | end | ||
return true | return true | ||
end | end | ||
| Строка 294: | Строка 294: | ||
local parts = {} | local parts = {} | ||
local function walk(tbl, prefix) | local function walk(tbl, prefix) | ||
| Строка 318: | Строка 311: | ||
if type(v) == "table" then | if type(v) == "table" then | ||
if next(v) ~= nil 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 | if is_array(v) then | ||
local first = v[1] | local first = v[1] | ||
| Строка 346: | Строка 342: | ||
end | end | ||
local function build_tpl(id, pagePath, tplPath, data) | 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 .. "=<nowiki>" .. json .. "</nowiki>" | |||
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, navigate_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, preEntry) | |||
if id == "" or pagePath == "" or tplPath == "" then | if id == "" or pagePath == "" or tplPath == "" then | ||
return "" | return "" | ||
end | end | ||
local moduleName = | local moduleName = JsonPaths.get(pagePath) | ||
data = data or load_cached_data(moduleName) | |||
if not data then | if not data then | ||
return "" | return "" | ||
end | end | ||
local entry = resolve_entry(data, id) | local entry = preEntry or resolve_entry(data, id) | ||
local extra = flatten_entry(entry) | local extra = flatten_entry(entry) | ||
local extraTplArgs = tplArgs or "" | |||
local tplStr = "{{" .. | local templatePath = resolve_template_path(tplPath) | ||
local tplStr = "{{Шаблон:" .. templatePath | |||
if extraTplArgs ~= "" then | |||
tplStr = tplStr .. "|" .. extraTplArgs | |||
end | |||
tplStr = tplStr .. "|id=" .. tostring(id) | |||
if extra ~= "" then | if extra ~= "" then | ||
tplStr = tplStr .. "|" .. extra | tplStr = tplStr .. "|" .. extra | ||
| Строка 385: | Строка 457: | ||
local storeName = (kind == "prototype") and "prototype_store.json" or "component_store.json" | local storeName = (kind == "prototype") and "prototype_store.json" or "component_store.json" | ||
local moduleName = | local moduleName = JsonPaths.get(storeName) | ||
local data = load_cached_data(moduleName) | local data = load_cached_data(moduleName) | ||
if not data then | if not data then | ||
| Строка 422: | Строка 494: | ||
end | end | ||
local moduleName = | local moduleName = JsonPaths.get(pagePath) | ||
local data = load_cached_data(moduleName) | local data = load_cached_data(moduleName) | ||
if not data then | if not data then | ||
| Строка 430: | Строка 502: | ||
local entry = resolve_entry(data, id) or {} | local entry = resolve_entry(data, id) or {} | ||
return flatten_entry(entry) | 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 = JsonPaths.get(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 | end | ||
| Строка 442: | Строка 543: | ||
end | end | ||
local moduleName = | local moduleName = JsonPaths.get(pagePath) | ||
local data = load_cached_data(moduleName) | local data = load_cached_data(moduleName) | ||
if not data then | if not data then | ||
| Строка 457: | Строка 558: | ||
end | end | ||
local value = | return format_value(navigate_path(entry, keyPath)) | ||
return | end | ||
local function collect_by_parsed_path(tbl, parsedPath, pos, out) | |||
if pos > #parsedPath then | |||
out[#out + 1] = tbl | |||
return | |||
end | |||
if type(tbl) ~= "table" then | |||
return | |||
end | |||
local token = parsedPath[pos] | |||
local key = token[1] | |||
local idx = token[2] | |||
if key == "*" then | |||
for _, child in pairs(tbl) do | |||
local nextCur = child | |||
if idx then | |||
if type(nextCur) ~= "table" then | |||
nextCur = nil | |||
else | |||
nextCur = nextCur[idx] | |||
end | |||
end | |||
collect_by_parsed_path(nextCur, parsedPath, pos + 1, out) | |||
end | |||
return | |||
end | |||
local nextCur | |||
if key and key ~= "" then | |||
nextCur = tbl[key] | |||
if nextCur == nil then nextCur = tbl["!type:" .. key] end | |||
else | |||
nextCur = tbl | |||
end | |||
if idx then | |||
if type(nextCur) ~= "table" then | |||
return | |||
end | |||
nextCur = nextCur[idx] | |||
end | |||
collect_by_parsed_path(nextCur, parsedPath, pos + 1, out) | |||
end | |||
local function get_by_parsed_path_multi(tbl, parsedPath) | |||
local out = {} | |||
collect_by_parsed_path(tbl, parsedPath, 1, out) | |||
return out | |||
end | |||
local function entry_matches_path(entry, parsedPath, searchValue, searchType) | |||
local values = get_by_parsed_path_multi(entry, parsedPath) | |||
local target = tostring(searchValue) | |||
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 | |||
return false | |||
end | |||
local function entry_has_any_nonempty_path(entry, parsedPath) | |||
local values = get_by_parsed_path_multi(entry, parsedPath) | |||
for _, v in ipairs(values) do | |||
if is_nonempty_value(v) then | |||
return true | |||
end | |||
end | |||
return false | |||
end | |||
local function split_csv_list(text) | |||
text = mw.text.unstripNoWiki(text or "") | |||
local out = {} | |||
for part in string.gmatch(text, "([^,]+)") do | |||
part = mw.text.trim(part) | |||
if part ~= "" then | |||
out[#out + 1] = part | |||
end | |||
end | |||
return out | |||
end | |||
local function add_scalar_values(value, set) | |||
if type(value) == "table" then | |||
if is_array(value) then | |||
for _, item in ipairs(value) do | |||
add_scalar_values(item, set) | |||
end | |||
else | |||
for _, item in pairs(value) do | |||
add_scalar_values(item, set) | |||
end | |||
end | |||
elseif value ~= nil then | |||
set[tostring(value)] = true | |||
end | |||
end | |||
local function table_has_all_values(value, targets) | |||
if type(targets) ~= "table" or #targets == 0 then | |||
return false | |||
end | |||
if type(value) ~= "table" then | |||
return #targets == 1 and tostring(value) == targets[1] | |||
end | |||
local set = {} | |||
add_scalar_values(value, set) | |||
for i = 1, #targets do | |||
if not set[targets[i]] then | |||
return false | |||
end | |||
end | |||
return true | |||
end | end | ||
| Строка 475: | Строка 713: | ||
end | end | ||
local moduleName = | local moduleName = JsonPaths.get(pagePath) | ||
local data = load_cached_data(moduleName) | local data = load_cached_data(moduleName) | ||
if not data then | if not data then | ||
return "[]" | return "[]" | ||
end | |||
local parsedPath = parse_path(keyPath) | |||
if not parsedPath then | |||
return "" | |||
end | end | ||
| Строка 486: | Строка 729: | ||
end | end | ||
local matches | local matches = {} | ||
local target = tostring(searchValue) | |||
for _, idKey in ipairs(ids) do | |||
local entry = resolve_entry(data, idKey) | |||
if type(entry) == "table" and entry_matches_path(entry, parsedPath, target, searchType) then | |||
matches[#matches + 1] = idKey | |||
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.search(frame) | |||
local args = getArgs(frame, { removeBlanks = false }) | |||
local mode = (args[1] or ""):lower() | |||
local rawList = args[2] or "" | |||
if mode ~= "component" and mode ~= "tag" then | |||
return "[]" | |||
end | |||
local targets = split_csv_list(rawList) | |||
if #targets == 0 then | |||
return "[]" | |||
end | |||
local pagePath | |||
if mode == "component" then | |||
pagePath = "component.json" | |||
else | |||
pagePath = "component/tag.json" | |||
end | |||
local moduleName = JsonPaths.get(pagePath) | |||
local data = load_cached_data(moduleName) | |||
if not data or type(data) ~= "table" then | |||
return "[]" | |||
end | |||
local ids = collect_id_keys(data) | |||
if #ids == 0 then | |||
return "[]" | |||
end | |||
table.sort(ids, function(a, b) | |||
return tostring(a) < tostring(b) | |||
end) | |||
local function entry_has_all_targets_component(entry, wanted) | |||
if type(entry) ~= "table" then | |||
return false | |||
end | |||
local set = {} | |||
for _, v in ipairs(entry) do | |||
set[tostring(v)] = true | |||
end | |||
for i = 1, #wanted do | |||
if not set[wanted[i]] then | |||
return false | |||
end | |||
end | |||
return true | |||
end | |||
local function entry_has_all_targets_tag(entry, wanted) | |||
if type(entry) ~= "table" then | |||
return false | |||
end | |||
local tags = entry.tags or entry.tag | |||
if type(tags) ~= "table" then | |||
return false | |||
end | |||
local set = {} | |||
if is_array(tags) then | |||
for _, v in ipairs(tags) do | |||
set[tostring(v)] = true | |||
end | |||
else | |||
for _, v in pairs(tags) do | |||
set[tostring(v)] = true | |||
end | end | ||
end | end | ||
for i = 1, #wanted do | |||
if not set[wanted[i]] then | |||
return false | |||
if | |||
end | end | ||
end | end | ||
return true | |||
end | end | ||
if #matches == | local matches = {} | ||
for _, idKey in ipairs(ids) do | |||
local entry = resolve_entry(data, idKey) | |||
if mode == "component" then | |||
if entry_has_all_targets_component(entry, targets) then | |||
matches[#matches + 1] = idKey | |||
end | |||
else | |||
if entry_has_all_targets_tag(entry, targets) then | |||
matches[#matches + 1] = idKey | |||
end | |||
end | |||
end | end | ||
| Строка 522: | Строка 856: | ||
end | end | ||
return "" | return "[]" | ||
end | end | ||
function p. | function p.searchIdTpl(frame) | ||
local args = getArgs(frame, { removeBlanks = false }) | local args = getArgs(frame, { removeBlanks = false }) | ||
local searchValue = args[1] or "" | local searchValue = args[1] or "" | ||
| Строка 531: | Строка 865: | ||
local keyPath = args[3] or "" | local keyPath = args[3] or "" | ||
local tplPath = mw.text.unstripNoWiki(args[4] or "") | local tplPath = mw.text.unstripNoWiki(args[4] or "") | ||
local tplArgs = args.tplArgs or args.templateArgs or "" | |||
local searchType = (args.searchType or ""):lower() | local searchType = (args.searchType or ""):lower() | ||
| Строка 542: | Строка 877: | ||
keyPath = args[2] or "" | keyPath = args[2] or "" | ||
tplPath = mw.text.unstripNoWiki(args[3] or "") | tplPath = mw.text.unstripNoWiki(args[3] or "") | ||
tplArgs = args[4] or args.tplArgs or args.templateArgs or "" | |||
end | end | ||
tplPath, tplArgs = split_template_spec(tplPath, tplArgs) | |||
if pagePath == "" or keyPath == "" or tplPath == "" then | if pagePath == "" or keyPath == "" or tplPath == "" then | ||
| Строка 551: | Строка 889: | ||
end | end | ||
local moduleName = | local moduleName = JsonPaths.get(pagePath) | ||
local data = load_cached_data(moduleName) | local data = load_cached_data(moduleName) | ||
if not data then | if not data then | ||
return "" | |||
end | |||
local parsedPath = parse_path(keyPath) | |||
if not parsedPath then | |||
return "" | return "" | ||
end | end | ||
| Строка 562: | Строка 905: | ||
end | end | ||
local matches | local matches = {} | ||
local entryCache = {} | |||
if searchType == "path" then | if searchType == "path" then | ||
for _, idKey in ipairs(ids) do | for _, idKey in ipairs(ids) do | ||
local entry = resolve_entry(data, idKey) | local entry = resolve_entry(data, idKey) | ||
entryCache[idKey] = entry | |||
if type(entry) == "table" and entry_has_any_nonempty_path(entry, parsedPath) then | |||
matches[#matches + 1] = idKey | |||
if type(entry) == "table" | |||
end | end | ||
end | end | ||
else | else | ||
local target = tostring(searchValue) | local target = tostring(searchValue) | ||
for _, idKey in ipairs(ids) do | for _, idKey in ipairs(ids) do | ||
local entry = resolve_entry(data, idKey) | local entry = resolve_entry(data, idKey) | ||
if type(entry) == "table" | entryCache[idKey] = entry | ||
if type(entry) == "table" and entry_matches_path(entry, parsedPath, target, searchType) then | |||
matches[#matches + 1] = idKey | |||
end | end | ||
end | end | ||
| Строка 606: | Строка 933: | ||
local out = {} | local out = {} | ||
for _, idKey in ipairs(matches) do | for _, idKey in ipairs(matches) do | ||
local tpl = build_tpl(idKey, pagePath, tplPath, data) | local tpl = build_tpl(idKey, pagePath, tplPath, data, tplArgs, entryCache[idKey]) | ||
if tpl ~= "" then | if tpl ~= "" then | ||
out[#out + 1] = tpl | out[#out + 1] = tpl | ||
| Строка 625: | Строка 952: | ||
local pagePath = args[2] or "" | local pagePath = args[2] or "" | ||
local tplPath = mw.text.unstripNoWiki(args[3] 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 | if id == "" or pagePath == "" or tplPath == "" then | ||
| Строка 630: | Строка 959: | ||
end | end | ||
local moduleName = | local moduleName = JsonPaths.get(pagePath) | ||
local data = frame.data | local data = frame.data | ||
if not data then | if not data then | ||
| Строка 639: | Строка 968: | ||
end | end | ||
local tplStr = build_tpl(id, pagePath, tplPath, data) | local tplStr = build_tpl(id, pagePath, tplPath, data, tplArgs) | ||
return preprocess_or_return(frame, tplStr) | return preprocess_or_return(frame, tplStr) | ||
end | end | ||
function p. | function p.searchStoreTpl(frame) | ||
local args = getArgs(frame, { removeBlanks = false }) | local args = getArgs(frame, { removeBlanks = false }) | ||
local searchId = args[1] or "" | local searchId = args[1] or "" | ||
| Строка 649: | Строка 978: | ||
local generatorId = args[3] or "" | local generatorId = args[3] or "" | ||
local tplPath = mw.text.unstripNoWiki(args[4] 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 | if searchId == "" or generatorId == "" or tplPath == "" then | ||
| Строка 666: | Строка 997: | ||
end | end | ||
local moduleName = | local moduleName = JsonPaths.get(pagePath) | ||
local data = load_cached_data(moduleName) | local data = load_cached_data(moduleName) | ||
if not data then | if not data then | ||
| Строка 674: | Строка 1005: | ||
local out = {} | local out = {} | ||
for _, id in ipairs(ids) do | for _, id in ipairs(ids) do | ||
local tpl = build_tpl(id, pagePath, tplPath, data) | local tpl = build_tpl(id, pagePath, tplPath, data, tplArgs) | ||
if tpl ~= "" then | if tpl ~= "" then | ||
out[#out + 1] = tpl | out[#out + 1] = tpl | ||
| Строка 688: | Строка 1019: | ||
end | end | ||
function p. | function p.searchStore(frame) | ||
local args = getArgs(frame, { removeBlanks = false }) | local args = getArgs(frame, { removeBlanks = false }) | ||
local searchId = args[1] or "" | local searchId = args[1] or "" | ||
| Строка 724: | Строка 1055: | ||
end | end | ||
local moduleName = | local moduleName = JsonPaths.get("component.json") | ||
local data = load_cached_data(moduleName) | local data = load_cached_data(moduleName) | ||
if not data then | if not data then | ||
| Строка 739: | Строка 1070: | ||
end | end | ||
for _, v in ipairs(entry) do | for _, v in ipairs(entry) do | ||
if tostring(v) == | if tostring(v) == compName then | ||
return "true" | return "true" | ||
end | end | ||
| Строка 749: | Строка 1079: | ||
end | end | ||
function p. | function p.getComp(frame) | ||
local args = getArgs(frame, { removeBlanks = false }) | |||
local entityId = args[1] or "" | |||
if entityId == "" then | |||
return "[]" | |||
end | |||
local moduleName = JsonPaths.get("component.json") | |||
local data = load_cached_data(moduleName) | |||
if not data or type(data) ~= "table" then | |||
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 | |||
local ok, json = pcall(mw.text.jsonEncode, out) | |||
if ok and json then | |||
return json | |||
end | |||
return "[]" | |||
end | |||
function p.getAll(frame) | |||
local args = getArgs(frame, { removeBlanks = false }) | local args = getArgs(frame, { removeBlanks = false }) | ||
local pagePath = args[1] or "" | local pagePath = args[1] or "" | ||
| Строка 759: | Строка 1123: | ||
end | end | ||
local moduleName = | local moduleName = JsonPaths.get(pagePath) | ||
local data = load_cached_data(moduleName) | local data = load_cached_data(moduleName) | ||
if not data then | if not data then | ||
| Строка 799: | Строка 1163: | ||
end | end | ||
function p. | function p.getAllTpl(frame) | ||
local args = getArgs(frame, { removeBlanks = false }) | local args = getArgs(frame, { removeBlanks = false }) | ||
local pagePath = args[1] or "" | local pagePath = args[1] or "" | ||
local tplPath = args[2] 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 | if pagePath == "" or tplPath == "" then | ||
| Строка 808: | Строка 1174: | ||
end | end | ||
local moduleName = | local moduleName = JsonPaths.get(pagePath) | ||
local data = load_cached_data(moduleName) | local data = load_cached_data(moduleName) | ||
if not data then | if not data then | ||
| Строка 822: | Строка 1188: | ||
for idKey in pairs(idsTable) do | for idKey in pairs(idsTable) do | ||
local tpl = build_tpl(idKey, pagePath, tplPath, data) | local tpl = build_tpl(idKey, pagePath, tplPath, data, tplArgs) | ||
if tpl ~= "" then | if tpl ~= "" then | ||
out[#out + 1] = tpl | out[#out + 1] = tpl | ||
| Строка 832: | Строка 1198: | ||
local result = table.concat(out, " ") | local result = table.concat(out, " ") | ||
return preprocess_or_return(frame, result) | 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 "<nowiki>" .. json .. "</nowiki>" | |||
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 not aPrimitive | |||
end | |||
return tostring(a) < tostring(b) | |||
end) | |||
return keys[1] | |||
end | |||
local function is_wrapper_block_key(key) | |||
return type(key) == "string" and not key:match("^[%a_][%w_]*$") | |||
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 .. "=" .. json | |||
end | |||
end | |||
local keys = collect_sorted_keys(value, false) | |||
for _, k in ipairs(keys) do | |||
if not (options.nestedKeyMode == "raw" and type(k) == "number") then | |||
local v = value[k] | |||
local key | |||
if prefix 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 .. "=" .. json | |||
end | |||
elseif options.nestedKeyMode == "raw" then | |||
local json = encode_nowiki_json(v) | |||
if json then | |||
parts[#parts + 1] = key .. "=" .. json | |||
end | |||
end | |||
if next(v) ~= nil and not is_array_of_primitives(v) then | |||
local childPrefix | |||
if options.nestedKeyMode == "prefixed" then | |||
childPrefix = key | |||
elseif type(k) == "string" then | |||
childPrefix = key | |||
else | |||
childPrefix = nil | |||
end | |||
append_table_fields(parts, v, options, childPrefix) | |||
end | |||
else | |||
parts[#parts + 1] = key .. "=" .. tostring(v) | |||
end | |||
end | |||
end | |||
end | end | ||
| Строка 838: | Строка 1333: | ||
local jsonStr = mw.text.unstripNoWiki(args[1] or args.json or "") | local jsonStr = mw.text.unstripNoWiki(args[1] or args.json or "") | ||
local tplPath = mw.text.unstripNoWiki(args[2] or args.template 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 | if jsonStr == "" or tplPath == "" then | ||
return "" | return "" | ||
| Строка 847: | Строка 1345: | ||
end | end | ||
local | local calls = {} | ||
local nestedOptions = { | |||
includeJsonAtPrefix = true, | |||
nestedKeyMode = "prefixed", | |||
skipPrimitiveRoot = false, | |||
} | |||
local rawTypeOptions = { | |||
includeJsonAtPrefix = false, | |||
nestedKeyMode = "raw", | |||
skipPrimitiveRoot = true, | |||
} | |||
local function is_object_map(tbl) | |||
local count = 0 | |||
for k, v in pairs(tbl) do | |||
if type(k) ~= "string" or type(v) ~= "table" then | |||
return false | |||
end | |||
count = count + 1 | |||
end | |||
return count > 1 | |||
end | |||
local firstParamOverride = mw.text.unstripNoWiki(args.first or args.firstParam or args.idKey or "") | |||
local | local function makeCall(obj) | ||
if type(obj) ~= "table" then | |||
return | |||
end | |||
local idKey = firstParamOverride | |||
if | if idKey == "" then | ||
idKey = choose_id_key(obj) | |||
end | |||
if not idKey then | |||
return | return | ||
end | end | ||
local parts = { "{{" .. tplPath | local parts = { "{{Шаблон:" .. resolve_template_path(tplPath) } | ||
if type( | if tplArgs ~= "" then | ||
parts[#parts + 1] = tplArgs | |||
end | |||
parts[#parts + 1] = | 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 is_wrapper_block_key(k) then | |||
if type(v) == "table" then | |||
local json = encode_nowiki_json(v) | |||
if json then | |||
parts[#parts + 1] = "value=" .. json | |||
end | |||
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=" .. json | |||
end | |||
end | |||
if next(v) ~= nil then | |||
append_table_fields(parts, v, nestedOptions, k) | |||
append_table_fields(parts, v, rawTypeOptions, nil) | |||
end | |||
elseif v ~= nil then | |||
parts[#parts + 1] = "value=" .. tostring(v) | |||
parts[#parts + 1] = k .. "=" .. tostring(v) | |||
end | end | ||
else | else | ||
if type(v) == "table" then | |||
if v ~= nil then | if next(v) ~= nil then | ||
parts | append_table_fields(parts, v, nestedOptions, k) | ||
end | end | ||
elseif v ~= nil then | |||
parts[#parts + 1] = k .. "=" .. tostring(v) | |||
end | end | ||
end | end | ||
end | end | ||
| Строка 882: | Строка 1440: | ||
for _, item in ipairs(data) do | for _, item in ipairs(data) do | ||
if type(item) == "table" then | if type(item) == "table" then | ||
makeCall(item) | |||
elseif item ~= nil and item ~= "" then | |||
makeCall({ [item] = {} }) | |||
end | end | ||
end | |||
elseif is_object_map(data) then | |||
local keys = collect_sorted_keys(data, true) | |||
for _, k in ipairs(keys) do | |||
makeCall({ [k] = data[k] }) | |||
end | end | ||
else | else | ||
makeCall(data) | |||
end | end | ||
| Строка 897: | Строка 1458: | ||
end | end | ||
return frame:preprocess(table.concat(calls, " ")) | |||
end | end | ||
| Строка 913: | Строка 1473: | ||
end | end | ||
local outputType = (args.type or "list" | local outputType = (args.type or "list") | ||
local bullet = mw.text.unstripNoWiki(args.prefix or "* ") | local bullet = mw.text.unstripNoWiki(args.prefix or "* ") | ||
local sep = mw.text.unstripNoWiki(args.sep or ": ") | local sep = mw.text.unstripNoWiki(args.sep or ": ") | ||
if outputType == "none" then | if outputType == "none" then | ||
bullet = "" | bullet = "" | ||
sep = "" | sep = "" | ||
elseif outputType == "revertList" then | |||
sep = mw.text.unstripNoWiki(args.sep or " ") | |||
end | end | ||
| Строка 929: | Строка 1492: | ||
local pairPattern = mw.text.unstripNoWiki(args.pattern or "(.*)") | local pairPattern = mw.text.unstripNoWiki(args.pattern or "(.*)") | ||
local pairReplace = mw.text.unstripNoWiki(args.replace or "\\1") | local pairReplace = mw.text.unstripNoWiki(args.replace or "\\1") | ||
local maxItems = tonumber(args.max or args.limit or args.max_count or args.maxCount or "") | |||
if maxItems ~= nil then | |||
maxItems = math.floor(maxItems) | |||
if maxItems < 0 then | |||
maxItems = nil | |||
end | |||
end | |||
local out = {} | local out = {} | ||
if is_array(data) then | if is_array(data) then | ||
local processed = 0 | |||
for _, v in ipairs(data) do | for _, v in ipairs(data) do | ||
processed = processed + 1 | |||
if maxItems ~= nil and processed > maxItems then | |||
break | |||
end | |||
local text = "" | local text = "" | ||
| Строка 976: | Строка 1554: | ||
return tostring(a) < tostring(b) | return tostring(a) < tostring(b) | ||
end) | end) | ||
local processed = 0 | |||
for _, k in ipairs(keys) do | for _, k in ipairs(keys) do | ||
processed = processed + 1 | |||
if maxItems ~= nil and processed > maxItems then | |||
break | |||
end | |||
local v = data[k] | local v = data[k] | ||
local vStr | local vStr | ||
| Строка 1010: | Строка 1595: | ||
if outputType == "enum" then | if outputType == "enum" then | ||
line = vStr .. " " .. keyStr | line = vStr .. " " .. keyStr | ||
elseif outputType == "revertList" then | |||
line = bullet .. vStr .. sep .. keyStr | |||
else | else | ||
line = bullet .. keyStr .. sep .. vStr | line = bullet .. keyStr .. sep .. vStr | ||
| Строка 1025: | Строка 1612: | ||
if outputType == "enum" then | if outputType == "enum" then | ||
return frame:preprocess(table.concat(out, ", ")) | return frame:preprocess(table.concat(out, ", ")) | ||
elseif outputType == "list" then | elseif outputType == "list" or outputType == "revertList" then | ||
return frame:preprocess(table.concat(out, "\n")) | return frame:preprocess(table.concat(out, "\n")) | ||
else | else | ||
return frame:preprocess(table.concat(out, "")) | return frame:preprocess(table.concat(out, " ")) | ||
end | |||
end | |||
function p.flattenFieldSelectiveDirect(id, dataPage, paramNames) | |||
if id == "" or dataPage == "" or type(paramNames) ~= "table" or #paramNames == 0 then | |||
return "" | |||
end | end | ||
local moduleName = JsonPaths.get(dataPage) | |||
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, paramNames) | |||
if #parts == 0 then | |||
return "" | |||
end | |||
return table.concat(parts, "|") | |||
end | end | ||
return p | return p | ||