local p = {}
--[[
array 是一个非空的数组,其各项均为 table。截取其 id 和 aliases 的内容,作为表的键。
例如,如果 array 的值为 { {id = 'a', aliases = {'b', 'c'} } },那么
处理之后,这个 array.a、array.b、array.c 的值都会是 array[1] 的值。
其中各项的 aliases 字段时可选的。
之所以先使用数组,是为了以便于此后按顺序迭代,而非按照默认的 pairs 的顺序按照散列迭代。
该函数会修改参数 array 的值,并返回它。
]]
function p.complete_index(array)
if type(array) ~= 'table' then
error('无法调用complete_index,因为' .. tostring(array) .. '的类型不为table')
end
for i, item in ipairs(array) do
if type(item) ~= 'table' then
error('错误:表的第' .. i .. '项(' .. tostring(item) .. ')' .. '的数据类型不为table')
end
local id = item.id
if not id then
-- 忽略没有 id 的情况。
elseif type(id) == 'number' then
error('错误:表的第' .. i .. '项的id(' .. tostring(id) .. ')' .. '的数据类型不可以是number')
else
array[id] = item
end
local aliases = item.aliases
if not aliases then
-- 忽略没有 aliases 的情况。
elseif type(aliases) == 'table' then
for _, alias in ipairs(aliases) do
if not alias then
elseif type(alias) == 'number' then
error('错误:表的第' .. i .. '项的alias中不应包含number数据类型:' .. tostring(alias))
else
array[alias] = item
end
end
elseif type(aliases) == 'number' then
error('错误:表的第' .. i .. '项的alises(' .. tostring(aliases) .. ')' .. '的数据类型不可以是number')
end
end
return array
end
--[[
格式化字符串,例如:
p.format('$1和$2', 'first', 'second') → first和second
p.format('$1和$2', {'first', 'second'}) → first和second
p.format('$a和$b', {a = 'first', b = 'second'}) → first和second
]]
function p.format(fs, args, ...)
if type(args) ~= 'table' then
args = {args, ...}
end
return string.gsub(fs, '%$(%w+)', function (capture)
return args[tonumber(capture) or capture]
end)
end
--[[
创建一个由两个表组合的表。
返回的这个表在索引时,如果 t1 中的键不存在(不包括为 false),则尝试访问 t2 中的。
返回的表是根据元表控制的,并不会实际复制表的内容。
例如,t1 = {a = 'A', b = 'B', c = 'C'}
t2 = {b = '2', c = '3', d = '4'}
t3 = fallbackTable(t1, t2)
那么,t3.a = 'A', t3.b = 'B', t3.d = '4'。
]]
function p.fallbackTable(t1, t2)
if not t1 then
-- 一项都没有,返回空。
return t2 or t1
end
if not t2 then
-- 只有一项,返回这一项。
return t1
end
return setmetatable({}, {
__index = function(self, key)
local v1 = t1[key]
if v1 == nil then
return t2[key]
else
return v1
end
end
})
end
--[[
判断某个元素是否在一个数组内,或者自身与它相等。
例如inArray('a', 'a') => true
inArray('a', {'a', 'b'}) => true
inArray('a', {'c', 'd'}) => true
判断相等是根据的“==”运算符。
]]
function p.inArray(element, array)
if element == array then return true end
if type(array) == 'table' then
for _, v in ipairs(array) do
if v == element then return true end
end
end
return false
end
--[[
判断 elements 中的任意一个元素是否在 array 中。
elements 和 array 都可以是数组,也可以是其他类型。
]]
function p.anyInArray(elements, array)
if p.inArray(elements, array) then return true end
if type(elements) == 'table' then
for _, element in ipairs(elements) do
if p.inArray(element, array) then return true end
end
end
return false
end
--[[
判断字符串能否找到匹配的正则表达式,或者正则表达式列表中的任何一个。
@string s
@string|table patterns
]]
function p.matchAnyOf(s, patterns)
if type(s) ~= 'string' and type(s) ~= 'number' then
error ('参数 s 的必须是字符串,而不是' .. tostring(s))
end
if type(patterns) == 'string' or type(patterns) == 'number' then
return mw.ustring.match(s, patterns)
elseif type(patterns) == 'table' then
for _, pattern in ipairs(patterns) do
if type(pattern) == 'string' or type(pattern) == 'number' then
local match = mw.ustring.match(s, pattern)
if match then return match end
else
error ('参数 patterns 中包含非字符串值:' .. tostring(pattern))
end
end
return nil
else
error ('参数 patterns 必须是字符串或者列表,而非' .. tostring(patterns))
end
end
--[[
array 和 patterns 应该至少一个不为 nil。
]]
function p.inArrayOrMatchAnyOf(elements, array, patterns)
return (array and p.inArray(elements, array))
or (patterns and (type(elements) == 'string' or type(elements) == 'nunmber') and p.matchAnyOf(elements, patterns))
end
function p.tryLoadData(name)
local successes, data = pcall(mw.loadData, name)
if successes then
return data
else
mw.log("无法加载" .. tostring(name) .. "中的数据,原因:" .. tostring(data))
end
end
function p.tryLoadJsonData(name)
local successes, data = pcall(mw.loadJsonData, name)
if successes then
return data
else
mw.log("无法加载" .. tostring(name) .. "中的数据,原因:" .. tostring(data))
end
end
--[[
strings 是由字符串组成的数组。
例如:
{'a', 'b', '!c', '!d'} => {'a', 'b'}, {'c', 'd'}
注意:返回的空表会转化为 nil
]]
function p.splitPrefixedStrings(strings, prefix)
if not strings then return nil end
local ret1, ret2 = {}, {}
local i = #prefix
for _, s in ipairs(strings) do
if s:sub(1, i) == prefix then
ret2[#ret2 + 1] = s:sub(i + 1)
else
ret1[#ret1 + 1] = s
end
end
return (#ret1 > 0 and ret1 or nil), (#ret2 > 0 and ret2 or nil)
end
return p