Module:Convert/show

From MattWiki
Jump to: navigation, search

-- Prepare tables of wikitext to display simple documentation about
-- specified units. Data is obtained by calling Module:Convert.
-- Also provides a function to show convert usage examples.

local function collection()
	-- Return a table to hold items.
	return {
		n = 0,
		add = function (self, item)
			self.n = self.n + 1
			self[self.n] = item
		end,
		join = function (self, sep)
			return table.concat(self, sep)
		end,
	}
end

local function strip(text)
	-- Return text with no leading/trailing whitespace.
	return text:match("^%s*(.-)%s*$")
end

local function frame(self_args, parent_args)
	-- Simulate enough of a MediaWiki module frame for convert.
	return {
		args = self_args,
		parent = parent_args and frame(parent_args, nil),
		getParent = function (self) return self.parent end,
	}
end

local cvt_function
local function call_convert(args)
	if not cvt_function then
		-- A testing program can set the global variable 'is_test_run'.
		cvt_function = require(is_test_run and 'convert' or 'Module:Convert').convert
	end
	return cvt_function(frame({}, args))
end

local function maketable(results, units)
	results:add('{| class="wikitable"')
	results:add('! Unit code !! Unit symbol !! Unit name !! US name, if different')
	for i, ucode in ipairs(units) do
		local row = collection()
		row:add(ucode)
		local args = { '1', ucode, abbr = 'on', disp = 'unit' }
		row:add(call_convert(args))
		args.abbr = 'off'
		local name1 = call_convert(args)
		row:add(name1)
		args.sp = 'us'
		local name1_us = call_convert(args)
		if name1_us == name1 then
			row:add('')
		else
			row:add(name1_us)
		end
		results:add('|-')
		results:add(strip('| ' .. row:join(' || ')))
	end
	results:add('|}')
	results:add('')
end

-- Commonly used units for main documentation.
-- Can only be input units (not combinations or multiples).
local common_units = {
	["Area"] = {
		heading = "Area",
		examples = { "1.5|sqmi|km2", "1.5|sqmi|km2|abbr=off", "1.5|sqmi|km2|abbr=on" },
		"acre",
		"ha",
		"m2",
		"cm2",
		"km2",
		"sqin",
		"sqft",
		"sqyd",
		"sqmi",
	},
	["Fuel efficiency"] = {
		heading = "Fuel efficiency",
		examples = { "12|mpgus|km/L", "12|mpgus|km/L|abbr=off", "12|mpgus|km/L|abbr=off|sp=us", "12|mpgus|km/L|abbr=on" },
		"km/L",
		"mpgimp",
		"mpgus",
		"L/km",
		"L/100 km",
	},
	["Length"] = {
		heading = "Length",
		examples = { "123|cm|in", "123|cm|in|abbr=off|sp=us", "123|cm|in|abbr=on" },
		"uin",
		"in",
		"ft",
		"yd",
		"mi",
		"nmi",
		"m",
		"cm",
		"mm",
		"km",
		"angstrom",
	},
	["Mass"] = {
		heading = "Mass",
		examples = { "72.3|kg|lb", "72.3|kg|lb|abbr=off", "72.3|kg|lb|abbr=on" },
		"g",
		"kg",
		"oz",
		"lb",
		"st",
		"LT",
		"MT",
		"ST",
	},
	["Pressure"] = {
		heading = "Pressure",
		examples = { "28|psi|Pa", "28|psi|Pa|abbr=off", "28|psi|Pa|abbr=on" },
		"atm",
		"mbar",
		"psi",
		"Pa",
	},
	["Speed"] = {
		heading = "Speed",
		examples = { "60|mph|km/h", "60|mph|km/h|abbr=off", "60|mph|km/h|abbr=on" },
		"km/h",
		"km/s",
		"kn",
		"mph",
	},
	["Temperature"] = {
		heading = "Temperature",
		examples = { "100|C|F", "100|C|F|abbr=off", "100|C-change|F-change", "100|C-change|F-change|abbr=out" },
		"C",
		"F",
		"K",
		"C-change",
		"F-change",
		"K-change",
	},
	["Torque"] = {
		heading = "Torque",
		examples = { "12.5|Nm|lb.in", "12.5|Nm|lb.in|abbr=off", "12.5|Nm|lb.in|abbr=on|lk=on" },
		"lb.in",
		"lb.ft",
		"Nm",
	},
	["Volume"] = {
		heading = "Volume",
		examples = { "125|cuin|l", "125|cuin|l|abbr=off", "125|cuin|l|abbr=on" },
		"cuin",
		"cuft",
		"cuyd",
		"cumi",
		"impgal",
		"impoz",
		"usgal",
		"usoz",
		"L",
		"l",
		"m3",
		"cc",
		"mm3",
	},
}

-- Order in which sections are wanted when doing all common units.
local common_sections = {
	"Area",
	"Fuel efficiency",
	"Length",
	"Mass",
	"Pressure",
	"Speed",
	"Temperature",
	"Torque",
	"Volume",
}

local function _show_examples(frame, results, examples, want_table)
	local fmt
	if want_table then
		results:add('{|')
		fmt = '|<code>%s</code>|| → ||%s'
	else
		fmt = '*<code>%s</code> → %s'
	end
	for i, item in ipairs(examples) do
		if want_table and i > 1 then
			results:add('|-')
		end
		item = item:gsub('!', '|')
		item = '{{convert' .. (item:sub(1, 1) == '|' and '' or '|') .. item .. '}}'
		results:add(fmt:format(mw.text.nowiki(item), frame:preprocess(item)))
	end
	if want_table then
		results:add('|}')
	end
end

local function _show_units(frame, results, args)
	local do_full
	if args[1] == nil then
		do_full = true
		args = common_sections
	end
	local group = collection()
	for _, item in ipairs(args) do
		local units = common_units[item] or common_units[item:sub(1, 1):upper() .. item:sub(2)]
		if units then
			if group.n > 0 then
				maketable(results, group)
				group = collection()
			end
			if do_full then
				if units.heading then
					results:add('===' .. units.heading .. '===')
				end
				if units.examples then
					results:add('Examples:')
					_show_examples(frame, results, units.examples)
				end
			end
			maketable(results, units)
		else
			group:add(item)
		end
	end
	if group.n > 0 then
		maketable(results, group)
	end
end

local function show_examples(frame, want_table)
	local results = collection()
	local ok, msg = pcall(_show_examples, frame, results, frame.args, want_table)
	if ok then
		return results:join('\n')
	end
	return '<strong class="error">Error</strong>\n' .. msg
end

local function show_units(frame)
	local results = collection()
	local ok, msg = pcall(_show_units, frame, results, frame.args)
	if ok then
		return results:join('\n')
	end
	return '<strong class="error">Error</strong>\n' .. msg
end

return {
	unit = show_units,
	units = show_units,
	['list'] = function (frame) return show_examples(frame, false) end,
	['table'] = function (frame) return show_examples(frame, true) end,
}