~rogpeppe/juju-core/azure

« back to all changes in this revision

Viewing changes to version/version.go

  • Committer: Roger Peppe
  • Date: 2012-04-25 15:54:21 UTC
  • mto: (130.2.2 go-upload-executables)
  • mto: This revision was merged to the branch mainline in revision 132.
  • Revision ID: roger.peppe@canonical.com-20120425155421-tmp9p3xdtep7m86i
version: simplify

Show diffs side-by-side

added added

removed removed

Lines of Context:
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.
3
3
package version
4
4
 
5
5
import (
6
6
        "fmt"
7
7
        "regexp"
8
8
        "strconv"
9
 
        "strings"
10
9
)
11
10
 
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")
 
12
 
 
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)
20
 
}
21
 
 
22
 
var charClasses = strings.NewReplacer("d", `[0-9]`, "c", `[\-0-9a-z]`)
23
 
 
24
 
const pattern = `^(d{1,9})\.(d{1,9})\.(d{1,9})(-c+(\.c+)*)?(\+c+(\.c+)*)?$`
25
 
 
26
 
var versionPat = regexp.MustCompile(charClasses.Replace(pattern))
27
 
 
28
 
// Parse parses the version, which is of one of the following forms:
29
 
//     1.2.3
30
 
//     1.2.3-prerelease
31
 
//     1.2.3+build
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.
 
18
}
 
19
 
 
20
var versionPat = regexp.MustCompile(`^([0-9]{1,9})\.([0-9]{1,9})\.([0-9]{1,9})$`)
 
21
 
 
22
// MustParse parses a version and panics if it does
 
23
// not parse correctly.
 
24
func MustParse(s string) Version {
 
25
        v, err := Parse(s)
 
26
        if err != nil {
 
27
                panic(fmt.Errorf("version: cannot parse %q: %v", s, err))
 
28
        }
 
29
        return v
 
30
}
 
31
 
 
32
// Parse parses the version, which is of the form 1.2.3
 
33
// giving the major, minor and release versions
 
34
// respectively.
 
35
func Parse(s string) (Version, error) {
34
36
        m := versionPat.FindStringSubmatch(s)
35
37
        if m == nil {
36
 
                return nil, fmt.Errorf("invalid version %q", s)
 
38
                return Version{}, fmt.Errorf("invalid version %q", s)
37
39
        }
38
 
        v := new(Version)
 
40
        var v Version
39
41
        v.Major = atoi(m[1])
40
42
        v.Minor = atoi(m[2])
41
43
        v.Patch = atoi(m[3])
42
 
        if m[4] != "" {
43
 
                v.Prerelease = strings.Split(m[4][1:], ".")
44
 
        }
45
 
        if m[6] != "" {
46
 
                v.Build = strings.Split(m[6][1:], ".")
47
 
        }
48
44
        return v, nil
49
45
}
50
46
 
59
55
}
60
56
 
61
57
func (v *Version) String() string {
62
 
        var pre, build string
63
 
        if v.Prerelease != nil {
64
 
                pre = "-" + strings.Join(v.Prerelease, ".")
65
 
        }
66
 
        if v.Build != nil {
67
 
                build = "+" + strings.Join(v.Build, ".")
68
 
        }
69
 
        return fmt.Sprintf("%d.%d.%d%s%s", v.Major, v.Minor, v.Patch, pre, build)
70
 
}
71
 
 
72
 
func allDigits(s string) bool {
73
 
        for _, c := range s {
74
 
                if c < '0' || c > '9' {
75
 
                        return false
76
 
                }
77
 
        }
78
 
        return true
79
 
}
80
 
 
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) {
84
 
        i := 0
85
 
        for ; i < len(a) && i < len(b); i++ {
86
 
                if c := cmp(a[i], b[i]); c != 0 {
87
 
                        return c < 0
88
 
                }
89
 
        }
90
 
        return i < len(b)
91
 
}
92
 
 
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 {
96
 
        if len(a) != len(b) {
97
 
                return false
98
 
        }
99
 
        for i, s := range a {
100
 
                if cmp(s, b[i]) != 0 {
101
 
                        return false
102
 
                }
103
 
        }
104
 
        return true
105
 
}
106
 
 
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,
109
 
// respectively.
110
 
//
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)
117
 
        switch {
118
 
        case numa && numb:
119
 
                return numCmp(a, b)
120
 
        case numa:
121
 
                return -1
122
 
        case numb:
123
 
                return 1
124
 
        case a < b:
125
 
                return -1
126
 
        case a > b:
127
 
                return 1
128
 
        }
129
 
        return 0
130
 
}
131
 
 
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")
139
 
        switch {
140
 
        case len(a) < len(b):
141
 
                return -1
142
 
        case len(a) > len(b):
143
 
                return 1
144
 
        case a < b:
145
 
                return -1
146
 
        case a > b:
147
 
                return 1
148
 
        }
149
 
        return 0
 
58
        return fmt.Sprintf("%d.%d.%d", v.Major, v.Minor, v.Patch)
150
59
}
151
60
 
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 {
155
64
        switch {
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
165
 
                }
166
 
                return lessIds(v.Prerelease, w.Prerelease)
167
 
        case !eqIds(v.Build, w.Build):
168
 
                return lessIds(v.Build, w.Build)
169
71
        }
170
72
        return false
171
73
}
172
74
 
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 {
177
 
                return false
178
 
        }
179
 
        if v.Minor > w.Minor {
180
 
                return false
181
 
        }
182
 
        // If the installed version is a pre-release version,
183
 
        // we're not compatible with it unless all version
184
 
        // numbers match.
185
 
        if w.Prerelease != nil {
186
 
                return v.Minor == w.Minor && v.Patch == w.Patch
187
 
        }
188
 
        return true
 
75
func isOdd(x int) bool {
 
76
        return x%2 != 0
 
77
}
 
78
 
 
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) ||
 
84
                isOdd(v.Minor) ||
 
85
                isOdd(v.Patch)
189
86
}