Skip to content

Commit 7d77b12

Browse files
committed
Consider compatible-with ^ constraint implicit if operator is missing
1 parent 31e139c commit 7d77b12

File tree

3 files changed

+62
-23
lines changed

3 files changed

+62
-23
lines changed

README.md

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -39,20 +39,22 @@ To parse a `RelaxedVersion` you can use the `ParseRelaxed` function.
3939

4040
Dependency version matching can be specified via version constraints, which might be a version range or an exact version.
4141

42-
The following operators are supported:
43-
44-
| | |
45-
| -------- | ------------------------ |
46-
| `=` | equal to |
47-
| `>` | greater than |
48-
| `>=` | greater than or equal to |
49-
| `<` | less than |
50-
| `<=` | less than or equal to |
51-
| `^` | compatible-with |
52-
| `!` | NOT |
53-
| `&&` | AND |
54-
| `\|\|` | OR |
55-
| `(`, `)` | constraint group |
42+
The following range operators are supported:
43+
44+
| Operator | Meaning |
45+
| ----------- | ------------------------ |
46+
| `^` or None | compatible-with |
47+
| `=` | equal to |
48+
| `>` | greater than |
49+
| `>=` | greater than or equal to |
50+
| `<` | less than |
51+
| `<=` | less than or equal to |
52+
| `!` | NOT |
53+
| `&&` | AND |
54+
| `\|\|` | OR |
55+
| `(`, `)` | constraint group |
56+
57+
In a constraint if a version is written without the operator the "compatible-with" operator is applied.
5658

5759
### Examples
5860

@@ -82,6 +84,8 @@ constraints conditions would match as follows:
8284
| `<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` |
8385
| `(>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` |
8486
| `^2.0.5` | `2.0.5`, `2.0.6`, `2.1.0` |
87+
| `2.0.5` | `2.0.5`, `2.0.6`, `2.1.0` (same as `^2.0.5`) |
88+
| `2.0.5 && !=2.0.6` | `2.0.5`, `2.1.0` (same as `^2.0.5 && !=2.0.6`) |
8589
| `^0.1.0` | `0.1.0`, `0.1.1` |
8690

8791
## Json parsable

constraints.go

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ func ParseConstraint(in string) (Constraint, error) {
5757
}
5858
return Parse(in[start:curr])
5959
}
60-
curr++
60+
next()
6161
}
6262
}
6363

