Module:Size
Jump to navigation
Jump to search
Details
The Size module offers a Lua class for parsing sizes in the format used for image links. It's intended to be used by other Lua modules instead of templates.
It has no dependencies and exports a Size
"class" you can use directly. It provides annotations documenting the various functions that are understood by Lua Language Server.
--- Module:Size
--- by User:LtDk, licensed CC-BY-SA 4.0
--- Simple Lua class for parsing image sizes.
local Size = {}
local SizeKeys = {
width = true,
height = true,
}
--- WWpx, xHHpx, and WWxHHpx sizes.
--- @class Size
--- @field width number?
--- @field height number?
local SizeProto = {}
local SizeMeta = {}
--- Checks if a value is a Size.
--- @param value any
--- @return boolean
function Size.isSize(value)
return type(value) == 'table' and rawequal(getmetatable(value), SizeMeta)
end
function SizeMeta.__eq(lhs, rhs)
if not Size.isSize(rhs) then
return false
end
return lhs.width == rhs.width and lhs.height == rhs.height
end
--- Sizes can have nil width or nil height, but not nil width and nil height.
function SizeMeta:__newindex(k, v)
if not SizeKeys[k] then
error(string.format('invalid key %s', k))
end
if v ~= nil and (type(v) ~= 'number' or math.floor(v) ~= v or v <= 0) then
error(string.format('%s must be positive integer or nil, was %s', k, v))
end
if v == nil then
if k == 'width' and self.height == nil then
error('cannot set width to nil when height is already nil')
elseif k == 'height' and self.width == nil then
error('cannot set height to nil when width is already nil')
end
end
rawset(self, '__' .. k, v)
end
--- We rename the actual keys to ensure newindex always fires.
function SizeMeta:__index(k)
if not SizeKeys[k] then
return SizeProto[k]
else
return rawget(self, '__' .. k)
end
end
--- Sizes are formatted as WWpx, xHHpx, or WWxHHpx.
function SizeMeta:__tostring()
local w = ''
local h = ''
if self.height ~= nil then
h = 'x' .. tostring(self.height)
end
if self.width ~= nil then
w = tostring(self.width)
end
return w .. h .. 'px'
end
--- Parses a size from a string. Accepts WWpx, xHHpx, and WWxHHpx.
--- @param size string
--- @return Size
function Size.parse(size)
size = tostring(size)
local parsed = Size.tryParse(size)
if parsed == nil then
error(string.format('%q is not a valid size', size))
end
return parsed
end
--- Attempts to parse a Size from a string, returning nil instead of errors.
--- @param size string
--- @return Size?
function Size.tryParse(size)
size = mw.text.trim(tostring(size)) --[[@as string]]
if string.sub(size, -2) == 'px' then
size = mw.text.trim(string.sub(size, 1, -3)) --[[@as string]]
end
local width = ''
local height = ''
local i, j = string.find(size, 'x', 1, true)
if i == nil then
width = size
else
width = mw.text.trim(string.sub(size, 1, i - 1)) --[[@as string]]
height = mw.text.trim(string.sub(size, j + 1)) --[[@as string]]
end
local width_n = tonumber(width, 10)
local height_n = tonumber(height, 10)
if width_n == nil and (width ~= '' or height_n == nil) then
return nil
end
if height_n == nil and (height ~= '' or width_n == nil) then
return nil
end
if width_n ~= nil and (math.floor(width_n) ~= width_n or width_n < 0) then
return nil
end
if height_n ~= nil and (math.floor(height_n) ~= height_n or height_n < 0) then
return nil
end
return Size.new(width_n, height_n)
end
--- Creates a new Size from parts.
--- @param width number?
--- @param height number?
--- @return Size
function Size.new(width, height)
local size = setmetatable({ width = width, height = height }, SizeMeta)
if width == nil and height == nil then
error('Size cannot have both nil width and nil height')
end
if width ~= nil and (type(width) ~= 'number' or math.floor(width) ~= width or width < 0) then
error(string.format('width must be a positive integer or nil, was %s', width))
end
if height ~= nil and (type(height) ~= 'number' or math.floor(height) ~= height or height < 0) then
error(string.format('height must be a positive integer or nil, was %s', height))
end
return size
end
return Size