src

Go monorepo.
git clone git://code.dwrz.net/src
Log | Files | Refs

gover.go (4297B)


      1 // Copyright 2023 The Go Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style
      3 // license that can be found in the LICENSE file.
      4 
      5 // This is a fork of internal/gover for use by x/tools until
      6 // go1.21 and earlier are no longer supported by x/tools.
      7 
      8 package versions
      9 
     10 import "strings"
     11 
     12 // A gover is a parsed Go gover: major[.Minor[.Patch]][kind[pre]]
     13 // The numbers are the original decimal strings to avoid integer overflows
     14 // and since there is very little actual math. (Probably overflow doesn't matter in practice,
     15 // but at the time this code was written, there was an existing test that used
     16 // go1.99999999999, which does not fit in an int on 32-bit platforms.
     17 // The "big decimal" representation avoids the problem entirely.)
     18 type gover struct {
     19 	major string // decimal
     20 	minor string // decimal or ""
     21 	patch string // decimal or ""
     22 	kind  string // "", "alpha", "beta", "rc"
     23 	pre   string // decimal or ""
     24 }
     25 
     26 // compare returns -1, 0, or +1 depending on whether
     27 // x < y, x == y, or x > y, interpreted as toolchain versions.
     28 // The versions x and y must not begin with a "go" prefix: just "1.21" not "go1.21".
     29 // Malformed versions compare less than well-formed versions and equal to each other.
     30 // The language version "1.21" compares less than the release candidate and eventual releases "1.21rc1" and "1.21.0".
     31 func compare(x, y string) int {
     32 	vx := parse(x)
     33 	vy := parse(y)
     34 
     35 	if c := cmpInt(vx.major, vy.major); c != 0 {
     36 		return c
     37 	}
     38 	if c := cmpInt(vx.minor, vy.minor); c != 0 {
     39 		return c
     40 	}
     41 	if c := cmpInt(vx.patch, vy.patch); c != 0 {
     42 		return c
     43 	}
     44 	if c := strings.Compare(vx.kind, vy.kind); c != 0 { // "" < alpha < beta < rc
     45 		return c
     46 	}
     47 	if c := cmpInt(vx.pre, vy.pre); c != 0 {
     48 		return c
     49 	}
     50 	return 0
     51 }
     52 
     53 // lang returns the Go language version. For example, lang("1.2.3") == "1.2".
     54 func lang(x string) string {
     55 	v := parse(x)
     56 	if v.minor == "" || v.major == "1" && v.minor == "0" {
     57 		return v.major
     58 	}
     59 	return v.major + "." + v.minor
     60 }
     61 
     62 // isValid reports whether the version x is valid.
     63 func isValid(x string) bool {
     64 	return parse(x) != gover{}
     65 }
     66 
     67 // parse parses the Go version string x into a version.
     68 // It returns the zero version if x is malformed.
     69 func parse(x string) gover {
     70 	var v gover
     71 
     72 	// Parse major version.
     73 	var ok bool
     74 	v.major, x, ok = cutInt(x)
     75 	if !ok {
     76 		return gover{}
     77 	}
     78 	if x == "" {
     79 		// Interpret "1" as "1.0.0".
     80 		v.minor = "0"
     81 		v.patch = "0"
     82 		return v
     83 	}
     84 
     85 	// Parse . before minor version.
     86 	if x[0] != '.' {
     87 		return gover{}
     88 	}
     89 
     90 	// Parse minor version.
     91 	v.minor, x, ok = cutInt(x[1:])
     92 	if !ok {
     93 		return gover{}
     94 	}
     95 	if x == "" {
     96 		// Patch missing is same as "0" for older versions.
     97 		// Starting in Go 1.21, patch missing is different from explicit .0.
     98 		if cmpInt(v.minor, "21") < 0 {
     99 			v.patch = "0"
    100 		}
    101 		return v
    102 	}
    103 
    104 	// Parse patch if present.
    105 	if x[0] == '.' {
    106 		v.patch, x, ok = cutInt(x[1:])
    107 		if !ok || x != "" {
    108 			// Note that we are disallowing prereleases (alpha, beta, rc) for patch releases here (x != "").
    109 			// Allowing them would be a bit confusing because we already have:
    110 			//	1.21 < 1.21rc1
    111 			// But a prerelease of a patch would have the opposite effect:
    112 			//	1.21.3rc1 < 1.21.3
    113 			// We've never needed them before, so let's not start now.
    114 			return gover{}
    115 		}
    116 		return v
    117 	}
    118 
    119 	// Parse prerelease.
    120 	i := 0
    121 	for i < len(x) && (x[i] < '0' || '9' < x[i]) {
    122 		if x[i] < 'a' || 'z' < x[i] {
    123 			return gover{}
    124 		}
    125 		i++
    126 	}
    127 	if i == 0 {
    128 		return gover{}
    129 	}
    130 	v.kind, x = x[:i], x[i:]
    131 	if x == "" {
    132 		return v
    133 	}
    134 	v.pre, x, ok = cutInt(x)
    135 	if !ok || x != "" {
    136 		return gover{}
    137 	}
    138 
    139 	return v
    140 }
    141 
    142 // cutInt scans the leading decimal number at the start of x to an integer
    143 // and returns that value and the rest of the string.
    144 func cutInt(x string) (n, rest string, ok bool) {
    145 	i := 0
    146 	for i < len(x) && '0' <= x[i] && x[i] <= '9' {
    147 		i++
    148 	}
    149 	if i == 0 || x[0] == '0' && i != 1 { // no digits or unnecessary leading zero
    150 		return "", "", false
    151 	}
    152 	return x[:i], x[i:], true
    153 }
    154 
    155 // cmpInt returns cmp.Compare(x, y) interpreting x and y as decimal numbers.
    156 // (Copied from golang.org/x/mod/semver's compareInt.)
    157 func cmpInt(x, y string) int {
    158 	if x == y {
    159 		return 0
    160 	}
    161 	if len(x) < len(y) {
    162 		return -1
    163 	}
    164 	if len(x) > len(y) {
    165 		return +1
    166 	}
    167 	if x < y {
    168 		return -1
    169 	} else {
    170 		return +1
    171 	}
    172 }