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

Нет описания правки
мНет описания правки
 
(не показаны 24 промежуточные версии этого же участника)
Строка 285: Строка 285:
end
end
return true
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
end


Строка 629: Строка 612:
local value = get_by_path(entry, keyPath)
local value = get_by_path(entry, keyPath)
return format_value(value)
return format_value(value)
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
end


Строка 656: Строка 728:
end
end


local idsTable = type(data.id) == "table" and data.id or data
local ids = collect_id_keys(data)
local ids = collect_id_keys(data)
if #ids == 0 then
if #ids == 0 then
Строка 662: Строка 733:
end
end


local matches
local matches = {}
if searchType == "key" then
local target = tostring(searchValue)
local target = tostring(searchValue)
 
matches = {}
for _, idKey in ipairs(ids) do
for _, idKey in ipairs(ids) do
local entry = resolve_entry(data, idKey)
local v = resolve_path_value(data, idKey, parsedPath)
if type(entry) == "table" and entry_matches_path(entry, parsedPath, target, searchType) then
if type(v) == "table" and v[target] ~= nil then
matches[#matches + 1] = idKey
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
end
end
Строка 745: Строка 801:
end
end


local matches
local matches = {}
 
if searchType == "path" then
if searchType == "path" then
matches = {}
for _, idKey in ipairs(ids) do
for _, idKey in ipairs(ids) do
local v = resolve_entry_path_value(data, idKey, parsedPath)
local entry = resolve_entry(data, idKey)
if is_nonempty_value(v) then
if type(entry) == "table" and entry_has_any_nonempty_path(entry, parsedPath) then
matches[#matches + 1] = idKey
matches[#matches + 1] = idKey
end
end
end
end
elseif searchType == "key" then
else
local target = tostring(searchValue)
local target = tostring(searchValue)
matches = {}
for _, idKey in ipairs(ids) do
for _, idKey in ipairs(ids) do
local v = resolve_path_value(data, idKey, parsedPath)
local entry = resolve_entry(data, idKey)
if type(v) == "table" and v[target] ~= nil then
if type(entry) == "table" and entry_matches_path(entry, parsedPath, target, searchType) then
matches[#matches + 1] = idKey
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
end
Строка 1047: Строка 1058:
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 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 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 .. "=" .. tostring(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 .. "=" .. 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 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


Строка 1066: Строка 1206:


local calls = {}
local calls = {}
local nestedOptions = {
includeJsonAtPrefix = true,
nestedKeyMode = "prefixed",
skipPrimitiveRoot = false,
}
local rawTypeOptions = {
includeJsonAtPrefix = false,
nestedKeyMode = "raw",
skipPrimitiveRoot = true,
}


local function makeTemplatePrefix()
local function is_object_map(tbl)
return "{{Шаблон:" .. resolve_template_path(tplPath)
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
end


local function makeCall(id, obj)
local function makeCall(obj)
if type(id) ~= "string" then
if type(obj) ~= "table" then
return
end
 
local idKey = choose_id_key(obj)
if not idKey then
return
return
end
end


local parts = { makeTemplatePrefix() }
local parts = { "{{Шаблон:" .. resolve_template_path(tplPath) }
 
if tplArgs ~= "" then
if tplArgs ~= "" then
parts[#parts + 1] = tplArgs
parts[#parts + 1] = tplArgs
end
end
parts[#parts + 1] = "id=" .. id


if type(obj) == "table" then
parts[#parts + 1] = tostring(idKey)
local extra = p.flattenParams(obj)
local keys = collect_sorted_keys(obj, true)
for i = 1, #extra do
 
parts[#parts + 1] = extra[i]
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=" .. tostring(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=" .. tostring(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
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
elseif obj ~= nil then
parts[#parts + 1] = "value=" .. tostring(obj)
end
end


Строка 1098: Строка 1295:
for _, item in ipairs(data) do
for _, item in ipairs(data) do
if type(item) == "table" then
if type(item) == "table" then
for k, v in pairs(item) do
makeCall(item)
makeCall(k, v)
elseif item ~= nil and item ~= "" then
end
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
for k, v in pairs(data) do
makeCall(data)
makeCall(k, v)
end
end
end


Строка 1113: Строка 1313:
end
end


local rendered = table.concat(calls, " ")
return frame:preprocess(table.concat(calls, " "))
return frame:preprocess(rendered)
end
end


Строка 1129: Строка 1328:
end
end


local outputType = (args.type or "list"):lower()
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


Строка 1226: Строка 1428:
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
Строка 1241: Строка 1445:
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
end
end


return p
return p