aboutsummaryrefslogtreecommitdiff
path: root/pkg
diff options
context:
space:
mode:
authorMax Magorsch <arzano@gentoo.org>2020-05-10 20:08:04 +0200
committerMax Magorsch <arzano@gentoo.org>2020-05-10 20:08:04 +0200
commit39dcf0c604614780517f3dd7388be3e6ba8cd61d (patch)
tree6ce9bfcec54ed07a22252e69b360d6eb38d3b9ef /pkg
parentReturn an array instead of an object when caling resolv.json (diff)
downloadsoko-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.go22
-rw-r--r--pkg/models/version.go160
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
+ }
+}