in rpm/compare.go [58:150]
func versionCompare(v1, v2 string) int {
// 1. If the strings are binary equal (a == b), they’re equal, return 0.
if v1 == v2 {
return 0
}
// 2. Loop over the strings, left-to-right.
for {
// 1. Trim anything that’s not [A-Za-z0-9] or tilde (~) from the front of both strings.
v1 = strings.TrimLeftFunc(v1, isntAlnumOrTilde)
v2 = strings.TrimLeftFunc(v2, isntAlnumOrTilde)
v1StartsWithTilde := len(v1) > 0 && v1[0] == '~'
v2StartsWithTilde := len(v2) > 0 && v2[0] == '~'
if v1StartsWithTilde && v2StartsWithTilde {
// 2. If both strings start with a tilde, discard it and move on to the next character.
v1, v2 = v1[1:], v2[1:]
continue
} else if v1StartsWithTilde {
// 3.a If string a starts with a tilde and string b does not, return -1 (string a is older);
return -1
} else if v2StartsWithTilde {
// 3.b and the inverse if string b starts with a tilde and string a does not.
return 1
}
// neither v1 nor v2 start with tilde
// they start with a letter or digit, or empty
if len(v1) == 0 || len(v2) == 0 {
// 4. End the loop if either string has reached zero length.
break
}
// 5. If the first character of a is a digit, pop the leading chunk of continuous digits from each
// string (which may be ” for b if only one a starts with digits). If a begins with a letter, do
// the same for leading letters.
var isNumeric bool
var segFunc func(rune) bool
if unicode.IsDigit(rune(v1[0])) {
isNumeric = true
segFunc = unicode.IsDigit
} else {
isNumeric = false
segFunc = unicode.IsLetter
}
var v1Seg, v2Seg string
v1Seg, v1 = takeWhile(v1, segFunc) // seg will always have len > 0
v2Seg, v2 = takeWhile(v2, segFunc)
// 6. If the segement from b had 0 length, return 1 if the segment from a was numeric, or -1 if it
// was alphabetic. The logical result of this is that if a begins with numbers and b does not, a is
// newer (return 1). If a begins with letters and b does not, then a is older (return -1). If the
// leading character(s) from a and b were both numbers or both letters, continue on.
if len(v2Seg) == 0 {
if isNumeric {
return 1
}
return -1
}
// here we know that they both start with either numbers or letters
if isNumeric {
// 7. If the leading segments were both numeric, discard any leading zeros and whichever one is
// longer wins. If a is longer than b (without leading zeroes), return 1, and vice-versa. If
// they’re of the same length, continue on.
v1Seg = strings.TrimLeftFunc(v1Seg, isZero)
v2Seg = strings.TrimLeftFunc(v2Seg, isZero)
if c := compareLen(v1Seg, v2Seg); c != 0 {
return c
}
}
// 8. Compare the leading segments with strcmp() (or <=> in Ruby). If that returns a non-zero value,
// then return that value. Else continue to the next iteration of the loop.
switch {
case v1Seg < v2Seg:
return -1
case v1Seg > v2Seg:
return 1
}
}
// 3. If the loop ended (nothing has been returned yet, either both strings are
// totally the same or they’re the same up to the end of one of them, like with
// “1.2.3” and “1.2.3b”), then the longest wins - if what’s left of a is longer
// than what’s left of b, return 1. Vice-versa for if what’s left of b is longer
// than what’s left of a. And finally, if what’s left of them is the same length,
// return 0.
return compareLen(v1, v2)
}