From 31e139ca8db93215016019fe52b2c0f0ddb0ba49 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Thu, 26 May 2022 16:02:38 +0200 Subject: [PATCH 1/4] Slightly improve error messages --- constraints.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/constraints.go b/constraints.go index ce882ae..b8abd96 100644 --- a/constraints.go +++ b/constraints.go @@ -53,7 +53,7 @@ func ParseConstraint(in string) (Constraint, error) { n := peek() if !isIdentifier(n) && !isVersionSeparator(n) { if start == curr { - return nil, fmt.Errorf("invalid version") + return nil, fmt.Errorf("invalid version: unexpected char '%c'", rune(n)) } return Parse(in[start:curr]) } @@ -80,7 +80,7 @@ func ParseConstraint(in string) (Constraint, error) { } skipSpace() if c := next(); c != ')' { - return nil, fmt.Errorf("unexpected char at: %s", in[curr-1:]) + return nil, fmt.Errorf("unexpected char: %c", rune(c)) } return expr, nil case '=': From 7d77b128cd91c35231c9327049e83ae294530fd8 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Thu, 26 May 2022 16:04:24 +0200 Subject: [PATCH 2/4] Consider compatible-with `^` constraint implicit if operator is missing --- README.md | 32 ++++++++++++++++++-------------- constraints.go | 16 +++++++++++++--- constraints_test.go | 37 +++++++++++++++++++++++++++++++------ 3 files changed, 62 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index 109fa2b..f1250a7 100644 --- a/README.md +++ b/README.md @@ -39,20 +39,22 @@ To parse a `RelaxedVersion` you can use the `ParseRelaxed` function. Dependency version matching can be specified via version constraints, which might be a version range or an exact version. -The following operators are supported: - -| | | -| -------- | ------------------------ | -| `=` | equal to | -| `>` | greater than | -| `>=` | greater than or equal to | -| `<` | less than | -| `<=` | less than or equal to | -| `^` | compatible-with | -| `!` | NOT | -| `&&` | AND | -| `\|\|` | OR | -| `(`, `)` | constraint group | +The following range operators are supported: + +| Operator | Meaning | +| ----------- | ------------------------ | +| `^` or None | compatible-with | +| `=` | equal to | +| `>` | greater than | +| `>=` | greater than or equal to | +| `<` | less than | +| `<=` | less than or equal to | +| `!` | NOT | +| `&&` | AND | +| `\|\|` | OR | +| `(`, `)` | constraint group | + +In a constraint if a version is written without the operator the "compatible-with" operator is applied. ### Examples @@ -82,6 +84,8 @@ constraints conditions would match as follows: | `<1.0.0 \|\| >2.0.0` | `0.1.0`, `0.1.1`, `0.2.0`, `2.0.5`, `2.0.6`, `2.1.0`, `3.0.0` | | `(>0.1.0 && <2.0.0) \|\| >2.0.5` | `0.1.1`, `0.2.0`, `1.0.0`, `2.0.6`, `2.1.0`, `3.0.0` | | `^2.0.5` | `2.0.5`, `2.0.6`, `2.1.0` | +| `2.0.5` | `2.0.5`, `2.0.6`, `2.1.0` (same as `^2.0.5`) | +| `2.0.5 && !=2.0.6` | `2.0.5`, `2.1.0` (same as `^2.0.5 && !=2.0.6`) | | `^0.1.0` | `0.1.0`, `0.1.1` | ## Json parsable diff --git a/constraints.go b/constraints.go index b8abd96..4c4ccf7 100644 --- a/constraints.go +++ b/constraints.go @@ -57,7 +57,7 @@ func ParseConstraint(in string) (Constraint, error) { } return Parse(in[start:curr]) } - curr++ + next() } } @@ -66,14 +66,16 @@ func ParseConstraint(in string) (Constraint, error) { terminal = func() (Constraint, error) { skipSpace() - switch next() { + switch peek() { case '!': + next() expr, err := terminal() if err != nil { return nil, err } return &Not{expr}, nil case '(': + next() expr, err := constraint() if err != nil { return nil, err @@ -84,18 +86,21 @@ func ParseConstraint(in string) (Constraint, error) { } return expr, nil case '=': + next() v, err := version() if err != nil { return nil, err } return &Equals{v}, nil case '^': + next() v, err := version() if err != nil { return nil, err } return &CompatibleWith{v}, nil case '>': + next() if peek() == '=' { next() v, err := version() @@ -111,6 +116,7 @@ func ParseConstraint(in string) (Constraint, error) { return &GreaterThan{v}, nil } case '<': + next() if peek() == '=' { next() v, err := version() @@ -126,7 +132,11 @@ func ParseConstraint(in string) (Constraint, error) { return &LessThan{v}, nil } default: - return nil, fmt.Errorf("unexpected char at: %s", in[curr-1:]) + v, err := version() + if err != nil { + return nil, err + } + return &CompatibleWith{v}, nil } } diff --git a/constraints_test.go b/constraints_test.go index cf6adab..510cec5 100644 --- a/constraints_test.go +++ b/constraints_test.go @@ -99,9 +99,14 @@ func TestConstraintsParser(t *testing.T) { good := []goodStringTest{ {"", ""}, // always true {"=1.3.0", "=1.3.0"}, + {"1.3.0", "^1.3.0"}, + {"!1.0.0", "!(^1.0.0)"}, {" =1.3.0 ", "=1.3.0"}, + {" 1.3.0 ", "^1.3.0"}, {"=1.3.0 ", "=1.3.0"}, + {"1.3.0 ", "^1.3.0"}, {" =1.3.0", "=1.3.0"}, + {" 1.3.0", "^1.3.0"}, {">=1.3.0", ">=1.3.0"}, {">1.3.0", ">1.3.0"}, {"<=1.3.0", "<=1.3.0"}, @@ -111,16 +116,35 @@ func TestConstraintsParser(t *testing.T) { {"^1.3.0 ", "^1.3.0"}, {" ^1.3.0 ", "^1.3.0"}, {"(=1.4.0)", "=1.4.0"}, + {"(1.4.0)", "^1.4.0"}, {"!(=1.4.0)", "!(=1.4.0)"}, + {"!(1.4.0)", "!(^1.4.0)"}, {"!(((=1.4.0)))", "!(=1.4.0)"}, + {"!(((1.4.0)))", "!(^1.4.0)"}, {"=1.2.4 && =1.3.0", "(=1.2.4 && =1.3.0)"}, {"=1.2.4 && ^1.3.0", "(=1.2.4 && ^1.3.0)"}, + {"1.2.4 && 1.3.0", "(^1.2.4 && ^1.3.0)"}, {"=1.2.4 && =1.3.0 && =1.2.0", "(=1.2.4 && =1.3.0 && =1.2.0)"}, + {"1.2.4 && 1.3.0 && 1.2.0", "(^1.2.4 && ^1.3.0 && ^1.2.0)"}, {"=1.2.4 && =1.3.0 || =1.2.0", "((=1.2.4 && =1.3.0) || =1.2.0)"}, + {"1.2.4 && 1.3.0 || 1.2.0", "((^1.2.4 && ^1.3.0) || ^1.2.0)"}, {"=1.2.4 || =1.3.0 && =1.2.0", "(=1.2.4 || (=1.3.0 && =1.2.0))"}, {"(=1.2.4 || =1.3.0) && =1.2.0", "((=1.2.4 || =1.3.0) && =1.2.0)"}, + {"(1.2.4 || 1.3.0) && 1.2.0", "((^1.2.4 || ^1.3.0) && ^1.2.0)"}, {"(=1.2.4 || !>1.3.0) && =1.2.0", "((=1.2.4 || !(>1.3.0)) && =1.2.0)"}, + {"(1.2.4 || !>1.3.0) && 1.2.0", "((^1.2.4 || !(>1.3.0)) && ^1.2.0)"}, {"!(=1.2.4 || >1.3.0) && =1.2.0", "(!(=1.2.4 || >1.3.0) && =1.2.0)"}, + {"!(1.2.4 || >1.3.0) && 1.2.0", "(!(^1.2.4 || >1.3.0) && ^1.2.0)"}, + {">1.0.0 && 2.0.0", "(>1.0.0 && ^2.0.0)"}, + {">1.0.0 && =2.0.0", "(>1.0.0 && =2.0.0)"}, + {">1.0.0 || 2.0.0", "(>1.0.0 || ^2.0.0)"}, + {">1.0.0 || =2.0.0", "(>1.0.0 || =2.0.0)"}, + {"(>1.0.0) || 2.0.0", "(>1.0.0 || ^2.0.0)"}, + {"(>1.0.0) || =2.0.0", "(>1.0.0 || =2.0.0)"}, + {">1.0.0 || (2.0.0)", "(>1.0.0 || ^2.0.0)"}, + {">1.0.0 || (=2.0.0)", "(>1.0.0 || =2.0.0)"}, + {"((>1.0.0) || (2.0.0))", "(>1.0.0 || ^2.0.0)"}, + {"((>1.0.0) || (=2.0.0))", "(>1.0.0 || =2.0.0)"}, } for i, test := range good { in := test.In @@ -134,7 +158,6 @@ func TestConstraintsParser(t *testing.T) { } bad := []string{ - "1.0.0", "= 1.0.0", ">= 1.0.0", "> 1.0.0", @@ -144,19 +167,21 @@ func TestConstraintsParser(t *testing.T) { ">1.0.0 =2.0.0", ">1.0.0 &", "^1.1.1.1", - "!1.0.0", - ">1.0.0 && 2.0.0", ">1.0.0 | =2.0.0", "(>1.0.0 | =2.0.0)", "(>1.0.0 || =2.0.0", - ">1.0.0 || 2.0.0", + "!1.0.0.0", + "!1.0.0 && !1.0.0.0", + "(!1.0.0 && !1.0.0", + "!1.0.0 || !1.0.0.0", + "(!1.0.0 || !1.0.0", } for i, s := range bad { in := s t.Run(fmt.Sprintf("BadString%03d", i), func(t *testing.T) { p, err := ParseConstraint(in) - require.Nil(t, p) - require.Error(t, err) + require.Nil(t, p, "parsing: %s", in) + require.Error(t, err, "parsing: %s", in) fmt.Printf("'%s' parse error: %s\n", in, err) }) } From 7bfe3364b9da4e741c0674d1340f7b3ccd51b0c9 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Sun, 29 May 2022 17:45:48 +0200 Subject: [PATCH 3/4] Fix markdown lint warning --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f1250a7..949ed6d 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ A library for handling a superset of semantic versioning in golang. ## Documentation and examples -See the godoc here: https://godoc.org/go.bug.st/relaxed-semver +See the godoc here: ## Semantic versioning specification followed in this library From a13eb20b8732774d8698ecd633b336484cca56aa Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Wed, 15 Mar 2023 13:58:38 +0100 Subject: [PATCH 4/4] Added more tests --- constraints_test.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/constraints_test.go b/constraints_test.go index 510cec5..b73e1ce 100644 --- a/constraints_test.go +++ b/constraints_test.go @@ -99,6 +99,9 @@ func TestConstraintsParser(t *testing.T) { good := []goodStringTest{ {"", ""}, // always true {"=1.3.0", "=1.3.0"}, + {"!=1.3.0", "!(=1.3.0)"}, + {" !=1.3.0", "!(=1.3.0)"}, + {"! =1.3.0", "!(=1.3.0)"}, {"1.3.0", "^1.3.0"}, {"!1.0.0", "!(^1.0.0)"}, {" =1.3.0 ", "=1.3.0"}, @@ -143,6 +146,8 @@ func TestConstraintsParser(t *testing.T) { {"(>1.0.0) || =2.0.0", "(>1.0.0 || =2.0.0)"}, {">1.0.0 || (2.0.0)", "(>1.0.0 || ^2.0.0)"}, {">1.0.0 || (=2.0.0)", "(>1.0.0 || =2.0.0)"}, + {"!>1.0.0 || (=2.0.0)", "(!(>1.0.0) || =2.0.0)"}, + {"!(>1.0.0 || =2.0.0)", "!(>1.0.0 || =2.0.0)"}, {"((>1.0.0) || (2.0.0))", "(>1.0.0 || ^2.0.0)"}, {"((>1.0.0) || (=2.0.0))", "(>1.0.0 || =2.0.0)"}, } @@ -159,6 +164,7 @@ func TestConstraintsParser(t *testing.T) { bad := []string{ "= 1.0.0", + "!= 1.3.9", ">= 1.0.0", "> 1.0.0", "<= 1.0.0",