1111package gover
1212
1313import (
14- "cmp "
14+ "internal/gover "
1515)
1616
17- // A version is a parsed Go version: major[.minor[.patch]][kind[pre]]
18- // The numbers are the original decimal strings to avoid integer overflows
19- // and since there is very little actual math. (Probably overflow doesn't matter in practice,
20- // but at the time this code was written, there was an existing test that used
21- // go1.99999999999, which does not fit in an int on 32-bit platforms.
22- // The "big decimal" representation avoids the problem entirely.)
23- type version struct {
24- major string // decimal
25- minor string // decimal or ""
26- patch string // decimal or ""
27- kind string // "", "alpha", "beta", "rc"
28- pre string // decimal or ""
29- }
30-
3117// Compare returns -1, 0, or +1 depending on whether
3218// x < y, x == y, or x > y, interpreted as toolchain versions.
3319// The versions x and y must not begin with a "go" prefix: just "1.21" not "go1.21".
3420// Malformed versions compare less than well-formed versions and equal to each other.
3521// The language version "1.21" compares less than the release candidate and eventual releases "1.21rc1" and "1.21.0".
3622func Compare (x , y string ) int {
37- vx := parse (x )
38- vy := parse (y )
39-
40- if c := cmpInt (vx .major , vy .major ); c != 0 {
41- return c
42- }
43- if c := cmpInt (vx .minor , vy .minor ); c != 0 {
44- return c
45- }
46- if c := cmpInt (vx .patch , vy .patch ); c != 0 {
47- return c
48- }
49- if c := cmp .Compare (vx .kind , vy .kind ); c != 0 { // "" < alpha < beta < rc
50- return c
51- }
52- if c := cmpInt (vx .pre , vy .pre ); c != 0 {
53- return c
54- }
55- return 0
23+ return gover .Compare (x , y )
5624}
5725
5826// Max returns the maximum of x and y interpreted as toolchain versions,
5927// compared using Compare.
6028// If x and y compare equal, Max returns x.
6129func Max (x , y string ) string {
62- if Compare (x , y ) < 0 {
63- return y
64- }
65- return x
66- }
67-
68- // Toolchain returns the maximum of x and y interpreted as toolchain names,
69- // compared using Compare(FromToolchain(x), FromToolchain(y)).
70- // If x and y compare equal, Max returns x.
71- func ToolchainMax (x , y string ) string {
72- if Compare (FromToolchain (x ), FromToolchain (y )) < 0 {
73- return y
74- }
75- return x
30+ return gover .Max (x , y )
7631}
7732
7833// IsLang reports whether v denotes the overall Go language version
@@ -85,22 +40,17 @@ func ToolchainMax(x, y string) string {
8540// meaning that Go 1.21rc1 and Go 1.21.0 will both handle go.mod files that
8641// say "go 1.21", but Go 1.21rc1 will not handle files that say "go 1.21.0".
8742func IsLang (x string ) bool {
88- v := parse (x )
89- return v != version {} && v .patch == "" && v .kind == "" && v .pre == ""
43+ return gover .IsLang (x )
9044}
9145
9246// Lang returns the Go language version. For example, Lang("1.2.3") == "1.2".
9347func Lang (x string ) string {
94- v := parse (x )
95- if v .minor == "" {
96- return v .major
97- }
98- return v .major + "." + v .minor
48+ return gover .Lang (x )
9949}
10050
10151// IsPrerelease reports whether v denotes a Go prerelease version.
10252func IsPrerelease (x string ) bool {
103- return parse (x ).kind != ""
53+ return gover . Parse (x ).Kind != ""
10454}
10555
10656// Prev returns the Go major release immediately preceding v,
@@ -112,143 +62,14 @@ func IsPrerelease(x string) bool {
11262// Prev("1.2") = "1.1"
11363// Prev("1.3rc4") = "1.2"
11464func Prev (x string ) string {
115- v := parse (x )
116- if cmpInt (v .minor , "1" ) <= 0 {
117- return v .major
65+ v := gover . Parse (x )
66+ if gover . CmpInt (v .Minor , "1" ) <= 0 {
67+ return v .Major
11868 }
119- return v .major + "." + decInt (v .minor )
69+ return v .Major + "." + gover . DecInt (v .Minor )
12070}
12171
12272// IsValid reports whether the version x is valid.
12373func IsValid (x string ) bool {
124- return parse (x ) != version {}
125- }
126-
127- // parse parses the Go version string x into a version.
128- // It returns the zero version if x is malformed.
129- func parse (x string ) version {
130- var v version
131-
132- // Parse major version.
133- var ok bool
134- v .major , x , ok = cutInt (x )
135- if ! ok {
136- return version {}
137- }
138- if x == "" {
139- // Interpret "1" as "1.0.0".
140- v .minor = "0"
141- v .patch = "0"
142- return v
143- }
144-
145- // Parse . before minor version.
146- if x [0 ] != '.' {
147- return version {}
148- }
149-
150- // Parse minor version.
151- v .minor , x , ok = cutInt (x [1 :])
152- if ! ok {
153- return version {}
154- }
155- if x == "" {
156- // Patch missing is same as "0" for older versions.
157- // Starting in Go 1.21, patch missing is different from explicit .0.
158- if cmpInt (v .minor , "21" ) < 0 {
159- v .patch = "0"
160- }
161- return v
162- }
163-
164- // Parse patch if present.
165- if x [0 ] == '.' {
166- v .patch , x , ok = cutInt (x [1 :])
167- if ! ok || x != "" {
168- // Note that we are disallowing prereleases (alpha, beta, rc) for patch releases here (x != "").
169- // Allowing them would be a bit confusing because we already have:
170- // 1.21 < 1.21rc1
171- // But a prerelease of a patch would have the opposite effect:
172- // 1.21.3rc1 < 1.21.3
173- // We've never needed them before, so let's not start now.
174- return version {}
175- }
176- return v
177- }
178-
179- // Parse prerelease.
180- i := 0
181- for i < len (x ) && (x [i ] < '0' || '9' < x [i ]) {
182- if x [i ] < 'a' || 'z' < x [i ] {
183- return version {}
184- }
185- i ++
186- }
187- if i == 0 {
188- return version {}
189- }
190- v .kind , x = x [:i ], x [i :]
191- if x == "" {
192- return v
193- }
194- v .pre , x , ok = cutInt (x )
195- if ! ok || x != "" {
196- return version {}
197- }
198-
199- return v
200- }
201-
202- // cutInt scans the leading decimal number at the start of x to an integer
203- // and returns that value and the rest of the string.
204- func cutInt (x string ) (n , rest string , ok bool ) {
205- i := 0
206- for i < len (x ) && '0' <= x [i ] && x [i ] <= '9' {
207- i ++
208- }
209- if i == 0 || x [0 ] == '0' && i != 1 {
210- return "" , "" , false
211- }
212- return x [:i ], x [i :], true
213- }
214-
215- // cmpInt returns cmp.Compare(x, y) interpreting x and y as decimal numbers.
216- // (Copied from golang.org/x/mod/semver's compareInt.)
217- func cmpInt (x , y string ) int {
218- if x == y {
219- return 0
220- }
221- if len (x ) < len (y ) {
222- return - 1
223- }
224- if len (x ) > len (y ) {
225- return + 1
226- }
227- if x < y {
228- return - 1
229- } else {
230- return + 1
231- }
232- }
233-
234- // decInt returns the decimal string decremented by 1, or the empty string
235- // if the decimal is all zeroes.
236- // (Copied from golang.org/x/mod/module's decDecimal.)
237- func decInt (decimal string ) string {
238- // Scan right to left turning 0s to 9s until you find a digit to decrement.
239- digits := []byte (decimal )
240- i := len (digits ) - 1
241- for ; i >= 0 && digits [i ] == '0' ; i -- {
242- digits [i ] = '9'
243- }
244- if i < 0 {
245- // decimal is all zeros
246- return ""
247- }
248- if i == 0 && digits [i ] == '1' && len (digits ) > 1 {
249- digits = digits [1 :]
250- } else {
251- digits [i ]--
252- }
253- return string (digits )
74+ return gover .IsValid (x )
25475}
0 commit comments