...

Source file src/pkg/cmd/go/internal/semver/semver.go

     1	// Copyright 2018 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	// Package semver implements comparison of semantic version strings.
     6	// In this package, semantic version strings must begin with a leading "v",
     7	// as in "v1.0.0".
     8	//
     9	// The general form of a semantic version string accepted by this package is
    10	//
    11	//	vMAJOR[.MINOR[.PATCH[-PRERELEASE][+BUILD]]]
    12	//
    13	// where square brackets indicate optional parts of the syntax;
    14	// MAJOR, MINOR, and PATCH are decimal integers without extra leading zeros;
    15	// PRERELEASE and BUILD are each a series of non-empty dot-separated identifiers
    16	// using only alphanumeric characters and hyphens; and
    17	// all-numeric PRERELEASE identifiers must not have leading zeros.
    18	//
    19	// This package follows Semantic Versioning 2.0.0 (see semver.org)
    20	// with two exceptions. First, it requires the "v" prefix. Second, it recognizes
    21	// vMAJOR and vMAJOR.MINOR (with no prerelease or build suffixes)
    22	// as shorthands for vMAJOR.0.0 and vMAJOR.MINOR.0.
    23	package semver
    24	
    25	// parsed returns the parsed form of a semantic version string.
    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	// IsValid reports whether v is a valid semantic version string.
    37	func IsValid(v string) bool {
    38		_, ok := parse(v)
    39		return ok
    40	}
    41	
    42	// Canonical returns the canonical formatting of the semantic version v.
    43	// It fills in any missing .MINOR or .PATCH and discards build metadata.
    44	// Two semantic versions compare equal only if their canonical formattings
    45	// are identical strings.
    46	// The canonical invalid semantic version is the empty string.
    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	// Major returns the major version prefix of the semantic version v.
    62	// For example, Major("v2.1.0") == "v2".
    63	// If v is an invalid semantic version string, Major returns the empty string.
    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	// MajorMinor returns the major.minor version prefix of the semantic version v.
    73	// For example, MajorMinor("v2.1.0") == "v2.1".
    74	// If v is an invalid semantic version string, MajorMinor returns the empty string.
    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	// Prerelease returns the prerelease suffix of the semantic version v.
    88	// For example, Prerelease("v2.1.0-pre+meta") == "-pre".
    89	// If v is an invalid semantic version string, Prerelease returns the empty string.
    90	func Prerelease(v string) string {
    91		pv, ok := parse(v)
    92		if !ok {
    93			return ""
    94		}
    95		return pv.prerelease
    96	}
    97	
    98	// Build returns the build suffix of the semantic version v.
    99	// For example, Build("v2.1.0+meta") == "+meta".
   100	// If v is an invalid semantic version string, Build returns the empty string.
   101	func Build(v string) string {
   102		pv, ok := parse(v)
   103		if !ok {
   104			return ""
   105		}
   106		return pv.build
   107	}
   108	
   109	// Compare returns an integer comparing two versions according to
   110	// according to semantic version precedence.
   111	// The result will be 0 if v == w, -1 if v < w, or +1 if v > w.
   112	//
   113	// An invalid semantic version string is considered less than a valid one.
   114	// All invalid semantic version strings compare equal to each other.
   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	// Max canonicalizes its arguments and then returns the version string
   140	// that compares greater.
   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		// "A pre-release version MAY be denoted by appending a hyphen and
   233		// a series of dot separated identifiers immediately following the patch version.
   234		// Identifiers MUST comprise only ASCII alphanumerics and hyphen [0-9A-Za-z-].
   235		// Identifiers MUST NOT be empty. Numeric identifiers MUST NOT include leading zeroes."
   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		// "When major, minor, and patch are equal, a pre-release version has
   322		// lower precedence than a normal version.
   323		// Example: 1.0.0-alpha < 1.0.0.
   324		// Precedence for two pre-release versions with the same major, minor,
   325		// and patch version MUST be determined by comparing each dot separated
   326		// identifier from left to right until a difference is found as follows:
   327		// identifiers consisting of only digits are compared numerically and
   328		// identifiers with letters or hyphens are compared lexically in ASCII
   329		// sort order. Numeric identifiers always have lower precedence than
   330		// non-numeric identifiers. A larger set of pre-release fields has a
   331		// higher precedence than a smaller set, if all of the preceding
   332		// identifiers are equal.
   333		// Example: 1.0.0-alpha < 1.0.0-alpha.1 < 1.0.0-alpha.beta <
   334		// 1.0.0-beta < 1.0.0-beta.2 < 1.0.0-beta.11 < 1.0.0-rc.1 < 1.0.0."
   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:] // skip - or .
   346			y = y[1:] // skip - or .
   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