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

Материал из Space Station 14 Вики
Новая страница: «local p = {} function p.getFromTitle(titleName) local title = mw.title.new(titleName) local jsonText = title and title:getContent() or "" local ok, data = pcall(mw.text.jsonDecode, jsonText) if ok and type(data) == "table" then return data else return {} end end function p.invoke(frame) local name = frame.args.title return p.getFromTitle(name) end return p»
 
Нет описания правки
Метка: отменено
Строка 1: Строка 1:
local p = {}
local p = {}
local function skip_ws(text, pos)
local len = #text
while pos <= len do
local ch = string.sub(text, pos, pos)
if ch ~= " " and ch ~= "\n" and ch ~= "\r" and ch ~= "\t" then
return pos
end
pos = pos + 1
end
return pos
end
local function skip_string(text, pos)
pos = pos + 1
local len = #text
while pos <= len do
local ch = string.sub(text, pos, pos)
if ch == "\\" then
pos = pos + 2
elseif ch == "\"" then
return pos
else
pos = pos + 1
end
end
return nil
end
local function find_value_end(text, pos)
local ch = string.sub(text, pos, pos)
if ch == "\"" then
return skip_string(text, pos)
end
if ch == "{" or ch == "[" then
local depth = 0
local len = #text
while pos <= len do
ch = string.sub(text, pos, pos)
if ch == "\"" then
pos = skip_string(text, pos)
if not pos then
return nil
end
elseif ch == "{" or ch == "[" then
depth = depth + 1
elseif ch == "}" or ch == "]" then
depth = depth - 1
if depth == 0 then
return pos
end
end
pos = pos + 1
end
return nil
end
local len = #text
while pos <= len do
ch = string.sub(text, pos, pos)
if ch == "," or ch == "}" or ch == "]" or ch == "\n" or ch == "\r" then
return pos - 1
end
pos = pos + 1
end
return len
end
local function decode_json(text)
local ok, data = pcall(mw.text.jsonDecode, text)
if ok then
return data
end
return nil
end
local function find_member_range(text, objectStart, keyName)
local pos = skip_ws(text, objectStart)
if string.sub(text, pos, pos) ~= "{" then
return nil, nil
end
pos = skip_ws(text, pos + 1)
while pos <= #text and string.sub(text, pos, pos) ~= "}" do
if string.sub(text, pos, pos) ~= "\"" then
return nil, nil
end
local keyStart = pos
local keyEnd = skip_string(text, pos)
if not keyEnd then
return nil, nil
end
local key = decode_json(string.sub(text, keyStart, keyEnd))
pos = skip_ws(text, keyEnd + 1)
if string.sub(text, pos, pos) ~= ":" then
return nil, nil
end
local valueStart = skip_ws(text, pos + 1)
local valueEnd = find_value_end(text, valueStart)
if not valueEnd then
return nil, nil
end
if key == keyName then
return valueStart, valueEnd
end
pos = skip_ws(text, valueEnd + 1)
local sep = string.sub(text, pos, pos)
if sep == "," then
pos = skip_ws(text, pos + 1)
elseif sep == "}" then
return nil, nil
else
return nil, nil
end
end
return nil, nil
end
local function deep_copy(src)
local dst = {}
for k, v in pairs(src) do
if type(v) == "table" then
dst[k] = deep_copy(v)
else
dst[k] = v
end
end
return dst
end
local function deep_merge(dst, src)
for k, v in pairs(src) do
if type(v) == "table" and type(dst[k]) == "table" then
deep_merge(dst[k], v)
elseif type(v) == "table" then
dst[k] = deep_copy(v)
else
dst[k] = v
end
end
end
local function new_loader(titleName)
local loader = {}
local title = mw.title.new(titleName)
local jsonData
local fullData
local defaultData
local idDataCache = {}
local function get_json()
if jsonData == nil then
jsonData = title and title:getContent() or ""
end
return jsonData
end
local function decode_root_member(keyName)
local text = get_json()
local rootStart = string.find(text, "{", 1, true)
if not rootStart then
return nil
end
local valueStart, valueEnd = find_member_range(text, rootStart, keyName)
if not valueStart then
return nil
end
return decode_json(string.sub(text, valueStart, valueEnd))
end
local function decode_id_member(id)
if idDataCache[id] ~= nil then
return idDataCache[id]
end
local text = get_json()
local rootStart = string.find(text, "{", 1, true)
if not rootStart then
idDataCache[id] = false
return nil
end
local idsStart = find_member_range(text, rootStart, "id")
if not idsStart then
idDataCache[id] = false
return nil
end
local valueStart, valueEnd = find_member_range(text, idsStart, id)
if not valueStart then
idDataCache[id] = false
return nil
end
local data = decode_json(string.sub(text, valueStart, valueEnd))
idDataCache[id] = data or false
return data
end
function loader.all()
if fullData == nil then
local data = decode_json(get_json())
fullData = type(data) == "table" and data or {}
end
return fullData
end
function loader.get(id)
id = tostring(id or "")
if defaultData == nil then
local data = decode_root_member("default")
defaultData = type(data) == "table" and data or false
end
local base = defaultData ~= false and defaultData or nil
if id == "" then
return base
end
local specific = decode_id_member(id)
if type(specific) ~= "table" then
return base
end
if type(base) == "table" then
local merged = deep_copy(base)
deep_merge(merged, specific)
return merged
end
return specific
end
return loader
end


