From 48ecb3b53911ffd962294e737163c842165e9bef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Enrique=20Garc=C3=ADa=20Cota?= Date: Sun, 15 Jan 2012 13:30:24 +0100 Subject: [PATCH] prereleases and builds now working 100% like the spec --- semver.lua | 48 ++++++++++++++++++++++++++++++-------------- spec/semver_spec.lua | 39 ++++++++++++++++++++++++++--------- 2 files changed, 63 insertions(+), 24 deletions(-) diff --git a/semver.lua b/semver.lua index 4c03a55..6488f5b 100644 --- a/semver.lua +++ b/semver.lua @@ -19,12 +19,37 @@ end local function parseExtra(extra) if not present(extra) then return end - local sign, data = extra:match("([-+])([%w-][%.%w-]*)") - assert(sign and data, ("The string %q must start with - or + followed by dashes, alphanumerics or dots."):format(extra)) - if sign == "-" then return data, nil end - return nil,data + local prereleaseWithSign, buildWithSign = extra:match("^(-[^+]+)(+.+)$") + if not (prereleaseWithSign and buildWithSign) then + prereleaseWithSign = extra:match("^(-.+)$") + buildWithSign = extra:match("^(+.+)$") + end + assert(prereleaseWithSign or buildWithSign, ("The parameter %q must begin with + or - to denote a prerelease or a buld"):format(extra)) + + local prerelease, build + + if prereleaseWithSign then + prerelease = prereleaseWithSign:match("^-(%w[%.%w-]*)$") + assert(prerelease, ("The prerelease %q is not a slash followed by alphanumerics, dots and slashes"):format(prereleaseWithSign)) + end + + if buildWithSign then + build = buildWithSign:match("^%+(%w[%.%w-]*)$") + assert(build, ("The build %q is not a + sign followed by alphanumerics, dots and slashes"):format(buildWithSign)) + end + + return prerelease, build end +local function parseString(str) + local sMajor, sMinor, sPatch, extra = 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 = parseExtra(extra) + 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 @@ -107,24 +132,19 @@ function mt:__pow(other) end function mt:__tostring() local buffer = { ("%d.%d.%d"):format(self.major, self.minor, self.patch) } - if self.prerelease then buffer[2] = "-" .. self.prerelease - elseif self.build then buffer[2] = "+" .. self.build - end + 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 -- defined as local at the begining of the file -version = function(major, minor, patch, extra) +version = function(major, minor, patch, prerelease, build) assert(major, "At least one parameter is needed") if type(major) == 'string' then - local sMajor, sMinor, sPatch - sMajor, sMinor, sPatch, extra = major:match("^(%d+)%.?(%d*)%.?(%d*)(.-)$") - assert(type(sMajor) == 'string', ("Could not extract version number(s) from %q"):format(major)) - major, minor, patch = tonumber(sMajor), tonumber(sMinor), tonumber(sPatch) + major,minor,patch,prerelease,build = parseString(major) end - patch = patch or 0 minor = minor or 0 @@ -132,8 +152,6 @@ version = function(major, minor, patch, extra) checkPositiveInteger(minor, "minor") checkPositiveInteger(patch, "patch") - local prerelease, build = parseExtra(extra) - local result = {major=major, minor=minor, patch=patch, prerelease=prerelease, build=build} return setmetatable(result, mt) end diff --git a/spec/semver_spec.lua b/spec/semver_spec.lua index 2e81d28..9f39a90 100644 --- a/spec/semver_spec.lua +++ b/spec/semver_spec.lua @@ -26,10 +26,13 @@ context('semver', function() end) it('parses prereleases', function() - checkVersion(v(1,2,3,"-alpha"), 1,2,3,"alpha") + checkVersion(v(1,2,3,"alpha"), 1,2,3,"alpha") end) it('parses builds', function() - checkVersion(v(1,2,3,"+build.1"), 1,2,3,nil,"build.1") + checkVersion(v(1,2,3,nil,"build.1"), 1,2,3,nil,"build.1") + end) + it('parses prereleases + builds', function() + checkVersion(v(1,2,3,"alpha","build.1"), 1,2,3,"alpha","build.1") end) end) @@ -52,6 +55,9 @@ context('semver', function() test("1.2.3+build.15", function() checkVersion( v'1.2.3+build.15', 1,2,3,nil,'build.15' ) end) + test("1.2.3-rc1+build.15", function() + checkVersion( v'1.2.3-rc1+build.15', 1,2,3,'rc1','build.15' ) + end) end) describe('errors', function() @@ -80,6 +86,12 @@ context('semver', function() test('a non-string or number is passed', function() assert_error(function() v({}) end) end) + test('an invalid prerelease', function() + assert_error(function() v'1.2.3-%?' end) + end) + test('an invalid build', function() + assert_error(function() v'1.2.3+%?' end) + end) end) end) @@ -89,10 +101,13 @@ context('semver', function() end) it("works with a prerelease", function() - assert_equal("1.2.3-beta", tostring(v(1,2,3,'-beta'))) + assert_equal("1.2.3-beta", tostring(v(1,2,3,'beta'))) end) it("works with a build", function() - assert_equal("1.2.3+foobar", tostring(v(1,2,3, '+foobar'))) + assert_equal("1.2.3+foobar", tostring(v(1,2,3,nil,'foobar'))) + end) + it("works with a prerelease and a build", function() + assert_equal("1.2.3-alpha+foobar", tostring(v'1.2.3-alpha+foobar')) end) end) @@ -181,6 +196,10 @@ context('semver', function() assert_less_than(v'1.3.7+build.2.b8f12d7', v'1.3.7+build.11.e0f985a') end) end) + test("prereleases + builds", function() + assert_less_than(v'1.0.0-rc.1', v'1.0.0-rc.1+build.1') + assert_less_than(v'1.0.0-rc.1+build.1', v'1.0.0') + end) end) describe("nextPatch", function() @@ -193,8 +212,8 @@ context('semver', function() it("increases the minor number by 1", function() assert_equal(v'1.2.0', v'1.1.0':nextMinor()) end) - it("resets the patch number", function() - assert_equal(v'1.2.0', v'1.1.7':nextMinor()) + it("resets the patch number, prerelease and build", function() + assert_equal(v'1.2.0', v'1.1.7-a+b':nextMinor()) end) end) @@ -202,8 +221,8 @@ context('semver', function() it("increases the major number by 1", function() assert_equal(v'2.0.0', v'1.0.0':nextMajor()) end) - it("resets the patch number", function() - assert_equal(v'2.0.0', v'1.2.3':nextMajor()) + it("resets the minor, patch, prerelease and build", function() + assert_equal(v'2.0.0', v'1.2.3-a+b':nextMajor()) end) end) @@ -220,9 +239,11 @@ context('semver', function() assert_false(v(2,0,0) ^ v(1,0,0)) end) - test("patch versions are always compatible", function() + test("patches, prereleases and builds are ignored", function() assert_true(v(1,2,3) ^ v(1,2,0)) assert_true(v(1,2,3) ^ v(1,2,5)) + assert_true(v(1,2,3,'foo') ^ v(1,2,3)) + assert_true(v(1,2,3,nil,'bar') ^ v(1,2,3)) end) test("it's safe to upgrade to a newer minor version", function()