From ca00c9027be76476e654e1d536d1222ffb5ef516 Mon Sep 17 00:00:00 2001 From: Mattias Wadman Date: Thu, 15 Jun 2023 19:27:58 +0200 Subject: [PATCH] Add version sort support --- internal/filter/sort/sort.go | 42 ++++++++++++--- internal/slicex/slicex.go | 9 ++++ internal/versioncmp/versioncmp.go | 75 ++++++++++++++++++++++++++ internal/versioncmp/versioncmp_test.go | 24 +++++++++ 4 files changed, 143 insertions(+), 7 deletions(-) create mode 100644 internal/versioncmp/versioncmp.go create mode 100644 internal/versioncmp/versioncmp_test.go diff --git a/internal/filter/sort/sort.go b/internal/filter/sort/sort.go index a47e697..facfb78 100644 --- a/internal/filter/sort/sort.go +++ b/internal/filter/sort/sort.go @@ -5,6 +5,7 @@ import ( "sort" "github.com/wader/bump/internal/filter" + "github.com/wader/bump/internal/versioncmp" ) // Name of filter @@ -19,18 +20,32 @@ Sort versions reverse alphabetically. static:a,b,c|sort `[1:] +type sortType int + +const ( + sortAlphabetical sortType = iota + sortVersion +) + // New sort filter func New(prefix string, arg string) (filter filter.Filter, err error) { if prefix != Name { return nil, nil } - if arg != "" { - return nil, fmt.Errorf("arg should be empty") + var sortType sortType + if arg == "" || arg == "alphabetical" { + sortType = sortAlphabetical + } else if arg == "version" { + sortType = sortVersion + } else { + return nil, fmt.Errorf("arg should be empty, alphabetical or version") } - return sortFilter{}, nil + return sortFilter{sortType: sortType}, nil } -type sortFilter struct{} +type sortFilter struct { + sortType sortType +} func (f sortFilter) String() string { return Name @@ -38,8 +53,21 @@ func (f sortFilter) String() string { func (f sortFilter) Filter(versions filter.Versions, versionKey string) (filter.Versions, string, error) { svs := append(filter.Versions{}, versions...) - sort.Slice(svs, func(i int, j int) bool { - return svs[i][versionKey] > svs[j][versionKey] - }) + + switch f.sortType { + case sortAlphabetical: + sort.Slice(svs, func(i int, j int) bool { + return svs[i][versionKey] < svs[j][versionKey] + }) + case sortVersion: + sort.Slice(svs, func(i int, j int) bool { + return !versioncmp.Cmp(svs[i][versionKey], svs[j][versionKey]) + }) + default: + panic("unreachable") + } + + // svs = slicex.Reverse(svs) + return svs, versionKey, nil } diff --git a/internal/slicex/slicex.go b/internal/slicex/slicex.go index 7802977..581b4bd 100644 --- a/internal/slicex/slicex.go +++ b/internal/slicex/slicex.go @@ -21,3 +21,12 @@ func Unique[T comparable](s []T) []T { } return us } + +func Reverse[T any](s []T) []T { + rs := make([]T, len(s)) + l := len(s) + for i, v := range s { + rs[l-i-1] = v + } + return rs +} diff --git a/internal/versioncmp/versioncmp.go b/internal/versioncmp/versioncmp.go new file mode 100644 index 0000000..48d366a --- /dev/null +++ b/internal/versioncmp/versioncmp.go @@ -0,0 +1,75 @@ +package versioncmp + +import ( + "strconv" + "unicode" +) + +func Split(a string) []any { + if len(a) == 0 { + return nil + } + + lastIsDigit := unicode.IsDigit(rune(a[0])) + lastIndex := 0 + var parts []any + + add := func(isNumber bool, s string) { + if isNumber { + n, _ := strconv.ParseInt(s, 10, 64) + parts = append(parts, n) + } else { + parts = append(parts, s) + } + } + + for i, r := range a[1:] { + isDigit := unicode.IsDigit(r) + if isDigit != lastIsDigit { + add(lastIsDigit, a[lastIndex:i+1]) + lastIsDigit = isDigit + lastIndex = i + 1 + continue + } + } + + if lastIndex != len(a) { + add(lastIsDigit, a[lastIndex:]) + } + + return parts +} + +func Cmp(a, b string) bool { + ap := Split(a) + bp := Split(b) + for i := 0; i < len(ap) && i < len(bp); i++ { + ae := ap[i] + be := bp[i] + + switch ae := ae.(type) { + case int64: + switch be := be.(type) { + case int64: + if ae == be { + continue + } + return ae < be + default: + return true + } + case string: + switch be := be.(type) { + case string: + if ae == be { + continue + } + return ae < be + default: + return false + } + } + } + + return len(ap) <= len(bp) +} diff --git a/internal/versioncmp/versioncmp_test.go b/internal/versioncmp/versioncmp_test.go new file mode 100644 index 0000000..4a16fbb --- /dev/null +++ b/internal/versioncmp/versioncmp_test.go @@ -0,0 +1,24 @@ +package versioncmp_test + +import ( + "log" + "sort" + "testing" + + "github.com/wader/bump/internal/versioncmp" +) + +func TestCmp(t *testing.T) { + + // s := []string{"ab.22.cc", "ab.11.dd", "ab.11.dd"} + + s := []string{"1_9_13p2", "1_9_13", "1_9_11"} + + log.Printf("b: %#+v\n", s) + + sort.SliceStable(s, func(i, j int) bool { + return versioncmp.Cmp(s[i], s[j]) + }) + + log.Printf("a: %#+v\n", s) +}