function p.getFromTitle(titleName)
function p.getFromTitle(titleName)
    local title = mw.title.new(titleName)
return new_loader(titleName)
    local jsonText = title and title:getContent() or ""
    local ok, data = pcall(mw.text.jsonDecode, jsonText)
    if ok and type(data) == "table" then
        return data
    else
        return {}
    end
end
end


function p.invoke(frame)
function p.invoke(frame)
    local name = frame.args.title
local name = frame.args.title
    return p.getFromTitle(name)
local loader = p.getFromTitle(name)
return loader.all()
end
end


return p
return p

Версия от 19:08, 18 июня 2026

Для документации этого модуля может быть создана страница Модуль:JsonLoader/doc

local p = {}

local function skip_ws(text, pos)
	local len = #text
	while pos <= len do
		local ch = string.sub(text, pos, pos)
		if ch ~= " " and ch ~= "\n" and ch ~= "\r" and ch ~= "\t" then
			return pos
		end
		pos = pos + 1
	end
	return pos
end

local function skip_string(text, pos)
	pos = pos + 1
	local len = #text
	while pos <= len do
		local ch = string.sub(text, pos, pos)
		if ch == "\\" then
			pos = pos + 2
		elseif ch == "\"" then
			return pos
		else
			pos = pos + 1
		end
	end
	return nil
end

local function find_value_end(text, pos)
	local ch = string.sub(text, pos, pos)
	if ch == "\"" then
		return skip_string(text, pos)
	end

	if ch == "{" or ch == "[" then
		local depth = 0
		local len = #text
		while pos <= len do
			ch = string.sub(text, pos, pos)
			if ch == "\"" then
				pos = skip_string(text, pos)
				if not pos then
					return nil
				end
			elseif ch == "{" or ch == "[" then
				depth = depth + 1
			elseif ch == "}" or ch == "]" then
				depth = depth - 1
				if depth == 0 then
					return pos
				end
			end
			pos = pos + 1
		end
		return nil
	end

	local len = #text
	while pos <= len do
		ch = string.sub(text, pos, pos)
		if ch == "," or ch == "}" or ch == "]" or ch == "\n" or ch == "\r" then
			return pos - 1
		end
		pos = pos + 1
	end
	return len
end

local function decode_json(text)
	local ok, data = pcall(mw.text.jsonDecode, text)
	if ok then
		return data
	end
	return nil
end

