Module:Core

Revision as of 12:53, 30 December 2017 by がか (talk | contribs) (Undo revision 934473 by がか (talk))

Documentation for this module may be created at Module:Core/doc

local Utils = {}

-- * Collection functions.

function Utils.find(tbl, v_, k_)
	for _, v in pairs(tbl) do
		if k_ and v and v[k_] == v_ or not k_ and v == v_ then
			return v
		end
	end
	return false
end

function Utils.map(tbl, fn)
	local result = {}
	for k, v in pairs(tbl) do
		table.insert(result, fn(v, k))
	end
	return result
end
 
function Utils.filter(tbl, pred)
	local result = {}
	for k, v in pairs(tbl) do
		if pred(v, k) then
			table.insert(result, v)
		end
	end
	return result
end

function Utils.first(tbl)
    for k, v in pairs(tbl) do
        return k, v
    end
end

function Utils.ifind(arr, v_)
	for i, v in ipairs(arr) do
		if v == v_ then
			return i
		end
	end
	return false
end

function Utils.ifindBy(arr, p)
	for i, v in ipairs(arr) do
		if p(v) then
			return v, i
		end
	end
	return false
end

function Utils.imap(arr, fn)
	local result = {}
	for i, v in ipairs(arr) do
		table.insert(result, fn(v, i))
	end
	return result
end

function Utils.ifilter(arr, pred)
	local result = {}
	for i, v in ipairs(arr) do
		if pred(v, i) then
			table.insert(result, v)
		end
	end
	return result
end

function Utils.ifirst(arr)
    for k, v in ipairs(arr) do
        return k, v
    end
end

function Utils.insertNew(arr, el)
    if not Utils.find(arr, el) then
        table.insert(arr, el)
    end
end

function Utils.concat(arr1, arr2)
    for i = 1, #arr2 do
        arr1[#arr1 + 1] = arr2[i]
    end
    return arr1
end

function Utils.icopy(arr)
    return Utils.imap(arr, function(v) return v end)
end

-- * String functions.

function Utils.category(name)
    return "[[" .. "Category:" .. name .. "]]"
end

function Utils.pad(s, n, c)
    c = c or " "
    n = n or 0
    s = tostring(s) or ""
    return #s < n and string.rep(c, n - #s) .. s or s
end

function Utils.trim(s)
    return string.gsub(s, "^%s*(.-)%s*$", "%1")
end

function Utils.startsWith(s, ss)
    return string.sub(s, 1, string.len(ss)) == ss
end

-- Capitalize each word in a string.
function Utils.capitalize(s)
    s = s:gsub("(%s)(%l)", function(a, b) return a .. string.upper(b) end)
    s = s:gsub("^(%l)", function(a) return string.upper(a) end)
    return s
end

-- * Calling arbitrary Lua functions using #invoke.

-- Used to call Formatting:tooltip in Template:Tooltip, mainly because Lua code properly escapes characters,
-- so that span's title attribute always works.
function Utils.method(frame)
	local m = require("Module:" .. frame.args[1])
	local f = frame.args[2]
	local args = {}
	for k, v in ipairs(frame.args) do
		if type(k) == "number" and k >= 3 and type(v) == "string" then
			table.insert(args, v)
		end
	end
	return m[f](m, unpack(args))
end

-- * Frame functions.

local getArgs = require("Module:GetArgs")

-- Unused.
function Utils.getContext(frame)
    local frame1 = frame:getParent()
    if frame1 then
        local frame2 = frame1:getParent()
        if frame2 then
            return { pagename = frame2:getTitle(), args = getArgs{ frame = frame2 } }
        else
            return { pagename = frame1:getTitle(), args = getArgs{ frame = frame1 } }
        end
    else
        return { pagename = frame:getTitle(), args = getArgs{ frame = frame } }
    end
end

-- getParent -> getArgs
function Utils.getParentArgs(frame)
    local frame1 = frame:getParent()
    if frame1 then
        return getArgs{ frame = frame1 }
    else
        return nil
    end
end

-- getArgs + getParent -> getArgs, "implicit" args can be defined in the template (e.g. pagename={{PAGENAME}})
-- "explicit" args are user defined.
function Utils.getTemplateArgs(frame)
    local frame1 = frame:getParent()
    if frame1 then
        return { implicit = getArgs{ frame = frame }, explicit = getArgs{ frame = frame1 } }
    else
        return { implicit = getArgs{ frame = frame }, explicit = {} }
    end
end

function Utils.requireModule(name)
    local success, data = pcall(function () return require(string.format("Module:%s", name)) end)
    -- module without return (or empty, nil, false, true return) gives success = true, data = true
    if data == true then
        return false, nil
    else
        return success, data
    end
end

-- * Testing functions.

function Utils.debugPrint(x, i)
    i = i or 0
    if type(x) == "table" then
        for k, v in pairs(x) do
            mw.log(
                string.rep("  ", i) .. tostring(k) .. " : " .. type(k) .. " = " ..
                (type(v) == "table" and "table" or tostring(v) .. " : " .. type(v))
            )
            if type(v) == "table" then
                debugPrint(v, i + 1)
            end
        end
    else
        mw.log(tostring(x) .. " : " .. type(x))
    end
end

function Utils.registerFormatTests(obj, tests, fn)
    obj.run_format_tests = function()
        for _, test in ipairs(tests) do
            local result = obj.format(nil, test)
            mw.log(fn and fn(result) or result)
        end
    end
end

function Utils.registerTableTests(obj, tests, fn)
    obj.run_table_tests = function()
        for _, test in ipairs(tests) do
            local result = obj:Table(test)
            mw.log(fn and fn(result) or result)
        end
    end
end

return Utils