Skip to content

Commit

Permalink
Added 'compatible-with' (caret ^) operator
Browse files Browse the repository at this point in the history
  • Loading branch information
cmaglie committed May 27, 2022
1 parent 2f82020 commit 0cb3fc0
Show file tree
Hide file tree
Showing 3 changed files with 162 additions and 24 deletions.
20 changes: 20 additions & 0 deletions constraints.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,12 @@ func ParseConstraint(in string) (Constraint, error) {
return nil, err
}
return &Equals{v}, nil
case '^':
v, err := version()
if err != nil {
return nil, err
}
return &CompatibleWith{v}, nil
case '>':
if peek() == '=' {
next()
Expand Down Expand Up @@ -273,6 +279,20 @@ func (gte *GreaterThanOrEqual) String() string {
return ">=" + gte.Version.String()
}

// CompatibleWith is the "compatible with" (^) constraint
type CompatibleWith struct {
Version *Version
}

// Match returns true if v satisfies the condition
func (cw *CompatibleWith) Match(v *Version) bool {
return v.CompatibleWith(cw.Version)
}

func (cw *CompatibleWith) String() string {
return "^" + cw.Version.String()
}

// Or will match if ANY of the Operands Constraint will match
type Or struct {
Operands []Constraint
Expand Down
14 changes: 14 additions & 0 deletions version.go
Original file line number Diff line number Diff line change
Expand Up @@ -205,3 +205,17 @@ func (v *Version) GreaterThan(u *Version) bool {
func (v *Version) GreaterThanOrEqual(u *Version) bool {
return v.CompareTo(u) >= 0
}

// CompatibleWith returns true if the Version is compatible with the version passed as paramater
func (v *Version) CompatibleWith(u *Version) bool {
if !u.GreaterThanOrEqual(v) {
return false
}
if v.major[0] != '0' {
return compareNumber(u.major, v.major) == 0
} else if v.minor[0] != '0' {
return compareNumber(u.major, v.major) == 0 && compareNumber(u.minor, v.minor) == 0
} else {
return compareNumber(u.major, v.major) == 0 && compareNumber(u.minor, v.minor) == 0 && compareNumber(u.patch, v.patch) == 0
}
}
152 changes: 128 additions & 24 deletions version_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,27 +12,38 @@ import (
"github.com/stretchr/testify/require"
)

var sign = map[int]string{1: ">", 0: "=", -1: "<"}

func v(vers string) *Version {
return MustParse(vers)
}

func TestVersionComparator(t *testing.T) {
sign := map[int]string{1: ">", 0: "=", -1: "<"}
ascending := func(list ...*Version) {
for i := range list[0 : len(list)-1] {
a := list[i]
b := list[i+1]
comp := a.CompareTo(b)
fmt.Printf("%s %s %s\n", a, sign[comp], b)
func ascending(t *testing.T, allowEqual bool, list ...string) {
for i := range list[0 : len(list)-1] {
a := MustParse(list[i])
b := MustParse(list[i+1])
comp := a.CompareTo(b)
fmt.Printf("%s %s %s\n", a, sign[comp], b)
if allowEqual {
require.LessOrEqual(t, comp, 0)
require.True(t, a.LessThanOrEqual(b))
require.False(t, a.GreaterThan(b))
} else {
require.Equal(t, comp, -1)
require.True(t, a.LessThan(b))
require.True(t, a.LessThanOrEqual(b))
require.False(t, a.Equal(b))
require.False(t, a.GreaterThanOrEqual(b))
require.False(t, a.GreaterThan(b))
}

comp = b.CompareTo(a)
fmt.Printf("%s %s %s\n", b, sign[comp], a)
comp = b.CompareTo(a)
fmt.Printf("%s %s %s\n", b, sign[comp], a)
if allowEqual {
require.GreaterOrEqual(t, comp, 0)
require.False(t, b.LessThan(a))
require.True(t, b.GreaterThanOrEqual(a))
} else {
require.Equal(t, comp, 1)
require.False(t, b.LessThan(a))
require.False(t, b.LessThanOrEqual(a))
Expand All @@ -41,6 +52,9 @@ func TestVersionComparator(t *testing.T) {
require.True(t, b.GreaterThan(a))
}
}
}

func TestVersionComparator(t *testing.T) {
equal := func(list ...*Version) {
for i, a := range list[:len(list)-1] {
for _, b := range list[i+1:] {
Expand All @@ -64,20 +78,20 @@ func TestVersionComparator(t *testing.T) {
}
}
}
ascending(
MustParse("1.0.0-alpha"),
MustParse("1.0.0-alpha.1"),
MustParse("1.0.0-alpha.beta"),
MustParse("1.0.0-beta"),
MustParse("1.0.0-beta.2"),
MustParse("1.0.0-beta.11"),
MustParse("1.0.0-rc.1"),
MustParse("1.0.0"),
MustParse("1.0.1"),
MustParse("1.1.1"),
MustParse("1.6.22"),
MustParse("1.8.1"),
MustParse("2.1.1"),
ascending(t, false,
"1.0.0-alpha",
"1.0.0-alpha.1",
"1.0.0-alpha.beta",
"1.0.0-beta",
"1.0.0-beta.2",
"1.0.0-beta.11",
"1.0.0-rc.1",
"1.0.0",
"1.0.1",
"1.1.1",
"1.6.22",
"1.8.1",
"2.1.1",
)
equal(
MustParse(""),
Expand All @@ -104,6 +118,96 @@ func TestVersionComparator(t *testing.T) {
)
}

func TestCompatibleWithVersionComparator(t *testing.T) {
require.True(t, v("0.0.1-rc.0+build").CompatibleWith(v("0.0.1-rc.0")))
list := []string{
"0.0.1-rc.0", // 0
"0.0.1-rc.0+build", // 1
"0.0.1-rc.1", // 2
"0.0.1", // 3
"0.0.1+build", // 4
"0.0.2-rc.1", // 5 - BREAKING CHANGE
"0.0.2-rc.1+build", // 6
"0.0.2", // 7
"0.0.2+build", // 8
"0.0.3-rc.1", // 9 - BREAKING CHANGE
"0.0.3-rc.2", // 10
"0.0.3", // 11
"0.1.0", // 12 - BREAKING CHANGE
"0.3.3-rc.0", // 13 - BREAKING CHANGE
"0.3.3-rc.1", // 14
"0.3.3", // 15
"0.3.3+build", // 16
"0.3.4-rc.1", // 17
"0.3.4", // 18
"0.4.0", // 19 - BREAKING CHANGE
"1.0.0-rc", // 20 - BREAKING CHANGE
"1.0.0", // 21
"1.0.0+build", // 22
"1.2.1-rc", // 23
"1.2.1", // 24
"1.2.1+build", // 25
"1.2.3-rc.2", // 26
"1.2.3-rc.2+build", // 27
"1.2.3", // 28
"1.2.3+build", // 29
"1.2.4", // 30
"1.3.0-rc.0+build", // 31
"1.3.0", // 32
"1.3.0+build", // 33
"1.3.1-rc.0", // 34
"1.3.1-rc.1", // 35
"1.3.1", // 36
"1.3.5", // 37
"2.0.0-rc", // 38 - BREAKING CHANGE
"2.0.0-rc+build", // 39
"2.0.0", // 40
"2.0.0+build", // 41
"2.1.0-rc", // 42
"2.1.0-rc+build", // 43
"2.1.0", // 44
"2.1.0+build", // 45
"2.1.3-rc", // 46
"2.1.3", // 47
"2.3.0", // 48
"2.3.1", // 49
"3.0.0", // 50 - BREAKING CHANGE
}
breaking := []int{5, 9, 12, 13, 19, 20, 38, 50}

ascending(t, true, list...)
compatible := func(which, from, to int) {
x := MustParse(list[which])
for _, comp := range list[:from] {
y := MustParse(comp)
require.False(t, x.CompatibleWith(y), "%s is not compatible with %s", x, y)
}
for _, comp := range list[from:to] {
y := MustParse(comp)
require.True(t, x.CompatibleWith(y), "%s is compatible with %s", x, y)
}
for _, comp := range list[to:] {
y := MustParse(comp)
require.False(t, x.CompatibleWith(y), "%s is not compatible with %s", x, y)
}
}

j := 0
for i := 0; i < len(list)-1; i++ {
breakingIdx := 0
for _, b := range breaking {
if b > i {
breakingIdx = b
break
}
}
if !MustParse(list[j]).Equal(MustParse(list[i])) {
j = i
}
compatible(i, j, breakingIdx)
}
}

func TestNilVersionString(t *testing.T) {
var nilVersion *Version
require.Equal(t, "", nilVersion.String())
Expand Down

0 comments on commit 0cb3fc0

Please sign in to comment.