Source file src/pkg/cmd/go/internal/semver/semver.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 package semver
24
25
26 type parsed struct {
27 major string
28 minor string
29 patch string
30 short string
31 prerelease string
32 build string
33 err string
34 }
35
36
37 func IsValid(v string) bool {
38 _, ok := parse(v)
39 return ok
40 }
41
42
43
44
45
46
47 func Canonical(v string) string {
48 p, ok := parse(v)
49 if !ok {
50 return ""
51 }
52 if p.build != "" {
53 return v[:len(v)-len(p.build)]
54 }
55 if p.short != "" {
56 return v + p.short
57 }
58 return v
59 }
60
61
62
63
64 func Major(v string) string {
65 pv, ok := parse(v)
66 if !ok {
67 return ""
68 }
69 return v[:1+len(pv.major)]
70 }
71
72
73
74
75 func MajorMinor(v string) string {
76 pv, ok := parse(v)
77 if !ok {
78 return ""
79 }
80 i := 1 + len(pv.major)
81 if j := i + 1 + len(pv.minor); j <= len(v) && v[i] == '.' && v[i+1:j] == pv.minor {
82 return v[:j]
83 }
84 return v[:i] + "." + pv.minor
85 }
86
87
88
89
90 func Prerelease(v string) string {
91 pv, ok := parse(v)
92 if !ok {
93 return ""
94 }
95 return pv.prerelease
96 }
97
98
99
100
101 func Build(v string) string {
102 pv, ok := parse(v)
103 if !ok {
104 return ""
105 }
106 return pv.build
107 }
108
109
110
111
112
113
114
115 func Compare(v, w string) int {
116 pv, ok1 := parse(v)
117 pw, ok2 := parse(w)
118 if !ok1 && !ok2 {
119 return 0
120 }
121 if !ok1 {
122 return -1
123 }
124 if !ok2 {
125 return +1
126 }
127 if c := compareInt(pv.major, pw.major); c != 0 {
128 return c
129 }
130 if c := compareInt(pv.minor, pw.minor); c != 0 {
131 return c
132 }
133 if c := compareInt(pv.patch, pw.patch); c != 0 {
134 return c
135 }
136 return comparePrerelease(pv.prerelease, pw.prerelease)
137 }
138
139
140
141 func Max(v, w string) string {
142 v = Canonical(v)
143 w = Canonical(w)
144 if Compare(v, w) > 0 {
145 return v
146 }
147 return w
148 }
149
150 func parse(v string) (p parsed, ok bool) {
151 if v == "" || v[0] != 'v' {
152 p.err = "missing v prefix"
153 return
154 }
155 p.major, v, ok = parseInt(v[1:])
156 if !ok {
157 p.err = "bad major version"
158 return
159 }
160 if v == "" {
161 p.minor = "0"
162 p.patch = "0"
163 p.short = ".0.0"
164 return
165 }
166 if v[0] != '.' {
167 p.err = "bad minor prefix"
168 ok = false
169 return
170 }
171 p.minor, v, ok = parseInt(v[1:])
172 if !ok {
173 p.err = "bad minor version"
174 return
175 }
176 if v == "" {
177 p.patch = "0"
178 p.short = ".0"
179 return
180 }
181 if v[0] != '.' {
182 p.err = "bad patch prefix"
183 ok = false
184 return
185 }
186 p.patch, v, ok = parseInt(v[1:])
187 if !ok {
188 p.err = "bad patch version"
189 return
190 }
191 if len(v) > 0 && v[0] == '-' {
192 p.prerelease, v, ok = parsePrerelease(v)
193 if !ok {
194 p.err = "bad prerelease"
195 return
196 }
197 }
198 if len(v) > 0 && v[0] == '+' {
199 p.build, v, ok = parseBuild(v)
200 if !ok {
201 p.err = "bad build"
202 return
203 }
204 }
205 if v != "" {
206 p.err = "junk on end"
207 ok = false
208 return
209 }
210 ok = true
211 return
212 }
213
214 func parseInt(v string) (t, rest string, ok bool) {
215 if v == "" {
216 return
217 }
218 if v[0] < '0' || '9' < v[0] {
219 return
220 }
221 i := 1
222 for i < len(v) && '0' <= v[i] && v[i] <= '9' {
223 i++
224 }
225 if v[0] == '0' && i != 1 {
226 return
227 }
228 return v[:i], v[i:], true
229 }
230
231 func parsePrerelease(v string) (t, rest string, ok bool) {
232
233
234
235
236 if v == "" || v[0] != '-' {
237 return
238 }
239 i := 1
240 start := 1
241 for i < len(v) && v[i] != '+' {
242 if !isIdentChar(v[i]) && v[i] != '.' {
243 return
244 }
245 if v[i] == '.' {
246 if start == i || isBadNum(v[start:i]) {
247 return
248 }
249 start = i + 1
250 }
251 i++
252 }
253 if start == i || isBadNum(v[start:i]) {
254 return
255 }
256 return v[:i], v[i:], true
257 }
258
259 func parseBuild(v string) (t, rest string, ok bool) {
260 if v == "" || v[0] != '+' {
261 return
262 }
263 i := 1
264 start := 1
265 for i < len(v) {
266 if !isIdentChar(v[i]) && v[i] != '.' {
267 return
268 }
269 if v[i] == '.' {
270 if start == i {
271 return
272 }
273 start = i + 1
274 }
275 i++
276 }
277 if start == i {
278 return
279 }
280 return v[:i], v[i:], true
281 }
282
283 func isIdentChar(c byte) bool {
284 return 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || '0' <= c && c <= '9' || c == '-'
285 }
286
287 func isBadNum(v string) bool {
288 i := 0
289 for i < len(v) && '0' <= v[i] && v[i] <= '9' {
290 i++
291 }
292 return i == len(v) && i > 1 && v[0] == '0'
293 }
294
295 func isNum(v string) bool {
296 i := 0
297 for i < len(v) && '0' <= v[i] && v[i] <= '9' {
298 i++
299 }
300 return i == len(v)
301 }
302
303 func compareInt(x, y string) int {
304 if x == y {
305 return 0
306 }
307 if len(x) < len(y) {
308 return -1
309 }
310 if len(x) > len(y) {
311 return +1
312 }
313 if x < y {
314 return -1
315 } else {
316 return +1
317 }
318 }
319
320 func comparePrerelease(x, y string) int {
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335 if x == y {
336 return 0
337 }
338 if x == "" {
339 return +1
340 }
341 if y == "" {
342 return -1
343 }
344 for x != "" && y != "" {
345 x = x[1:]
346 y = y[1:]
347 var dx, dy string
348 dx, x = nextIdent(x)
349 dy, y = nextIdent(y)
350 if dx != dy {
351 ix := isNum(dx)
352 iy := isNum(dy)
353 if ix != iy {
354 if ix {
355 return -1
356 } else {
357 return +1
358 }
359 }
360 if ix {
361 if len(dx) < len(dy) {
362 return -1
363 }
364 if len(dx) > len(dy) {
365 return +1
366 }
367 }
368 if dx < dy {
369 return -1
370 } else {
371 return +1
372 }
373 }
374 }
375 if x == "" {
376 return -1
377 } else {
378 return +1
379 }
380 }
381
382 func nextIdent(x string) (dx, rest string) {
383 i := 0
384 for i < len(x) && x[i] != '.' {
385 i++
386 }
387 return x[:i], x[i:]
388 }
389
View as plain text