Модуль:Сущность: различия между версиями
Материал из Space Station 14 Вики
Pok (обсуждение | вклад) Нет описания правки |
Pok (обсуждение | вклад) мНет описания правки |
||
| (не показано 13 промежуточных версий этого же участника) | |||
| Строка 49: | Строка 49: | ||
local line = tpl | local line = tpl | ||
local src = sources and sources[i] | local src = sources and sources[i] | ||
line = '< | line = '<div>' .. line .. '</div><div class="ts-Сущность-field">' .. makeSourceLink(src) .. '</div>' | ||
table.insert(parts, '< | table.insert(parts, '<div class="ts-Сущность">' .. line .. '</div>') | ||
end | end | ||
end | end | ||
| Строка 155: | Строка 155: | ||
local foundComponents, foundPrototypes = {}, {} | local foundComponents, foundPrototypes = {}, {} | ||
local compList = componentDefs[id] | local compList = componentDefs[id] | ||
if type(compList) == "table" then for _, v in ipairs(compList) do if type(v) == "string" then foundComponents[v] = true end end end | if type(compList) == "table" then | ||
local | for _, v in ipairs(compList) do | ||
if type( | if type(v) == "string" then | ||
foundComponents[v] = true | |||
end | |||
end | |||
end | |||
local protoEntry = prototypeDefs[id] | |||
if type(protoEntry) == "table" then | |||
for protoName, _ in pairs(protoEntry) do | |||
if type(protoName) == "string" then | |||
foundPrototypes[protoName] = true | |||
end | |||
end | |||
end | |||
for name in string.gmatch(id, "[^,]+") do | for name in string.gmatch(id, "[^,]+") do | ||
local n = trim(name) | local n = trim(name) | ||
if n ~= "" then | if n ~= "" then | ||
if componentDefs[n] ~= nil then foundComponents[n] = true end | if componentDefs[n] ~= nil then foundComponents[n] = true end | ||
if componentDefs[n] == nil and prototypeDefs[n] == nil then foundComponents[n] = true end | if componentDefs[n] == nil and prototypeDefs[n] == nil then foundComponents[n] = true end | ||
end | end | ||
| Строка 239: | Строка 251: | ||
local blocks = renderBlocks(frame, switches, switchConfigs, switchKeyOrder, switchKeyToTemplates, switchKeySources, | local blocks = renderBlocks(frame, switches, switchConfigs, switchKeyOrder, switchKeyToTemplates, switchKeySources, | ||
hasWhitelist) | hasWhitelist) | ||
for _, e in ipairs(errors) do table.insert(out, e) end | |||
for _, b in ipairs(blocks) do table.insert(out, b) end | for _, b in ipairs(blocks) do table.insert(out, b) end | ||
return frame:preprocess(table.concat(out, " | return frame:preprocess(table.concat(out, "\n")) | ||
end | end | ||
| Строка 287: | Строка 299: | ||
for _, b in ipairs(blocks) do table.insert(out, b) end | for _, b in ipairs(blocks) do table.insert(out, b) end | ||
return frame:preprocess(table.concat(out, "\n\n" | return frame:preprocess(table.concat(out, "\n")) | ||
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 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 | |||
function p.jsonList(frame) | |||
local args = getArgs(frame, { removeBlanks = false }) | |||
local jsonStr = mw.text.unstripNoWiki(trim(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 bullet = mw.text.unstripNoWiki(args.prefix or "* ") | |||
local sep = mw.text.unstripNoWiki(args.sep or ": ") | |||
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 out = {} | |||
if is_array(data) then | |||
for _, v in ipairs(data) do | |||
local text = "" | |||
if type(v) == "table" then | |||
if is_array(v) then | |||
if #v == 1 then | |||
text = tostring(v[1]) | |||
elseif #v > 1 then | |||
local parts = {} | |||
for i = 1, #v do | |||
parts[#parts + 1] = tostring(v[i]) | |||
end | |||
text = table.concat(parts, ", ") | |||
end | |||
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) | |||
table.insert(out, bullet .. text) | |||
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 keyStr = tostring(k) | |||
keyStr = apply_pattern(keyStr, keyPattern, keyReplace) | |||
vStr = apply_pattern(vStr, valuePattern, valueReplace) | |||
if vStr ~= "" then | |||
table.insert(out, bullet .. keyStr .. sep .. vStr) | |||
end | |||
end | |||
end | |||
return table.concat(out, "\n") | |||
end | end | ||
return p | return p | ||
Текущая версия от 23:05, 25 февраля 2026
Для документации этого модуля может быть создана страница Модуль:Сущность/doc
local p = {}
local getArgs = require('Module:Arguments').getArgs
local function trim(s)
if not s then return s end
return (s:gsub("^%s*(.-)%s*$", "%1"))
end
local function load_module_data(page)
local baseUser = "IanComradeBot/"
local moduleName = "Module:" .. baseUser .. page .. "/data"
local ok, data = pcall(mw.loadData, moduleName)
if not ok then return nil end
return data
end
local function load_template_content(path)
local title = mw.title.new("Template:" .. path)
if not title then return nil end
local ok, content = pcall(function() return title:getContent() end)
if not ok then return nil end
return content
end
local function lcfirst(s)
if not s or s == "" then return s end
return string.lower(s:sub(1, 1)) .. (s:sub(2) or "")
end
local function makeTplCall(tplPath, sw, key, id, extra)
local tplStr = "{{" .. tplPath .. "|" .. sw .. "|" .. key
tplStr = tplStr .. "|id=" .. tostring(id)
if extra and extra ~= "" then tplStr = tplStr .. "|" .. extra end
tplStr = tplStr .. "}}"
return tplStr
end
local function makeSourceLink(s)
local className = s.name .. (s.kind and (s.kind:sub(1, 1):upper() .. s.kind:sub(2)) or "")
local tplLabel = "Template:" .. s.tplPath
return "[[" .. tplLabel .. "|" .. className .. "]]"
end
local function renderTitleBlock(key, tplCalls, sources, includeHeader)
local parts = {}
if includeHeader then table.insert(parts, "<h2>" .. mw.text.encode(key) .. "</h2>") end
if tplCalls and #tplCalls > 0 then
for i, tpl in ipairs(tplCalls) do
local line = tpl
local src = sources and sources[i]
line = '<div>' .. line .. '</div><div class="ts-Сущность-field">' .. makeSourceLink(src) .. '</div>'
table.insert(parts, '<div class="ts-Сущность">' .. line .. '</div>')
end
end
return table.concat(parts, "\n")
end
local switches = { "card", "title" }
local switchConfigs = {
card = {
wrapper = function(key, tplCalls, sources)
if not tplCalls or #tplCalls == 0 then return "" end
local calls = table.concat(tplCalls, " ")
local srcStr = ""
if sources and #sources > 0 then
local srcParts = {}
for _, s in ipairs(sources) do table.insert(srcParts, makeSourceLink(s)) end
srcStr = " " .. table.concat(srcParts, " ")
end
return "{{карточка/Сущность|" .. mw.text.encode(key) .. "|" .. calls .. srcStr .. "}}"
end
},
title = {
wrapper = function(key, tplCalls, sources)
return renderTitleBlock(key, tplCalls, sources, true)
end
}
}
local function getTemplateMeta(frame, tplPath)
local expanded = frame:expandTemplate {
title = tplPath,
args = { "json" }
}
local ok, data = pcall(mw.text.jsonDecode, expanded)
if ok and type(data) == "table" then
return data
end
return ""
end
local function parseListArg(str)
local res = {}
if not str or str == "" then return res end
for item in string.gmatch(str, "[^,]+") do
local s = trim(item)
if s ~= "" then
local a, b = s:match("^([^_]+)_(.+)$")
if a and b then
res[a] = res[a] or {}
res[a][b] = true
end
end
end
return res
end
local function renderBlocks(frame, switchesTbl, configs, keyOrder, keyToTemplates, keySources, noHeaders)
local outLocal = {}
for _, sw in ipairs(switchesTbl) do
local cfg = configs[sw] or {}
for _, key in ipairs(keyOrder[sw] or {}) do
local entries = keyToTemplates[sw][key] or {}
local tplCalls = {}
local sources = {}
if #entries > 0 then
table.sort(entries, function(a, b)
if a.priority == b.priority then return a.idx < b.idx end
return a.priority > b.priority
end)
for _, e in ipairs(entries) do
table.insert(tplCalls, e.tpl)
table.insert(sources, e.source)
end
end
if noHeaders and sw == "title" then
local outStr = renderTitleBlock(key, tplCalls, sources, false)
if outStr and outStr ~= "" then table.insert(outLocal, outStr) end
else
if cfg.wrapper then
local outStr = cfg.wrapper(key, tplCalls, sources)
if outStr and outStr ~= "" then table.insert(outLocal, outStr) end
end
end
end
end
return outLocal
end
function p.get(frame)
local args = getArgs(frame, { removeBlanks = false })
local id = args[1] or ""
if id == "" then return "" end
local blacklist = parseListArg(args.blacklist or "")
local whitelist = parseListArg(args.whitelist or "")
local hasWhitelist = next(whitelist) ~= nil
local componentDefs = load_module_data("component.json")
local prototypeDefs = load_module_data("prototype.json")
if not componentDefs or not prototypeDefs then return "" end
local foundComponents, foundPrototypes = {}, {}
local compList = componentDefs[id]
if type(compList) == "table" then
for _, v in ipairs(compList) do
if type(v) == "string" then
foundComponents[v] = true
end
end
end
local protoEntry = prototypeDefs[id]
if type(protoEntry) == "table" then
for protoName, _ in pairs(protoEntry) do
if type(protoName) == "string" then
foundPrototypes[protoName] = true
end
end
end
for name in string.gmatch(id, "[^,]+") do
local n = trim(name)
if n ~= "" then
if componentDefs[n] ~= nil then foundComponents[n] = true end
if componentDefs[n] == nil and prototypeDefs[n] == nil then foundComponents[n] = true end
end
end
local switchKeyOrder, switchKeyToTemplates, switchKeySources = {}, {}, {}
for _, sw in ipairs(switches) do
switchKeyOrder[sw] = {}; switchKeyToTemplates[sw] = {}; switchKeySources[sw] = {}
end
local errors = {}
local function processEntity(kind, name)
local pathName = lcfirst(name)
local tplPath = kind .. "/" .. pathName
local content = load_template_content(tplPath)
if not content then
if hasWhitelist then
return
end
local classType = (kind and (kind:sub(1, 1):upper() .. kind:sub(2)) or "")
local className = name .. classType
local tplLabel = "Template:" .. tplPath
table.insert(errors,
"{{сущность/infobox|тип=" .. classType .. "|название=" .. className .. "|ссылка=" .. tplLabel .. "}}")
return
end
local parsed = getTemplateMeta(frame, tplPath)
local ok, dp = pcall(require, "Module:GetField")
for _, sw in ipairs(switches) do
local keys = parsed[sw] or {}
for _, key in ipairs(keys) do
local skip = false
if next(whitelist) ~= nil then
if not (whitelist[sw] and whitelist[sw][key]) then skip = true end
else
if blacklist[sw] and blacklist[sw][key] then skip = true end
end
if not skip then
if not switchKeyToTemplates[sw][key] then
switchKeyToTemplates[sw][key] = {}
table.insert(switchKeyOrder[sw], key)
end
local extra = ""
if ok and dp and dp.flattenField then
local dataPage = tplPath .. ".json"
extra = dp.flattenField({ args = { id, dataPage } })
end
local tplStr = makeTplCall(tplPath, sw, key, id, extra)
local priority = 1
if parsed and parsed.priority ~= nil then
if type(parsed.priority) == "number" then
priority = parsed.priority
else
local pnum = tonumber(parsed.priority)
if pnum then priority = pnum end
end
end
local entry = {
tpl = tplStr,
source = { kind = kind, name = name, pathName = pathName, tplPath = tplPath },
priority = priority,
idx = #switchKeyToTemplates[sw][key] + 1
}
table.insert(switchKeyToTemplates[sw][key], entry)
end
end
end
end
local items = {}
for compName, _ in pairs(foundComponents) do table.insert(items, { kind = "component", name = compName }) end
for protoName, _ in pairs(foundPrototypes) do table.insert(items, { kind = "prototype", name = protoName }) end
for _, it in ipairs(items) do processEntity(it.kind, it.name) end
local out = {}
local blocks = renderBlocks(frame, switches, switchConfigs, switchKeyOrder, switchKeyToTemplates, switchKeySources,
hasWhitelist)
for _, e in ipairs(errors) do table.insert(out, e) end
for _, b in ipairs(blocks) do table.insert(out, b) end
return frame:preprocess(table.concat(out, "\n"))
end
function p.preview(frame)
local args = getArgs(frame, { removeBlanks = false })
local tplPath = args[1] or ""
if tplPath == "" then return "" end
local content = load_template_content(tplPath)
if not content then
return ""
end
local parsed = getTemplateMeta(frame, tplPath) or {}
local switchKeyOrder, switchKeyToTemplates, switchKeySources = {}, {}, {}
for _, sw in ipairs(switches) do
switchKeyOrder[sw] = {}; switchKeyToTemplates[sw] = {}; switchKeySources[sw] = {}
end
for _, sw in ipairs(switches) do
local keys = parsed[sw] or {}
for idx, key in ipairs(keys) do
if not switchKeyToTemplates[sw][key] then
switchKeyToTemplates[sw][key] = {}
table.insert(switchKeyOrder[sw], key)
end
local tplStr = makeTplCall(tplPath, sw, key, "")
local entry = {
tpl = tplStr,
source = { kind = "", name = tplPath, pathName = tplPath, tplPath = tplPath },
priority = 1,
idx = #switchKeyToTemplates[sw][key] + 1
}
table.insert(switchKeyToTemplates[sw][key], entry)
end
end
local whitelist = parseListArg(args.whitelist or "")
local hasWhitelist = next(whitelist) ~= nil
local out = {}
local blocks = renderBlocks(frame, switches, switchConfigs, switchKeyOrder, switchKeyToTemplates, switchKeySources,
hasWhitelist)
for _, b in ipairs(blocks) do table.insert(out, b) end
return frame:preprocess(table.concat(out, "\n"))
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 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
function p.jsonList(frame)
local args = getArgs(frame, { removeBlanks = false })
local jsonStr = mw.text.unstripNoWiki(trim(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 bullet = mw.text.unstripNoWiki(args.prefix or "* ")
local sep = mw.text.unstripNoWiki(args.sep or ": ")
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 out = {}
if is_array(data) then
for _, v in ipairs(data) do
local text = ""
if type(v) == "table" then
if is_array(v) then
if #v == 1 then
text = tostring(v[1])
elseif #v > 1 then
local parts = {}
for i = 1, #v do
parts[#parts + 1] = tostring(v[i])
end
text = table.concat(parts, ", ")
end
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)
table.insert(out, bullet .. text)
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 keyStr = tostring(k)
keyStr = apply_pattern(keyStr, keyPattern, keyReplace)
vStr = apply_pattern(vStr, valuePattern, valueReplace)
if vStr ~= "" then
table.insert(out, bullet .. keyStr .. sep .. vStr)
end
end
end
return table.concat(out, "\n")
end
return p