1
// The version package implements semantic versions as described
2
// at http://semver.org/
1
// The version package implements version parsing.
2
// It also acts as guardian of the current client Juju version number.
12
// Version represents a parsed version. See http://semvar.org/ for
13
// detailed description of the various components.
11
var ClientVersion = MustParse("0.0.0")
13
// Version represents a parsed version.
14
14
type Version struct {
15
Major int // The major version number.
16
Minor int // The minor version number.
17
Patch int // The patch version number.
18
Prerelease []string // The pre-release version (dot-separated elements)
19
Build []string // The build version (dot-separated elements)
22
var charClasses = strings.NewReplacer("d", `[0-9]`, "c", `[\-0-9a-z]`)
24
const pattern = `^(d{1,9})\.(d{1,9})\.(d{1,9})(-c+(\.c+)*)?(\+c+(\.c+)*)?$`
26
var versionPat = regexp.MustCompile(charClasses.Replace(pattern))
28
// Parse parses the version, which is of one of the following forms:
32
// 1.2.3-prerelease+build
33
func Parse(s string) (*Version, error) {
15
Major int // The major version number.
16
Minor int // The minor version number.
17
Patch int // The patch version number.
20
var versionPat = regexp.MustCompile(`^([0-9]{1,9})\.([0-9]{1,9})\.([0-9]{1,9})$`)
22
// MustParse parses a version and panics if it does
23
// not parse correctly.
24
func MustParse(s string) Version {
27
panic(fmt.Errorf("version: cannot parse %q: %v", s, err))
32
// Parse parses the version, which is of the form 1.2.3
33
// giving the major, minor and release versions
35
func Parse(s string) (Version, error) {
34
36
m := versionPat.FindStringSubmatch(s)
36
return nil, fmt.Errorf("invalid version %q", s)
38
return Version{}, fmt.Errorf("invalid version %q", s)
39
41
v.Major = atoi(m[1])
40
42
v.Minor = atoi(m[2])
41
43
v.Patch = atoi(m[3])
43
v.Prerelease = strings.Split(m[4][1:], ".")
46
v.Build = strings.Split(m[6][1:], ".")
61
57
func (v *Version) String() string {
63
if v.Prerelease != nil {
64
pre = "-" + strings.Join(v.Prerelease, ".")
67
build = "+" + strings.Join(v.Build, ".")
69
return fmt.Sprintf("%d.%d.%d%s%s", v.Major, v.Minor, v.Patch, pre, build)
72
func allDigits(s string) bool {
74
if c < '0' || c > '9' {
81
// lessIds returns whether the slice of identifiers a is less than b,
82
// as specified in semver.org,
83
func lessIds(a, b []string) (v bool) {
85
for ; i < len(a) && i < len(b); i++ {
86
if c := cmp(a[i], b[i]); c != 0 {
93
// lessIds returns whether the slice of identifiers a is equal to b,
94
// as specified in semver.org,
95
func eqIds(a, b []string) bool {
100
if cmp(s, b[i]) != 0 {
107
// cmp implements comparison of identifiers as specified at semver.org.
108
// It returns 1, -1 or 0 if a is greater-than, less-than or equal to b,
111
// Identifiers consisting of only digits are compared numerically and
112
// identifiers with letters or dashes are compared lexically in ASCII
113
// sort order. Numeric identifiers always have lower precedence than
114
// non-numeric identifiers.
115
func cmp(a, b string) int {
116
numa, numb := allDigits(a), allDigits(b)
132
// numCmp 1, -1 or 0 depending on whether the known-to-be-all-digits
133
// strings a and b are numerically greater than, less than or equal to
134
// each other. Avoiding the conversion means we can work correctly with
135
// very long version numbers.
136
func numCmp(a, b string) int {
137
a = strings.TrimLeft(a, "0")
138
b = strings.TrimLeft(b, "0")
140
case len(a) < len(b):
142
case len(a) > len(b):
58
return fmt.Sprintf("%d.%d.%d", v.Major, v.Minor, v.Patch)
152
61
// Less returns whether v is semantically earlier in the
153
62
// version sequence than w.
154
func (v *Version) Less(w *Version) bool {
63
func (v Version) Less(w Version) bool {
156
65
case v.Major != w.Major:
157
66
return v.Major < w.Major
159
68
return v.Minor < w.Minor
160
69
case v.Patch != w.Patch:
161
70
return v.Patch < w.Patch
162
case !eqIds(v.Prerelease, w.Prerelease):
163
if v.Prerelease == nil || w.Prerelease == nil {
164
return v.Prerelease != nil
166
return lessIds(v.Prerelease, w.Prerelease)
167
case !eqIds(v.Build, w.Build):
168
return lessIds(v.Build, w.Build)
173
// Compatible returns whether v is compatible
174
// with some installed version w.
175
func (v *Version) Compatible(w *Version) bool {
176
if v.Major != w.Major {
179
if v.Minor > w.Minor {
182
// If the installed version is a pre-release version,
183
// we're not compatible with it unless all version
185
if w.Prerelease != nil {
186
return v.Minor == w.Minor && v.Patch == w.Patch
75
func isOdd(x int) bool {
79
// IsDev returns whether the version represents a development
80
// version. A version with an odd-numbered major, minor
81
// or patch version is considered to be a development version.
82
func (v Version) IsDev() bool {
83
return isOdd(v.Major) ||