diff --git a/README.md b/README.md index f67a27b..39693c4 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ versions, can increment versions, etc. Versions used with go-version must follow [SemVer](http://semver.org/). -## Installation and Usage +# Installation and Usage Package documentation can be found on [GoDoc](http://godoc.org/github.com/6543/go-version). @@ -22,7 +22,7 @@ Installation can be done with a normal `go get`: $ go get github.com/6543/go-version ``` -#### Version Parsing and Comparison +# Version Parsing and Comparison ```go v1, err := version.NewVersion("1.2") @@ -35,7 +35,7 @@ if v1.LessThan(v2) { } ``` -#### Version Constraints +# Version Constraints ```go v1, err := version.NewVersion("1.2") @@ -45,9 +45,38 @@ constraints, err := version.NewConstraint(">= 1.0, < 1.4") if constraints.Check(v1) { fmt.Printf("%s satisfies constraints %s", v1, constraints) } + +### Tilde Range Comparisons (Patch) + +The tilde (`~`) comparison operator is for patch level ranges when a minor +version is specified and major level changes when the minor number is missing. +For example, + +* `~1.2.3` is equivalent to `>= 1.2.3, < 1.3.0` +* `~1` is equivalent to `>= 1, < 2` +* `~2.3` is equivalent to `>= 2.3, < 2.4` +* `~1.2.x` is equivalent to `>= 1.2.0, < 1.3.0` +* `~1.x` is equivalent to `>= 1, < 2` + +### Caret Range Comparisons (Major) + +The caret (`^`) comparison operator is for major level changes once a stable +(1.0.0) release has occurred. Prior to a 1.0.0 release the minor versions acts +as the API stability level. This is useful when comparisons of API versions as a +major change is API breaking. For example, + +* `^1.2.3` is equivalent to `>= 1.2.3, < 2.0.0` +* `^1.2.x` is equivalent to `>= 1.2.0, < 2.0.0` +* `^2.3` is equivalent to `>= 2.3, < 3` +* `^2.x` is equivalent to `>= 2.0.0, < 3` +* `^0.2.3` is equivalent to `>=0.2.3 <0.3.0` +* `^0.2` is equivalent to `>=0.2.0 <0.3.0` +* `^0.0.3` is equivalent to `>=0.0.3 <0.0.4` +* `^0.0` is equivalent to `>=0.0.0 <0.1.0` +* `^0` is equivalent to `>=0.0.0 <1.0.0` ``` -#### Version Sorting +# Version Sorting ```go versionsRaw := []string{"1.1", "0.7.1", "1.4-beta", "1.4", "2"} @@ -61,7 +90,7 @@ for i, raw := range versionsRaw { sort.Sort(version.Collection(versions)) ``` -## Issues and Contributing +# Issues and Contributing If you find an issue with this library, please report an issue. If you'd like, we welcome any contributions. Fork this library and submit a pull diff --git a/constraint.go b/constraint.go index a2f638d..6d7ad2e 100644 --- a/constraint.go +++ b/constraint.go @@ -35,6 +35,8 @@ func init() { ">=": constraintGreaterThanEqual, "<=": constraintLessThanEqual, "~>": constraintPessimistic, + "^": constraintCaret, + "~": constraintTilde, } ops := make([]string, 0, len(constraintOperators)) @@ -197,3 +199,31 @@ func constraintPessimistic(v, c *Version) bool { // be valid against the constraint return c.segments[cs-1] <= v.segments[cs-1] } + +func constraintCaret(v, c *Version) bool { + if !prereleaseCheck(v, c) || v.LessThan(c) { + return false + } + + if v.segments[0] != c.segments[0] { + return false + } + + return true +} + +func constraintTilde(v, c *Version) bool { + if !prereleaseCheck(v, c) || v.LessThan(c) { + return false + } + + if v.segments[0] != c.segments[0] { + return false + } + + if c.si > 1 && v.segments[1] != c.segments[1] { + return false + } + + return true +} diff --git a/constraint_test.go b/constraint_test.go index d3721dc..6d1479e 100644 --- a/constraint_test.go +++ b/constraint_test.go @@ -75,6 +75,45 @@ func TestConstraintCheck(t *testing.T) { {">= 2.1.0-a", "2.1.1-beta", false}, {">= 2.1.0-a", "2.1.0", true}, {"<= 2.1.0-a", "2.0.0", true}, + {"^1.1", "1.1.1", true}, + {"^1.1", "1.2.3", true}, + {"^1.1", "2.1.0", false}, + {"^1.1.2", "1.1.1", false}, + {"^1.1.2", "1.1.2", true}, + {"^1.1.2", "1.1.2.3", true}, + {"~1", "1.3.5", true}, + {"~1", "2.1.0", false}, + {"~1.1", "1.1.1", true}, + {"~1.1", "1.2.3", false}, + {"~1.1.2", "1.1.1", false}, + {"~1.1.2", "1.1.2", true}, + {"~1.1.2", "1.1.2.5", true}, + {"~1.1.2", "1.1.2.3", true}, + {"^1.0.0", "1.0.0", true}, + {"^1.0.0", "1.0.1", true}, + {"^1.0.0", "1.9.1", true}, + {"^1.0.0", "2.0.0", false}, + {"^0.1.0", "0.1.0", true}, + {"^0.1.0", "0.2.0", true}, + {"^0.1.0", "0.3.0", true}, + {"^0.1.0", "0.1.4", true}, + {"^0.0.1", "0.0.0", false}, + {"^0.0.1", "0.0.1", true}, + {"^0.0.1", "0.0.2", true}, + {"^0.0.1", "0.2.0", true}, + {"^1.2.3", "1.99.99", true}, + {"^1.2.3", "2.00.00", false}, + {"~1.0.0", "1.0.0", true}, + {"~1.0.0", "1.0.1", true}, + {"~1.0.0", "1.9.1", false}, + {"~1.0.0", "2.0.0", false}, + {"~0.1.0", "0.1.0", true}, + {"~0.1.0", "0.2.0", false}, + {"~0.1.0", "0.1.4", true}, + {"~0.0.1", "0.0.0", false}, + {"~0.0.1", "0.0.1", true}, + {"~0.0.1", "0.0.2", true}, + {"~0.0.1", "0.2.0", false}, } for _, tc := range cases { @@ -91,7 +130,7 @@ func TestConstraintCheck(t *testing.T) { actual := c.Check(v) expected := tc.check if actual != expected { - t.Fatalf("Version: %s\nConstraint: %s\nExpected: %#v", + t.Fatalf("\nVersion: %s\nConstraint: %s\nExpected: %#v", tc.version, tc.constraint, expected) } }