local function find_member_range(text, objectStart, keyName)
	local pos = skip_ws(text, objectStart)
	if string.sub(text, pos, pos) ~= "{" then
		return nil, nil
	end

	pos = skip_ws(text, pos + 1)
	while pos <= #text and string.sub(text, pos, pos) ~= "}" do
		if string.sub(text, pos, pos) ~= "\"" then
			return nil, nil
		end

		local keyStart = pos
		local keyEnd = skip_string(text, pos)
		if not keyEnd then
			return nil, nil
		end

		local key = decode_json(string.sub(text, keyStart, keyEnd))
		pos = skip_ws(text, keyEnd + 1)
		if string.sub(text, pos, pos) ~= ":" then
			return nil, nil
		end

		local valueStart = skip_ws(text, pos + 1)
		local valueEnd = find_value_end(text, valueStart)
		if not valueEnd then
			return nil, nil
		end

		if key == keyName then
			return valueStart, valueEnd
		end

		pos = skip_ws(text, valueEnd + 1)
		local sep = string.sub(text, pos, pos)
		if sep == "," then
			pos = skip_ws(text, pos + 1)
		elseif sep == "}" then
			return nil, nil
		else
			return nil, nil
		end
	end

	return nil, nil
end

local function deep_copy(src)
	local dst = {}
	for k, v in pairs(src) do
		if type(v) == "table" then
			dst[k] = deep_copy(v)
		else
			dst[k] = v
		end
	end
	return dst
end

local function deep_merge(dst, src)
	for k, v in pairs(src) do
		if type(v) == "table" and type(dst[k]) == "table" then
			deep_merge(dst[k], v)
		elseif type(v) == "table" then
			dst[k] = deep_copy(v)
		else
			dst[k] = v
		end
	end
end

local function new_loader(titleName)
	local loader = {}
	local title = mw.title.new(titleName)
	local jsonData
	local fullData
	local defaultData
	local idDataCache = {}

	local function get_json()
		if jsonData == nil then
			jsonData = title and title:getContent() or ""
		end
		return jsonData
	end

	local function decode_root_member(keyName)
		local text = get_json()
		local rootStart = string.find(text, "{", 1, true)
		if not rootStart then
			return nil
		end

		local valueStart, valueEnd = find_member_range(text, rootStart, keyName)
		if not valueStart then
			return nil
		end

		return decode_json(string.sub(text, valueStart, valueEnd))
	end

	local function decode_id_member(id)
		if idDataCache[id] ~= nil then
			return idDataCache[id]
		end

		local text = get_json()
		local rootStart = string.find(text, "{", 1, true)
		if not rootStart then
			idDataCache[id] = false
			return nil
		end

		local idsStart = find_member_range(text, rootStart, "id")
		if not idsStart then
			idDataCache[id] = false
			return nil
		end

		local valueStart, valueEnd = find_member_range(text, idsStart, id)
		if not valueStart then
			idDataCache[id] = false
			return nil
		end

		local data = decode_json(string.sub(text, valueStart, valueEnd))
		idDataCache[id] = data or false
		return data
	end

	function loader.all()
		if fullData == nil then
			local data = decode_json(get_json())
			fullData = type(data) == "table" and data or {}
		end
		return fullData
	end

	function loader.get(id)
		id = tostring(id or "")

		if defaultData == nil then
			local data = decode_root_member("default")
			defaultData = type(data) == "table" and data or false
		end

		local base = defaultData ~= false and defaultData or nil
		if id == "" then
			return base
		end

		local specific = decode_id_member(id)
		if type(specific) ~= "table" then
			return base
		end

		if type(base) == "table" then
			local merged = deep_copy(base)
			deep_merge(merged, specific)
			return merged
		end

		return specific
	end

	return loader
end

function p.getFromTitle(titleName)
	return new_loader(titleName)
end

function p.invoke(frame)
	local name = frame.args.title
	local loader = p.getFromTitle(name)
	return loader.all()
end

return p