232 lines
		
	
	
	
		
			8.2 KiB
		
	
	
	
		
			Lua
		
	
	
	
	
	
			
		
		
	
	
			232 lines
		
	
	
	
		
			8.2 KiB
		
	
	
	
		
			Lua
		
	
	
	
	
	
| --[[
 | |
| Checkout semver.lua semantic versioning library for LUA
 | |
| https://github.com/kikito/semver.lua
 | |
| 
 | |
| Copyright (c) 2011 Enrique García Cota
 | |
| 
 | |
| MIT LICENSE
 | |
| Permission is hereby granted, free of charge, to any person obtaining a
 | |
| copy of this 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 this 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.
 | |
| ]]
 | |
| 
 | |
| 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
 | 
