terraform/vendor/github.com/apparentlymart/go-versions/versions/version.go

223 lines
5.7 KiB
Go

package versions
import (
"fmt"
"strings"
)
// Version represents a single version.
type Version struct {
Major uint64
Minor uint64
Patch uint64
Prerelease VersionExtra
Metadata VersionExtra
}
// Unspecified is the zero value of Version and represents the absense of a
// version number.
//
// Note that this is indistinguishable from the explicit version that
// results from parsing the string "0.0.0".
var Unspecified Version
// Same returns true if the receiver has the same precedence as the other
// given version. In other words, it has the same major, minor and patch
// version number and an identical prerelease portion. The Metadata, if
// any, is not considered.
func (v Version) Same(other Version) bool {
return (v.Major == other.Major &&
v.Minor == other.Minor &&
v.Patch == other.Patch &&
v.Prerelease == other.Prerelease)
}
// Comparable returns a version that is the same as the receiver but its
// metadata is the empty string. For Comparable versions, the standard
// equality operator == is equivalent to method Same.
func (v Version) Comparable() Version {
v.Metadata = ""
return v
}
// String is an implementation of fmt.Stringer that returns the receiver
// in the canonical "semver" format.
func (v Version) String() string {
s := fmt.Sprintf("%d.%d.%d", v.Major, v.Minor, v.Patch)
if v.Prerelease != "" {
s = fmt.Sprintf("%s-%s", s, v.Prerelease)
}
if v.Metadata != "" {
s = fmt.Sprintf("%s+%s", s, v.Metadata)
}
return s
}
func (v Version) GoString() string {
return fmt.Sprintf("versions.MustParseVersion(%q)", v.String())
}
// LessThan returns true if the receiver has a lower precedence than the
// other given version, as defined by the semantic versioning specification.
func (v Version) LessThan(other Version) bool {
switch {
case v.Major != other.Major:
return v.Major < other.Major
case v.Minor != other.Minor:
return v.Minor < other.Minor
case v.Patch != other.Patch:
return v.Patch < other.Patch
case v.Prerelease != other.Prerelease:
if v.Prerelease == "" {
return false
}
if other.Prerelease == "" {
return true
}
return v.Prerelease.LessThan(other.Prerelease)
default:
return false
}
}
// GreaterThan returns true if the receiver has a higher precedence than the
// other given version, as defined by the semantic versioning specification.
func (v Version) GreaterThan(other Version) bool {
switch {
case v.Major != other.Major:
return v.Major > other.Major
case v.Minor != other.Minor:
return v.Minor > other.Minor
case v.Patch != other.Patch:
return v.Patch > other.Patch
case v.Prerelease != other.Prerelease:
if v.Prerelease == "" {
return true
}
if other.Prerelease == "" {
return false
}
return !v.Prerelease.LessThan(other.Prerelease)
default:
return false
}
}
// MarshalText is an implementation of encoding.TextMarshaler, allowing versions
// to be automatically marshalled for text-based serialization formats,
// including encoding/json.
//
// The format used is that returned by String, which can be parsed using
// ParseVersion.
func (v Version) MarshalText() (text []byte, err error) {
return []byte(v.String()), nil
}
// UnmarshalText is an implementation of encoding.TextUnmarshaler, allowing
// versions to be automatically unmarshalled from strings in text-based
// serialization formats, including encoding/json.
//
// The format expected is what is accepted by ParseVersion. Any parser errors
// are passed on verbatim to the caller.
func (v *Version) UnmarshalText(text []byte) error {
str := string(text)
new, err := ParseVersion(str)
if err != nil {
return err
}
*v = new
return nil
}
// VersionExtra represents a string containing dot-delimited tokens, as used
// in the pre-release and build metadata portions of a Semantic Versioning
// version expression.
type VersionExtra string
// Parts tokenizes the string into its separate parts by splitting on dots.
//
// The result is undefined if the receiver is not valid per the semver spec,
func (e VersionExtra) Parts() []string {
return strings.Split(string(e), ".")
}
func (e VersionExtra) Raw() string {
return string(e)
}
// LessThan returns true if the receiever has lower precedence than the
// other given VersionExtra string, per the rules defined in the semver
// spec for pre-release versions.
//
// Build metadata has no defined precedence rules, so it is not meaningful
// to call this method on a VersionExtra representing build metadata.
func (e VersionExtra) LessThan(other VersionExtra) bool {
if e == other {
// Easy path
return false
}
s1 := string(e)
s2 := string(other)
for {
d1 := strings.IndexByte(s1, '.')
d2 := strings.IndexByte(s2, '.')
switch {
case d1 == -1 && d2 != -1:
// s1 has fewer parts, so it precedes s2
return true
case d2 == -1 && d1 != -1:
// s1 has more parts, so it succeeds s2
return false
case d1 == -1: // d2 must be -1 too, because of the above
// this is our last portion to compare
return lessThanStr(s1, s2)
default:
s1s := s1[:d1]
s2s := s2[:d2]
if s1s != s2s {
return lessThanStr(s1s, s2s)
}
s1 = s1[d1+1:]
s2 = s2[d2+1:]
}
}
}
func lessThanStr(s1, s2 string) bool {
// How we compare here depends on whether the string is entirely consistent of digits
s1Numeric := true
s2Numeric := true
for _, c := range s1 {
if c < '0' || c > '9' {
s1Numeric = false
break
}
}
for _, c := range s2 {
if c < '0' || c > '9' {
s2Numeric = false
break
}
}
switch {
case s1Numeric && !s2Numeric:
return true
case s2Numeric && !s1Numeric:
return false
case s1Numeric: // s2Numeric must also be true
switch {
case len(s1) < len(s2):
return true
case len(s2) < len(s1):
return false
default:
return s1 < s2
}
default:
return s1 < s2
}
}