12 // The compiled regular expression used to test the validity of a version.
13 var versionRegexp *regexp.Regexp
15 // The raw regular expression string used for testing the validity
17 const VersionRegexpRaw string = `v?([0-9]+(\.[0-9]+)*?)` +
18 `(-([0-9]+[0-9A-Za-z\-~]*(\.[0-9A-Za-z\-~]+)*)|(-?([A-Za-z\-~]+[0-9A-Za-z\-~]*(\.[0-9A-Za-z\-~]+)*)))?` +
19 `(\+([0-9A-Za-z\-~]+(\.[0-9A-Za-z\-~]+)*))?` +
22 // Version represents a single version.
32 versionRegexp = regexp.MustCompile("^" + VersionRegexpRaw + "$")
35 // NewVersion parses the given version and returns a new
37 func NewVersion(v string) (*Version, error) {
38 matches := versionRegexp.FindStringSubmatch(v)
40 return nil, fmt.Errorf("Malformed version: %s", v)
42 segmentsStr := strings.Split(matches[1], ".")
43 segments := make([]int64, len(segmentsStr))
45 for i, str := range segmentsStr {
46 val, err := strconv.ParseInt(str, 10, 64)
48 return nil, fmt.Errorf(
49 "Error parsing version: %s", err)
52 segments[i] = int64(val)
56 // Even though we could support more than three segments, if we
57 // got less than three, pad it with 0s. This is to cover the basic
58 // default usecase of semver, which is MAJOR.MINOR.PATCH at the minimum
59 for i := len(segments); i < 3; i++ {
60 segments = append(segments, 0)
69 metadata: matches[10],
77 // Must is a helper that wraps a call to a function returning (*Version, error)
78 // and panics if error is non-nil.
79 func Must(v *Version, err error) *Version {
87 // Compare compares this version to another version. This
88 // returns -1, 0, or 1 if this version is smaller, equal,
89 // or larger than the other version, respectively.
91 // If you want boolean results, use the LessThan, Equal,
92 // or GreaterThan methods.
93 func (v *Version) Compare(other *Version) int {
94 // A quick, efficient equality check
95 if v.String() == other.String() {
99 segmentsSelf := v.Segments64()
100 segmentsOther := other.Segments64()
102 // If the segments are the same, we must compare on prerelease info
103 if reflect.DeepEqual(segmentsSelf, segmentsOther) {
104 preSelf := v.Prerelease()
105 preOther := other.Prerelease()
106 if preSelf == "" && preOther == "" {
116 return comparePrereleases(preSelf, preOther)
119 // Get the highest specificity (hS), or if they're equal, just use segmentSelf length
120 lenSelf := len(segmentsSelf)
121 lenOther := len(segmentsOther)
123 if lenSelf < lenOther {
126 // Compare the segments
127 // Because a constraint could have more/less specificity than the version it's
128 // checking, we need to account for a lopsided or jagged comparison
129 for i := 0; i < hS; i++ {
131 // This means Self had the lower specificity
132 // Check to see if the remaining segments in Other are all zeros
133 if !allZero(segmentsOther[i:]) {
134 // if not, it means that Other has to be greater than Self
138 } else if i > lenOther-1 {
139 // this means Other had the lower specificity
140 // Check to see if the remaining segments in Self are all zeros -
141 if !allZero(segmentsSelf[i:]) {
142 //if not, it means that Self has to be greater than Other
147 lhs := segmentsSelf[i]
148 rhs := segmentsOther[i]
151 } else if lhs < rhs {
154 // Otherwis, rhs was > lhs, they're not equal
158 // if we got this far, they're equal
162 func allZero(segs []int64) bool {
163 for _, s := range segs {
171 func comparePart(preSelf string, preOther string) int {
172 if preSelf == preOther {
178 selfInt, err := strconv.ParseInt(preSelf, 10, 64)
185 otherInt, err = strconv.ParseInt(preOther, 10, 64)
190 // if a part is empty, we use the other to decide
205 if selfNumeric && !otherNumeric {
207 } else if !selfNumeric && otherNumeric {
209 } else if !selfNumeric && !otherNumeric && preSelf > preOther {
211 } else if selfInt > otherInt {
218 func comparePrereleases(v string, other string) int {
219 // the same pre release!
224 // split both pre releases for analyse their parts
225 selfPreReleaseMeta := strings.Split(v, ".")
226 otherPreReleaseMeta := strings.Split(other, ".")
228 selfPreReleaseLen := len(selfPreReleaseMeta)
229 otherPreReleaseLen := len(otherPreReleaseMeta)
231 biggestLen := otherPreReleaseLen
232 if selfPreReleaseLen > otherPreReleaseLen {
233 biggestLen = selfPreReleaseLen
236 // loop for parts to find the first difference
237 for i := 0; i < biggestLen; i = i + 1 {
239 if i < selfPreReleaseLen {
240 partSelfPre = selfPreReleaseMeta[i]
244 if i < otherPreReleaseLen {
245 partOtherPre = otherPreReleaseMeta[i]
248 compare := comparePart(partSelfPre, partOtherPre)
249 // if parts are equals, continue the loop
258 // Equal tests if two versions are equal.
259 func (v *Version) Equal(o *Version) bool {
260 return v.Compare(o) == 0
263 // GreaterThan tests if this version is greater than another version.
264 func (v *Version) GreaterThan(o *Version) bool {
265 return v.Compare(o) > 0
268 // LessThan tests if this version is less than another version.
269 func (v *Version) LessThan(o *Version) bool {
270 return v.Compare(o) < 0
273 // Metadata returns any metadata that was part of the version
276 // Metadata is anything that comes after the "+" in the version.
277 // For example, with "1.2.3+beta", the metadata is "beta".
278 func (v *Version) Metadata() string {
282 // Prerelease returns any prerelease data that is part of the version,
283 // or blank if there is no prerelease data.
285 // Prerelease information is anything that comes after the "-" in the
286 // version (but before any metadata). For example, with "1.2.3-beta",
287 // the prerelease information is "beta".
288 func (v *Version) Prerelease() string {
292 // Segments returns the numeric segments of the version as a slice of ints.
294 // This excludes any metadata or pre-release information. For example,
295 // for a version "1.2.3-beta", segments will return a slice of
297 func (v *Version) Segments() []int {
298 segmentSlice := make([]int, len(v.segments))
299 for i, v := range v.segments {
300 segmentSlice[i] = int(v)
305 // Segments64 returns the numeric segments of the version as a slice of int64s.
307 // This excludes any metadata or pre-release information. For example,
308 // for a version "1.2.3-beta", segments will return a slice of
310 func (v *Version) Segments64() []int64 {
311 result := make([]int64, len(v.segments))
312 copy(result, v.segments)
316 // String returns the full version string included pre-release
317 // and metadata information.
319 // This value is rebuilt according to the parsed segments and other
320 // information. Therefore, ambiguities in the version string such as
321 // prefixed zeroes (1.04.0 => 1.4.0), `v` prefix (v1.0.0 => 1.0.0), and
322 // missing parts (1.0 => 1.0.0) will be made into a canonicalized form
323 // as shown in the parenthesized examples.
324 func (v *Version) String() string {
326 fmtParts := make([]string, len(v.segments))
327 for i, s := range v.segments {
328 // We can ignore err here since we've pre-parsed the values in segments
329 str := strconv.FormatInt(s, 10)
332 fmt.Fprintf(&buf, strings.Join(fmtParts, "."))
334 fmt.Fprintf(&buf, "-%s", v.pre)
336 if v.metadata != "" {
337 fmt.Fprintf(&buf, "+%s", v.metadata)
343 // Original returns the original parsed version as-is, including any
344 // potential whitespace, `v` prefix, etc.
345 func (v *Version) Original() string {