diff options
author | Max Magorsch <arzano@gentoo.org> | 2020-05-10 20:08:04 +0200 |
---|---|---|
committer | Max Magorsch <arzano@gentoo.org> | 2020-05-10 20:08:04 +0200 |
commit | 39dcf0c604614780517f3dd7388be3e6ba8cd61d (patch) | |
tree | 6ce9bfcec54ed07a22252e69b360d6eb38d3b9ef /pkg | |
parent | Return an array instead of an object when caling resolv.json (diff) | |
download | soko-39dcf0c604614780517f3dd7388be3e6ba8cd61d.tar.gz soko-39dcf0c604614780517f3dd7388be3e6ba8cd61d.tar.bz2 soko-39dcf0c604614780517f3dd7388be3e6ba8cd61d.zip |
Make version comparisons PMS compliant
Signed-off-by: Max Magorsch <arzano@gentoo.org>
Diffstat (limited to 'pkg')
-rw-r--r-- | pkg/app/handler/packages/utils.go | 22 | ||||
-rw-r--r-- | pkg/models/version.go | 160 |
2 files changed, 166 insertions, 16 deletions
diff --git a/pkg/app/handler/packages/utils.go b/pkg/app/handler/packages/utils.go index ece6308..f9ab405 100644 --- a/pkg/app/handler/packages/utils.go +++ b/pkg/app/handler/packages/utils.go @@ -7,9 +7,7 @@ import ( "encoding/hex" "github.com/go-pg/pg" "github.com/go-pg/pg/v9/orm" - "github.com/mcuadros/go-version" "html/template" - textTemplate "text/template" "net/http" "soko/pkg/app/utils" "soko/pkg/database" @@ -18,6 +16,7 @@ import ( utils2 "soko/pkg/utils" "sort" "strings" + textTemplate "text/template" ) // getAddedPackages returns a list of recently added @@ -361,23 +360,14 @@ func showRemovalNotice(versions []*models.Version) bool { // sort the versions in ascending order func sortVersionsAsc(versions []*models.Version) { - sortVersions(versions, "<") + sort.Slice(versions, func(i, j int) bool { + return !versions[i].CompareTo(*versions[j]) + }) } // sort the versions in descending order func sortVersionsDesc(versions []*models.Version) { - sortVersions(versions, ">") -} - -// sort the versions - the order is given by the given operator -func sortVersions(versions []*models.Version, operator string) { - sort.SliceStable(versions, func(i, j int) bool { - // the version library normally results in i.e. 1.1-r1 < 1.1 - // that's why we replace -rXXX by .XXX so that 1.1-r1 > 1.1 - firstVersion := strings.ReplaceAll(versions[i].Version, "-r", ".") - secondVersion := strings.ReplaceAll(versions[j].Version, "-r", ".") - firstVersion = version.Normalize(firstVersion) - secondVersion = version.Normalize(secondVersion) - return version.Compare(firstVersion, secondVersion, operator) + sort.Slice(versions, func(i, j int) bool { + return versions[i].CompareTo(*versions[j]) }) } diff --git a/pkg/models/version.go b/pkg/models/version.go index 4ac908e..4688eda 100644 --- a/pkg/models/version.go +++ b/pkg/models/version.go @@ -2,6 +2,14 @@ package models +import ( + "github.com/mcuadros/go-version" + "regexp" + "strconv" + "strings" + "unicode" +) + type Version struct { Id string `pg:",pk"` Category string @@ -21,3 +29,155 @@ type Version struct { Commits []*Commit `pg:"many2many:commit_to_versions,joinFK:commit_id"` Masks []*Mask `pg:"many2many:mask_to_versions,joinFK:mask_versions"` } + + +// Compare two versions strings - compliant to the 'Version Comparison' +// described in the Package Manager Specification (PMS) +func (v *Version) CompareTo(other Version) bool { + versionIdentifierA := v.computeVersionIdentifier() + versionIdentifierB := other.computeVersionIdentifier() + + // compare the numeric part + numericPartA := version.Normalize(versionIdentifierA.NumericPart) + numericPartB := version.Normalize(versionIdentifierB.NumericPart) + if !version.Compare(numericPartA, numericPartB, "=") { + return version.Compare(numericPartA, numericPartB, ">") + } + + // compare the letter + if versionIdentifierA.Letter != versionIdentifierB.Letter { + return strings.Compare(versionIdentifierA.Letter, versionIdentifierB.Letter) == 1 + } + + // compare the suffixes + for i := 0; i < min(len(versionIdentifierA.Suffixes), len(versionIdentifierB.Suffixes)); i++ { + if versionIdentifierA.Suffixes[i] == versionIdentifierA.Suffixes[i] { + return versionIdentifierA.Suffixes[i].Number > versionIdentifierB.Suffixes[i].Number + } else { + return getSuffixOrder(versionIdentifierA.Suffixes[i].Name) > getSuffixOrder(versionIdentifierB.Suffixes[i].Name) + } + } + if len(versionIdentifierA.Suffixes) != len(versionIdentifierB.Suffixes) { + return len(versionIdentifierA.Suffixes) > len(versionIdentifierB.Suffixes) + } + + // compare the revision + if versionIdentifierA.Revision != versionIdentifierB.Revision { + return versionIdentifierA.Revision > versionIdentifierB.Revision + } + + // the versions are equal based on the PMS specification but + // we have to return a bool, that's why we return true here + return true +} + + + +// utils + +type VersionIdentifier struct { + NumericPart string + Letter string + Suffixes []*VersionSuffix + Revision int +} + +type VersionSuffix struct { + Name string + Number int +} + +// get the minimum of the two given ints +func min(a, b int) int { + if a < b { + return a + } + return b +} + +// computeVersionIdentifier is parsing the Version string of a +// version and is computing a VersionIdentifier based on this +// string. +func (v *Version) computeVersionIdentifier() VersionIdentifier { + + rawVersionParts := strings.FieldsFunc(v.Version, func(r rune) bool { + return r == '_' || r == '-' + }) + + versionIdentifier := new(VersionIdentifier) + versionIdentifier.NumericPart, versionIdentifier.Letter = getNumericPart(rawVersionParts[0]) + rawVersionParts = rawVersionParts[1:] + + for _, rawVersionPart := range rawVersionParts { + if suffix := getSuffix(rawVersionPart); suffix != nil { + versionIdentifier.Suffixes = append(versionIdentifier.Suffixes, suffix) + } else if isRevision(rawVersionPart) { + parsedRevision, err := strconv.Atoi(strings.ReplaceAll(rawVersionPart, "r", "")) + if err == nil { + versionIdentifier.Revision = parsedRevision + } + } + } + + return *versionIdentifier +} + +// getNumericPart returns the numeric part of the version, that is: +// version, letter +// i.e. 10.3.18a becomes +// 10.3.18, a +// The first returned string is the version and the second if the (optional) letter +func getNumericPart(str string) (string, string) { + if unicode.IsLetter(rune(str[len(str)-1])) { + return str[:len(str)-1], str[len(str)-1:] + } + return str, "" +} + +// getSuffix creates a VersionSuffix based on the given string. +// The given string is expected to be look like +// pre20190518 +// for instance. The suffix named as well as the following number +// will be parsed and returned as VersionSuffix +func getSuffix(str string) *VersionSuffix { + allowedSuffixes := []string{"alpha", " beta", "pre", "rc", "p"} + for _, allowedSuffix := range allowedSuffixes { + if regexp.MustCompile(allowedSuffix + `\d+`).MatchString(str) { + parsedSuffix, err := strconv.Atoi(strings.ReplaceAll(str, allowedSuffix, "")) + if err == nil { + return &VersionSuffix{ + Name: allowedSuffix, + Number: parsedSuffix, + } + } + } + } + return nil +} + +// isRevision checks whether the given string +// matches the format of a revision, that is +// 'r2' for instance. +func isRevision(str string) bool { + return regexp.MustCompile(`r\d+`).MatchString(str) +} + +// getSuffixOrder returns an int for the given suffix, +// based on the following: +// _alpha < _beta < _pre < _rc < _p < none +// as defined in the Package Manager Specification (PMS) +func getSuffixOrder(suffix string) int { + if suffix == "p" { + return 4 + } else if suffix == "rc" { + return 3 + } else if suffix == "pre" { + return 2 + } else if suffix == "beta" { + return 1 + } else if suffix == "alpha" { + return 0 + } else { + return 9999 + } +} |