跳转到内容

模組:Ifexist

被永久保护的模块
维基百科,自由的百科全书
local p = {}

local function makeTitle(title, callFn)
	if type(title) == type({}) and type(title.fullText) == type('') then
		return title
	elseif type(title) == type('') then
		local success, titleObj = pcall(mw.title.new, title)
		if success and titleObj then
			return titleObj
		end
		return false
	end
	error(string.format(
		'bad argument #1 to \'%s\' (%s expected, got %s)',
		callFn,
		'string or mw.title object',
		type(title)
	))
end

local function wrapCacheAbleExistsFunc(callFn, callback)
	local cache = {}
	return function (title, ...)
		local titleObj = makeTitle(title, callFn)
		if not titleObj or titleObj.interwiki ~= '' then
			return false
		end
		local cacheValue = cache[titleObj.fullText]
		if cacheValue == nil then
			cacheValue = callback(titleObj, ...)
			cache[titleObj.fullText] = cacheValue
		end
		return cacheValue
	end
end

local function wrapIfExists(fn)
	return function (title, thenVal, elseVal)
		return fn(title) and thenVal or elseVal
	end
end

-- 使用 Lua Api mw.title.exists 來判斷
-- 不會呼叫 findVariantLink 所以有繁簡差異的頁面「不會」識別出來
-- 也會嚴格限制 500 次調用
-- from [[Special:PermanentLink/42867949#L-142]]
p._luaExists = wrapCacheAbleExistsFunc('_luaExists', function (titleObj)
	return titleObj.exists and true or false
end)

p._luaIfExists = wrapIfExists(p._luaExists)

-- 檢查檔案的
p._luaFileExists = wrapCacheAbleExistsFunc('_luaFileExists', function (titleObj)
	return (titleObj.namespace == 6 or titleObj.namespace == -2) and titleObj.file.exists and true or false
end)

p._luaFileIfExists = wrapIfExists(p._luaFileExists)

-- 使用解析器函數來判斷
-- 該解析器函數會呼叫 findVariantLink 有繁簡差異的頁面會識別出來
-- 由於使用了 LinkCache,所以允許次數相對寬鬆
-- from [[Special:PermanentLink/85840827#L-31]]
local existsPlaceholder = '__EXISTS__'
local missingPlaceholder = '__MISSING__'
p._pfExists = wrapCacheAbleExistsFunc('_pfExists', function (titleObj)
	return mw.getCurrentFrame():callParserFunction('#ifexist', { titleObj.fullText, existsPlaceholder, missingPlaceholder }) == existsPlaceholder
end)

-- @deprecated
p._parseFunctionExists = p._pfExists

p._pfIfExists = wrapIfExists(p._pfExists)

-- @deprecated
p._parseFunctionIfExists = p._pfIfExists

-- 檢查檔案的
p._pfFileExists = wrapCacheAbleExistsFunc('_pfFileExists', function (titleObj)
	if titleObj.namespace == 6 then
		-- 解析器函數檢查檔案本身是否存在的方式是查詢 Media 命名空間
		titleObj = mw.title.makeTitle(-2, titleObj.text)
	end
	return titleObj.namespace == -2 and p._pfExists(titleObj) or false
end)

p._pfFileIfExists = wrapIfExists(p._pfFileExists)

function p._detectLangConvDiff(title)
	-- 檢查輸入頁面是否不存在但呼叫 findVariantLink 後可以找到對應的繁簡頁面
	-- 技術細節:
	-- - 解析器函數會呼叫 findVariantLink
	-- - Lua 函數不會呼叫 findVariantLink
	-- - 故可以檢查兩個函數的返回值來確定一個頁面與其實際存在的頁面是否是有繁簡差異
	-- 註:邏輯上可以用 parseFunctionExists ~= luaExists
	--     但後者是高開銷函數要省著用,並且解析器函數版本都回報不存在了就不用測 Lua Api 了
	local titleObj = makeTitle(title, '_detectLangConvDiff')
	if titleObj and titleObj.interwiki == '' then
		local pfExists = p._pfExists(titleObj)
		if pfExists then
			return not p._luaExists(titleObj)
		end
	end
	return false
end

-- //// //// --

local yesno

local function tidyValNoChange(value)
	return value
end

local function tidyValTrim(value)
	return mw.text.trim(value)
end

-- 註:wikitext 呼叫解析器函數不需要透過 Lua
-- 所以只提供 Lua Api
function p.main(frame)
	if type(yesno) ~= type(tonumber) then
		yesno = require('Module:Yesno')
	end
	local args = frame.args
	local tidyValue = yesno(args.noTrim) and tidyValNoChange or tidyValTrim
	local title = args.title or args[1]
	if not title then
		error('[[Module:Ifexist]]: Missing title.')
	end
	local thenVal = tidyValue(args['then'] or args[2] or title)
	local elseVal = tidyValue(args['else'] or args[3] or '')
	return p._luaIfExists(title, thenVal, elseVal)
end

return p