diff --git a/README.md b/README.md index 6efcc43..e0325f2 100644 --- a/README.md +++ b/README.md @@ -128,12 +128,25 @@ greater than or equal to 4.2.3. The basic comparisons are: * `=`: equal (aliased to no operator) +* `==`: strictly equal * `!=`: not equal * `>`: greater than * `<`: less than * `>=`: greater than or equal to * `<=`: less than or equal to +### Strict Comparisons + +The double equal (`==`) operator is used to check if a version is really strictly equal to +the passed version. +These look like: + +* `==1.2.1` will only look for a `1.2.1` version, metadatas or preleased versions will be considered different from this version. +* `==1.2.1-alpha` will only look for a `1.2.1-alpha` preleased version +* `==1.2.1+alpha` will only look for a `1.2.1-alpha` preleased version + +> This helps looking for a very specific version only, the `=` operator (or the no operator) will be ok with metadatas in the version in the first place, ex: `=1.2.1` will be equivalent to `=1.2.1+x` where `x` can be any medatata. + ### Working With Prerelease Versions Pre-releases, for those not familiar with them, are used for software releases @@ -177,7 +190,7 @@ parsed as a single constraint `1.2.0` with _prerelease_ `1.4.5`. ### Wildcards In Comparisons The `x`, `X`, and `*` characters can be used as a wildcard character. This works -for all comparison operators. When used on the `=` operator it falls +for all comparison operators except the strict operators. When used on the `=` operator it falls back to the patch level comparison (see tilde below). For example, * `1.2.x` is equivalent to `>= 1.2.0, < 1.3.0` diff --git a/constraints.go b/constraints.go index 8461c7e..6950442 100644 --- a/constraints.go +++ b/constraints.go @@ -17,7 +17,6 @@ type Constraints struct { // NewConstraint returns a Constraints instance that a Version instance can // be checked against. If there is a parse error it will be returned. func NewConstraint(c string) (*Constraints, error) { - // Rewrite - ranges into a comparison operation. c = rewriteRange(c) @@ -97,7 +96,6 @@ func (cs Constraints) Validate(v *Version) (bool, []error) { joy = false } else { - if _, err := c.check(v); err != nil { e = append(e, err) joy = false @@ -151,9 +149,11 @@ func (cs Constraints) MarshalText() ([]byte, error) { return []byte(cs.String()), nil } -var constraintOps map[string]cfunc -var constraintRegex *regexp.Regexp -var constraintRangeRegex *regexp.Regexp +var ( + constraintOps map[string]cfunc + constraintRegex *regexp.Regexp + constraintRangeRegex *regexp.Regexp +) // Used to find individual constraints within a multi-constraint string var findConstraintRegex *regexp.Regexp @@ -174,6 +174,7 @@ func init() { "<": constraintLessThan, ">=": constraintGreaterThanEqual, "=>": constraintGreaterThanEqual, + "==": constraintEqual, "<=": constraintLessThanEqual, "=<": constraintLessThanEqual, "~": constraintTilde, @@ -181,7 +182,7 @@ func init() { "^": constraintCaret, } - ops := `=||!=|>|<|>=|=>|<=|=<|~|~>|\^` + ops := `=||!=|==|>|<|>=|=>|<=|=<|~|~>|\^` constraintRegex = regexp.MustCompile(fmt.Sprintf( `^\s*(%s)\s*(%s)\s*$`, @@ -269,7 +270,6 @@ func parseConstraint(c string) (*constraint, error) { con, err := NewVersion(ver) if err != nil { - // The constraintRegex should catch any regex parsing errors. So, // we should never get here. return nil, errors.New("constraint Parser Error") @@ -287,7 +287,6 @@ func parseConstraint(c string) (*constraint, error) { // is equivalent to * or >=0.0.0 con, err := StrictNewVersion("0.0.0") if err != nil { - // The constraintRegex should catch any regex parsing errors. So, // we should never get here. return nil, errors.New("constraint Parser Error") @@ -307,7 +306,6 @@ func parseConstraint(c string) (*constraint, error) { // Constraint functions func constraintNotEqual(v *Version, c *constraint) (bool, error) { if c.dirty { - // If there is a pre-release on the version but the constraint isn't looking // for them assume that pre-releases are not compatible. See issue 21 for // more details. @@ -345,8 +343,17 @@ func constraintNotEqual(v *Version, c *constraint) (bool, error) { return true, nil } -func constraintGreaterThan(v *Version, c *constraint) (bool, error) { +// Constraint functions +func constraintEqual(v *Version, c *constraint) (bool, error) { + eq := v.Original() == c.orig + if eq { + return true, nil + } + + return false, fmt.Errorf("%s is not equal to %s", v, c.orig) +} +func constraintGreaterThan(v *Version, c *constraint) (bool, error) { // If there is a pre-release on the version but the constraint isn't looking // for them assume that pre-releases are not compatible. See issue 21 for // more details. @@ -407,7 +414,6 @@ func constraintLessThan(v *Version, c *constraint) (bool, error) { } func constraintGreaterThanEqual(v *Version, c *constraint) (bool, error) { - // If there is a pre-release on the version but the constraint isn't looking // for them assume that pre-releases are not compatible. See issue 21 for // more details. diff --git a/constraints_test.go b/constraints_test.go index f519a48..d1f1c59 100644 --- a/constraints_test.go +++ b/constraints_test.go @@ -17,6 +17,8 @@ func TestParseConstraint(t *testing.T) { }{ {">= 1.2", constraintGreaterThanEqual, "1.2.0", false}, {"1.0", constraintTildeOrEqual, "1.0.0", false}, + {"==1.0", constraintEqual, "1.0.0", false}, + {"==1", constraintEqual, "1.0.0", false}, {"foo", nil, "", true}, {"<= 1.2", constraintLessThanEqual, "1.2.0", false}, {"=< 1.2", constraintLessThanEqual, "1.2.0", false}, @@ -138,6 +140,23 @@ func TestConstraintCheck(t *testing.T) { {"2", "2.1.1", true}, {"2.1", "2.1.1", true}, {"2.1", "2.2.1", false}, + {"==2", "1", false}, + {"==2", "3.4.5", false}, + {"==2", "2.0.0", false}, + {"==2", "2.0.0+alpha", false}, + {"==2", "2.0.0-alpha", false}, + {"==2", "2.0.1", false}, + {"==2.1", "2.1.0", false}, + {"==2.1.x", "2.1.0", false}, + {"==2.1.x", "2.1.1", false}, + {"==2.1", "2.1.1", false}, + {"==2.1", "2.2.1", false}, + {"==2.1.1", "2.1.1", true}, + {"==2.1.1", "2.2.1", false}, + {"==2.1.1+alpha", "2.1.1+alpha.1", false}, + {"==2.1.1+alpha.1", "2.1.1+alpha.1", true}, + {"==2.1.1-alpha", "2.1.1-alpha.1", false}, + {"==2.1.1-alpha.1", "2.1.1-alpha.1", true}, {"~1.2.3", "1.2.4", true}, {"~1.2.3", "1.3.4", false}, {"~1.2", "1.2.4", true}, @@ -290,7 +309,10 @@ func TestConstraintsCheck(t *testing.T) { {"= 2.0", "1.2.3", false}, {"= 2.0", "2.0.0", true}, {"4.1", "4.1.0", true}, + {"4.1", "4.1.3+alpha", true}, {"4.1.x", "4.1.3", true}, + {"4.1.x", "4.1.3+alpha", true}, + {"4.1.3", "4.1.3+alpha", true}, {"1.x", "1.4", true}, {"!=4.1", "4.1.0", false}, {"!=4.1-alpha", "4.1.0-alpha", false}, @@ -474,7 +496,10 @@ func TestConstraintsValidate(t *testing.T) { {"= 2.0", "1.2.3", false}, {"= 2.0", "2.0.0", true}, {"4.1", "4.1.0", true}, + {"4.1", "4.1.3+alpha", true}, {"4.1.x", "4.1.3", true}, + {"4.1.x", "4.1.3+alpha", true}, + {"4.1.3", "4.1.3+alpha", true}, {"1.x", "1.4", true}, {"!=4.1", "4.1.0", false}, {"!=4.1", "5.1.0", true}, @@ -482,6 +507,16 @@ func TestConstraintsValidate(t *testing.T) { {"!=4.x", "4.1.0", false}, {"!=4.1.x", "4.2.0", true}, {"!=4.2.x", "4.2.3", false}, + {"==4.1", "4.1.0", false}, + {"==4.1", "5.1.0", false}, + {"==4.x", "5.1.0", false}, + {"==4.x", "4.1.0", false}, + {"==4.1.x", "4.2.0", false}, + {"==4.1.0", "4.1.0", true}, + {"==4.1.0+alpha", "4.1.0+alpha", true}, + {"==4.1.0+alpha", "4.1.0+alpha-1", false}, + {"==4.1.0-alpha", "4.1.0-alpha-1", false}, + {"==4.2.x", "4.2.3", false}, {">1.1", "4.1.0", true}, {">1.1", "1.1.0", false}, {"<1.1", "0.1.0", true}, @@ -600,6 +635,15 @@ func TestConstraintsValidate(t *testing.T) { {"= 2.0", "1.2.3", "1.2.3 is less than 2.0"}, {"!=4.1", "4.1.0", "4.1.0 is equal to 4.1"}, {"!=4.x", "4.1.0", "4.1.0 is equal to 4.x"}, + {"==4.x", "5.1.0", "5.1.0 is not equal to 4.x"}, + {"==4.x", "4.1.0", "4.1.0 is not equal to 4.x"}, + {"==4.1.x", "4.1.0", "4.1.0 is not equal to 4.1.x"}, + {"==4.1.x", "4.2.0", "4.2.0 is not equal to 4.1.x"}, + {"==4.1.2", "4.1.3", "4.1.3 is not equal to 4.1.2"}, + {"==4.1.2-beta", "4.1.2-beta.1", "4.1.2-beta.1 is not equal to 4.1.2-beta"}, + {"==4.1.2+beta", "4.1.2+beta.1", "4.1.2+beta.1 is not equal to 4.1.2+beta"}, + {"==4.0.1", "4.0.1-beta.1", "4.0.1-beta.1 is a prerelease version and the constraint is only looking for release versions"}, + {"==4.2", "4.1.0", "4.1.0 is not equal to 4.2"}, {"!=4.2.x", "4.2.3", "4.2.3 is equal to 4.2.x"}, {">1.1", "1.1.0", "1.1.0 is less than or equal to 1.1"}, {"<1.1", "1.1.0", "1.1.0 is greater than or equal to 1.1"},