diff options
| author | William Boman <william@redwill.se> | 2023-03-05 03:04:31 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-03-05 03:04:31 +0100 |
| commit | 90bbcceffa1f1051b4e6c62e5ec7d33d93efa5ca (patch) | |
| tree | 9891c5f91b5b7bde294387fabef91e9020dfe2fb /lua | |
| parent | chore(ci): bump selene and stylua versions (#1059) (diff) | |
| download | mason-90bbcceffa1f1051b4e6c62e5ec7d33d93efa5ca.tar mason-90bbcceffa1f1051b4e6c62e5ec7d33d93efa5ca.tar.gz mason-90bbcceffa1f1051b4e6c62e5ec7d33d93efa5ca.tar.bz2 mason-90bbcceffa1f1051b4e6c62e5ec7d33d93efa5ca.tar.lz mason-90bbcceffa1f1051b4e6c62e5ec7d33d93efa5ca.tar.xz mason-90bbcceffa1f1051b4e6c62e5ec7d33d93efa5ca.tar.zst mason-90bbcceffa1f1051b4e6c62e5ec7d33d93efa5ca.zip | |
feat: add semver module (#1058)
This will be used to compare semver versions, for example:
```lua
local v1 = try(semver.parse("1.0.0"))
local v2 = try(semver.parse("v2.0.0"))
local _ = v1 < v2 -- true
```
Diffstat (limited to 'lua')
| -rw-r--r-- | lua/mason-core/semver.lua | 12 | ||||
| -rw-r--r-- | lua/mason-vendor/semver.lua | 211 |
2 files changed, 223 insertions, 0 deletions
diff --git a/lua/mason-core/semver.lua b/lua/mason-core/semver.lua new file mode 100644 index 00000000..d2b02b40 --- /dev/null +++ b/lua/mason-core/semver.lua @@ -0,0 +1,12 @@ +local semver = require "mason-vendor.semver" +local Result = require "mason-core.result" + +local M = {} + +---@param version string +function M.parse(version) + version = version:gsub("^v", "") + return Result.pcall(semver, version) +end + +return M diff --git a/lua/mason-vendor/semver.lua b/lua/mason-vendor/semver.lua new file mode 100644 index 00000000..b61e3b40 --- /dev/null +++ b/lua/mason-vendor/semver.lua @@ -0,0 +1,211 @@ +-- stylua: ignore start + +local semver = { + _VERSION = '1.2.1', + _DESCRIPTION = 'semver for Lua', + _URL = 'https://github.com/kikito/semver.lua', + _LICENSE = [[ + MIT LICENSE + + Copyright (c) 2015 Enrique GarcĂa Cota + + Permission is hereby granted, free of charge, to any person obtaining a + copy of tother software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and tother permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + ]] +} + +local function checkPositiveInteger(number, name) + assert(number >= 0, name .. ' must be a valid positive number') + assert(math.floor(number) == number, name .. ' must be an integer') +end + +local function present(value) + return value and value ~= '' +end + +-- splitByDot("a.bbc.d") == {"a", "bbc", "d"} +local function splitByDot(str) + str = str or "" + local t, count = {}, 0 + str:gsub("([^%.]+)", function(c) + count = count + 1 + t[count] = c + end) + return t +end + +local function parsePrereleaseAndBuildWithSign(str) + local prereleaseWithSign, buildWithSign = str:match("^(-[^+]+)(+.+)$") + if not (prereleaseWithSign and buildWithSign) then + prereleaseWithSign = str:match("^(-.+)$") + buildWithSign = str:match("^(+.+)$") + end + assert(prereleaseWithSign or buildWithSign, ("The parameter %q must begin with + or - to denote a prerelease or a build"):format(str)) + return prereleaseWithSign, buildWithSign +end + +local function parsePrerelease(prereleaseWithSign) + if prereleaseWithSign then + local prerelease = prereleaseWithSign:match("^-(%w[%.%w-]*)$") + assert(prerelease, ("The prerelease %q is not a slash followed by alphanumerics, dots and slashes"):format(prereleaseWithSign)) + return prerelease + end +end + +local function parseBuild(buildWithSign) + if buildWithSign then + local build = buildWithSign:match("^%+(%w[%.%w-]*)$") + assert(build, ("The build %q is not a + sign followed by alphanumerics, dots and slashes"):format(buildWithSign)) + return build + end +end + +local function parsePrereleaseAndBuild(str) + if not present(str) then return nil, nil end + + local prereleaseWithSign, buildWithSign = parsePrereleaseAndBuildWithSign(str) + + local prerelease = parsePrerelease(prereleaseWithSign) + local build = parseBuild(buildWithSign) + + return prerelease, build +end + +local function parseVersion(str) + local sMajor, sMinor, sPatch, sPrereleaseAndBuild = str:match("^(%d+)%.?(%d*)%.?(%d*)(.-)$") + assert(type(sMajor) == 'string', ("Could not extract version number(s) from %q"):format(str)) + local major, minor, patch = tonumber(sMajor), tonumber(sMinor), tonumber(sPatch) + local prerelease, build = parsePrereleaseAndBuild(sPrereleaseAndBuild) + return major, minor, patch, prerelease, build +end + + +-- return 0 if a == b, -1 if a < b, and 1 if a > b +local function compare(a,b) + return a == b and 0 or a < b and -1 or 1 +end + +local function compareIds(myId, otherId) + if myId == otherId then return 0 + elseif not myId then return -1 + elseif not otherId then return 1 + end + + local selfNumber, otherNumber = tonumber(myId), tonumber(otherId) + + if selfNumber and otherNumber then -- numerical comparison + return compare(selfNumber, otherNumber) + -- numericals are always smaller than alphanums + elseif selfNumber then + return -1 + elseif otherNumber then + return 1 + else + return compare(myId, otherId) -- alphanumerical comparison + end +end + +local function smallerIdList(myIds, otherIds) + local myLength = #myIds + local comparison + + for i=1, myLength do + comparison = compareIds(myIds[i], otherIds[i]) + if comparison ~= 0 then + return comparison == -1 + end + -- if comparison == 0, continue loop + end + + return myLength < #otherIds +end + +local function smallerPrerelease(mine, other) + if mine == other or not mine then return false + elseif not other then return true + end + + return smallerIdList(splitByDot(mine), splitByDot(other)) +end + +local methods = {} + +function methods:nextMajor() + return semver(self.major + 1, 0, 0) +end +function methods:nextMinor() + return semver(self.major, self.minor + 1, 0) +end +function methods:nextPatch() + return semver(self.major, self.minor, self.patch + 1) +end + +local mt = { __index = methods } +function mt:__eq(other) + return self.major == other.major and + self.minor == other.minor and + self.patch == other.patch and + self.prerelease == other.prerelease + -- notice that build is ignored for precedence in semver 2.0.0 +end +function mt:__lt(other) + if self.major ~= other.major then return self.major < other.major end + if self.minor ~= other.minor then return self.minor < other.minor end + if self.patch ~= other.patch then return self.patch < other.patch end + return smallerPrerelease(self.prerelease, other.prerelease) + -- notice that build is ignored for precedence in semver 2.0.0 +end +-- This works like the "pessimisstic operator" in Rubygems. +-- if a and b are versions, a ^ b means "b is backwards-compatible with a" +-- in other words, "it's safe to upgrade from a to b" +function mt:__pow(other) + if self.major == 0 then + return self == other + end + return self.major == other.major and + self.minor <= other.minor +end +function mt:__tostring() + local buffer = { ("%d.%d.%d"):format(self.major, self.minor, self.patch) } + if self.prerelease then table.insert(buffer, "-" .. self.prerelease) end + if self.build then table.insert(buffer, "+" .. self.build) end + return table.concat(buffer) +end + +local function new(major, minor, patch, prerelease, build) + assert(major, "At least one parameter is needed") + + if type(major) == 'string' then + major,minor,patch,prerelease,build = parseVersion(major) + end + patch = patch or 0 + minor = minor or 0 + + checkPositiveInteger(major, "major") + checkPositiveInteger(minor, "minor") + checkPositiveInteger(patch, "patch") + + local result = {major=major, minor=minor, patch=patch, prerelease=prerelease, build=build} + return setmetatable(result, mt) +end + +setmetatable(semver, { __call = function(_, ...) return new(...) end }) +semver._VERSION= semver(semver._VERSION) + +return semver |