@@ -66,14 +66,16 @@ func ParseConstraint(in string) (Constraint, error) {
6666

6767
terminal = func() (Constraint, error) {
6868
skipSpace()
69-
switch next() {
69+
switch peek() {
7070
case '!':
71+
next()
7172
expr, err := terminal()
7273
if err != nil {
7374
return nil, err
7475
}
7576
return &Not{expr}, nil
7677
case '(':
78+
next()
7779
expr, err := constraint()
7880
if err != nil {
7981
return nil, err
@@ -84,18 +86,21 @@ func ParseConstraint(in string) (Constraint, error) {
8486
}
8587
return expr, nil
8688
case '=':
89+
next()
8790
v, err := version()
8891
if err != nil {
8992
return nil, err
9093
}
9194
return &Equals{v}, nil
9295
case '^':
96+
next()
9397
v, err := version()
9498
if err != nil {
9599
return nil, err
96100
}
97101
return &CompatibleWith{v}, nil
98102
case '>':
103+
next()
99104
if peek() == '=' {
100105
next()
101106
v, err := version()
@@ -111,6 +116,7 @@ func ParseConstraint(in string) (Constraint, error) {
111116
return &GreaterThan{v}, nil
112117
}
113118
case '<':
119+
next()
114120
if peek() == '=' {
115121
next()
116122
v, err := version()
@@ -126,7 +132,11 @@ func ParseConstraint(in string) (Constraint, error) {
126132
return &LessThan{v}, nil
127133
}
128134
default:
129-
return nil, fmt.Errorf("unexpected char at: %s", in[curr-1:])
135+
v, err := version()
136+
if err != nil {
137+
return nil, err
138+
}
139+
return &CompatibleWith{v}, nil
130140
}
131141
}
132142

constraints_test.go

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -99,9 +99,14 @@ func TestConstraintsParser(t *testing.T) {
9999
good := []goodStringTest{
100100
{"", ""}, // always true
101101
{"=1.3.0", "=1.3.0"},
102+
{"1.3.0", "^1.3.0"},
103+
{"!1.0.0", "!(^1.0.0)"},
102104
{" =1.3.0 ", "=1.3.0"},
105+
{" 1.3.0 ", "^1.3.0"},
103106
{"=1.3.0 ", "=1.3.0"},
107+
{"1.3.0 ", "^1.3.0"},
104108
{" =1.3.0", "=1.3.0"},
109+
{" 1.3.0", "^1.3.0"},
105110
{">=1.3.0", ">=1.3.0"},
106111
{">1.3.0", ">1.3.0"},
107112
{"<=1.3.0", "<=1.3.0"},
@@ -111,16 +116,35 @@ func TestConstraintsParser(t *testing.T) {
111116
{"^1.3.0 ", "^1.3.0"},
112117
{" ^1.3.0 ", "^1.3.0"},
113118
{"(=1.4.0)", "=1.4.0"},
119+
{"(1.4.0)", "^1.4.0"},
114120
{"!(=1.4.0)", "!(=1.4.0)"},
121+
{"!(1.4.0)", "!(^1.4.0)"},
115122
{"!(((=1.4.0)))", "!(=1.4.0)"},
123+
{"!(((1.4.0)))", "!(^1.4.0)"},
116124
{"=1.2.4 && =1.3.0", "(=1.2.4 && =1.3.0)"},
117125
{"=1.2.4 && ^1.3.0", "(=1.2.4 && ^1.3.0)"},
126+
{"1.2.4 && 1.3.0", "(^1.2.4 && ^1.3.0)"},
118127
{"=1.2.4 && =1.3.0 && =1.2.0", "(=1.2.4 && =1.3.0 && =1.2.0)"},
128+
{"1.2.4 && 1.3.0 && 1.2.0", "(^1.2.4 && ^1.3.0 && ^1.2.0)"},
119129
{"=1.2.4 && =1.3.0 || =1.2.0", "((=1.2.4 && =1.3.0) || =1.2.0)"},
130+
{"1.2.4 && 1.3.0 || 1.2.0", "((^1.2.4 && ^1.3.0) || ^1.2.0)"},
120131
{"=1.2.4 || =1.3.0 && =1.2.0", "(=1.2.4 || (=1.3.0 && =1.2.0))"},
121132
{"(=1.2.4 || =1.3.0) && =1.2.0", "((=1.2.4 || =1.3.0) && =1.2.0)"},
133+
{"(1.2.4 || 1.3.0) && 1.2.0", "((^1.2.4 || ^1.3.0) && ^1.2.0)"},
122134
{"(=1.2.4 || !>1.3.0) && =1.2.0", "((=1.2.4 || !(>1.3.0)) && =1.2.0)"},
135+
{"(1.2.4 || !>1.3.0) && 1.2.0", "((^1.2.4 || !(>1.3.0)) && ^1.2.0)"},
123136
{"!(=1.2.4 || >1.3.0) && =1.2.0", "(!(=1.2.4 || >1.3.0) && =1.2.0)"},
137+
{"!(1.2.4 || >1.3.0) && 1.2.0", "(!(^1.2.4 || >1.3.0) && ^1.2.0)"},
138+
{">1.0.0 && 2.0.0", "(>1.0.0 && ^2.0.0)"},
139+
{">1.0.0 && =2.0.0", "(>1.0.0 && =2.0.0)"},
140+
{">1.0.0 || 2.0.0", "(>1.0.0 || ^2.0.0)"},
141+
{">1.0.0 || =2.0.0", "(>1.0.0 || =2.0.0)"},
142+
{"(>1.0.0) || 2.0.0", "(>1.0.0 || ^2.0.0)"},
143+
{"(>1.0.0) || =2.0.0", "(>1.0.0 || =2.0.0)"},
144+
{">1.0.0 || (2.0.0)", "(>1.0.0 || ^2.0.0)"},
145+
{">1.0.0 || (=2.0.0)", "(>1.0.0 || =2.0.0)"},
146+
{"((>1.0.0) || (2.0.0))", "(>1.0.0 || ^2.0.0)"},
147+
{"((>1.0.0) || (=2.0.0))", "(>1.0.0 || =2.0.0)"},
124148
}
125149
for i, test := range good {
126150
in := test.In
@@ -134,7 +158,6 @@ func TestConstraintsParser(t *testing.T) {
134158
}
135159

136160
bad := []string{
137-
"1.0.0",
138161
"= 1.0.0",
139162
">= 1.0.0",
140163
"> 1.0.0",
@@ -144,19 +167,21 @@ func TestConstraintsParser(t *testing.T) {
144167
">1.0.0 =2.0.0",
145168
">1.0.0 &",
146169
"^1.1.1.1",
147-
"!1.0.0",
148-
">1.0.0 && 2.0.0",
149170
">1.0.0 | =2.0.0",
150171
"(>1.0.0 | =2.0.0)",
151172
"(>1.0.0 || =2.0.0",
152-
">1.0.0 || 2.0.0",
173+
"!1.0.0.0",
174+
"!1.0.0 && !1.0.0.0",
175+
"(!1.0.0 && !1.0.0",
176+
"!1.0.0 || !1.0.0.0",
177+
"(!1.0.0 || !1.0.0",
153178
}
154179
for i, s := range bad {
155180
in := s
156181
t.Run(fmt.Sprintf("BadString%03d", i), func(t *testing.T) {
157182
p, err := ParseConstraint(in)
158-
require.Nil(t, p)
159-
require.Error(t, err)
183+
require.Nil(t, p, "parsing: %s", in)
184+
require.Error(t, err, "parsing: %s", in)
160185
fmt.Printf("'%s' parse error: %s\n", in, err)
161186
})
162187
}

0 commit comments

Comments
 (0)