diff --git a/semver.lua b/semver.lua index 998de58..4c03a55 100644 --- a/semver.lua +++ b/semver.lua @@ -16,12 +16,13 @@ local function present(value) return value and value ~= '' end -local function parsePrerelease(extra) +local function parseExtra(extra) if not present(extra) then return end - local prerelease = extra:match("-([%w-][%.%w-]*)") - assert(prerelease, ("The prerelease %q must start with a dash and be followed by dashes, alphanumerics or dots."):format(extra)) - return prerelease + 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 end -- return 0 if a == b, -1 if a < b, and 1 if a > b @@ -88,14 +89,17 @@ function mt:__eq(other) return self.major == other.major and self.minor == other.minor and self.patch == other.patch and - self.prerelease == other.prerelease + self.prerelease == other.prerelease and + self.build == other.build end function mt:__lt(other) return self.major < other.major or self.minor < other.minor or self.patch < other.patch or (self.prerelease and not other.prerelease) or - smallerExtra(self.prerelease, other.prerelease) + smallerExtra(self.prerelease, other.prerelease) or + (not self.build and other.build) or + smallerExtra(self.build, other.build) end function mt:__pow(other) return self.major == other.major and @@ -103,7 +107,9 @@ function mt:__pow(other) 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.prerelease then buffer[2] = "-" .. self.prerelease + elseif self.build then buffer[2] = "+" .. self.build + end return table.concat(buffer) end @@ -126,9 +132,9 @@ version = function(major, minor, patch, extra) checkPositiveInteger(minor, "minor") checkPositiveInteger(patch, "patch") - local prerelease = parsePrerelease(extra) + local prerelease, build = parseExtra(extra) - local result = {major=major, minor=minor, patch=patch, prerelease=prerelease} + 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 a6570ba..2e81d28 100644 --- a/spec/semver_spec.lua +++ b/spec/semver_spec.lua @@ -1,10 +1,11 @@ local v = require 'semver' -local function checkVersion(ver, major, minor, patch, prerelease) +local function checkVersion(ver, major, minor, patch, prerelease, build) assert_equal(major, ver.major) assert_equal(minor, ver.minor) assert_equal(patch, ver.patch) assert_equal(prerelease, ver.prerelease) + assert_equal(build, ver.build) end context('semver', function() @@ -24,9 +25,12 @@ context('semver', function() checkVersion(v(1), 1,0,0) end) - it('parses prereleases, if they exist', function() + it('parses prereleases', function() 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") + end) end) describe("from strings", function() @@ -45,6 +49,9 @@ context('semver', function() test("1.2.3-alpha", function() checkVersion( v'1.2.3-alpha', 1,2,3,'alpha' ) end) + test("1.2.3+build.15", function() + checkVersion( v'1.2.3+build.15', 1,2,3,nil,'build.15' ) + end) end) describe('errors', function() @@ -84,6 +91,9 @@ context('semver', function() it("works with a prerelease", function() 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'))) + end) end) describe("==", function() @@ -96,6 +106,9 @@ context('semver', function() it("false if all is the same except the prerelease", function() assert_not_equal(v(1,2,3), v'1.2.3-alpha') end) + it("false if all is the same except the build", function() + assert_not_equal(v(1,2,3), v'1.2.3+peter.1') + end) end) describe("<", function() @@ -143,6 +156,31 @@ context('semver', function() assert_less_than(v'1.0.0-beta.11', v'1.0.0-rc.1') end) end) + describe("builds", function() + test("false if exact same build", function() + assert_false(v'1.0.0+build1' < v'1.0.0+build1') + end) + test("a regular (not-build) version is always less than a build version", function() + assert_less_than(v'1.0.0', v'1.0.0+12') + end) + test("identifiers with only digits are compared numerically", function() + assert_less_than(v'1.0.0+1', v'1.0.0+2') + assert_less_than(v'1.0.0+2', v'1.0.0+10') + end) + test("idendifiers with letters or dashes are compared lexiconumerically", function() + assert_less_than(v'1.0.0+build1', v'1.0.0+build2') + assert_less_than(v'1.0.0+build10', v'1.0.0+build2') + end) + test("numerical ids always have less priority than lexiconumericals", function() + assert_less_than(v'1.0.0+1', v'1.0.0+build1') + assert_less_than(v'1.0.0+2', v'1.0.0+1build') + end) + test("identifiers can be separated by colons; they must be compared individually", function() + assert_less_than(v'1.0.0+0.3.7', v'1.3.7+build') + assert_less_than(v'1.3.7+build', v'1.3.7+build.2.b8f12d7') + assert_less_than(v'1.3.7+build.2.b8f12d7', v'1.3.7+build.11.e0f985a') + end) + end) end) describe("nextPatch", function()