...

Source file src/github.com/hashicorp/go-version/version.go

Documentation: github.com/hashicorp/go-version

     1  package version
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"reflect"
     7  	"regexp"
     8  	"strconv"
     9  	"strings"
    10  )
    11  
    12  // The compiled regular expression used to test the validity of a version.
    13  var (
    14  	versionRegexp *regexp.Regexp
    15  	semverRegexp  *regexp.Regexp
    16  )
    17  
    18  // The raw regular expression string used for testing the validity
    19  // of a version.
    20  const (
    21  	VersionRegexpRaw string = `v?([0-9]+(\.[0-9]+)*?)` +
    22  		`(-([0-9]+[0-9A-Za-z\-~]*(\.[0-9A-Za-z\-~]+)*)|(-?([A-Za-z\-~]+[0-9A-Za-z\-~]*(\.[0-9A-Za-z\-~]+)*)))?` +
    23  		`(\+([0-9A-Za-z\-~]+(\.[0-9A-Za-z\-~]+)*))?` +
    24  		`?`
    25  
    26  	// SemverRegexpRaw requires a separator between version and prerelease
    27  	SemverRegexpRaw string = `v?([0-9]+(\.[0-9]+)*?)` +
    28  		`(-([0-9]+[0-9A-Za-z\-~]*(\.[0-9A-Za-z\-~]+)*)|(-([A-Za-z\-~]+[0-9A-Za-z\-~]*(\.[0-9A-Za-z\-~]+)*)))?` +
    29  		`(\+([0-9A-Za-z\-~]+(\.[0-9A-Za-z\-~]+)*))?` +
    30  		`?`
    31  )
    32  
    33  // Version represents a single version.
    34  type Version struct {
    35  	metadata string
    36  	pre      string
    37  	segments []int64
    38  	si       int
    39  	original string
    40  }
    41  
    42  func init() {
    43  	versionRegexp = regexp.MustCompile("^" + VersionRegexpRaw + "$")
    44  	semverRegexp = regexp.MustCompile("^" + SemverRegexpRaw + "$")
    45  }
    46  
    47  // NewVersion parses the given version and returns a new
    48  // Version.
    49  func NewVersion(v string) (*Version, error) {
    50  	return newVersion(v, versionRegexp)
    51  }
    52  
    53  // NewSemver parses the given version and returns a new
    54  // Version that adheres strictly to SemVer specs
    55  // https://semver.org/
    56  func NewSemver(v string) (*Version, error) {
    57  	return newVersion(v, semverRegexp)
    58  }
    59  
    60  func newVersion(v string, pattern *regexp.Regexp) (*Version, error) {
    61  	matches := pattern.FindStringSubmatch(v)
    62  	if matches == nil {
    63  		return nil, fmt.Errorf("Malformed version: %s", v)
    64  	}
    65  	segmentsStr := strings.Split(matches[1], ".")
    66  	segments := make([]int64, len(segmentsStr))
    67  	for i, str := range segmentsStr {
    68  		val, err := strconv.ParseInt(str, 10, 64)
    69  		if err != nil {
    70  			return nil, fmt.Errorf(
    71  				"Error parsing version: %s", err)
    72  		}
    73  
    74  		segments[i] = val
    75  	}
    76  
    77  	// Even though we could support more than three segments, if we
    78  	// got less than three, pad it with 0s. This is to cover the basic
    79  	// default usecase of semver, which is MAJOR.MINOR.PATCH at the minimum
    80  	for i := len(segments); i < 3; i++ {
    81  		segments = append(segments, 0)
    82  	}
    83  
    84  	pre := matches[7]
    85  	if pre == "" {
    86  		pre = matches[4]
    87  	}
    88  
    89  	return &Version{
    90  		metadata: matches[10],
    91  		pre:      pre,
    92  		segments: segments,
    93  		si:       len(segmentsStr),
    94  		original: v,
    95  	}, nil
    96  }
    97  
    98  // Must is a helper that wraps a call to a function returning (*Version, error)
    99  // and panics if error is non-nil.
   100  func Must(v *Version, err error) *Version {
   101  	if err != nil {
   102  		panic(err)
   103  	}
   104  
   105  	return v
   106  }
   107  
   108  // Compare compares this version to another version. This
   109  // returns -1, 0, or 1 if this version is smaller, equal,
   110  // or larger than the other version, respectively.
   111  //
   112  // If you want boolean results, use the LessThan, Equal,
   113  // GreaterThan, GreaterThanOrEqual or LessThanOrEqual methods.
   114  func (v *Version) Compare(other *Version) int {
   115  	// A quick, efficient equality check
   116  	if v.String() == other.String() {
   117  		return 0
   118  	}
   119  
   120  	segmentsSelf := v.Segments64()
   121  	segmentsOther := other.Segments64()
   122  
   123  	// If the segments are the same, we must compare on prerelease info
   124  	if reflect.DeepEqual(segmentsSelf, segmentsOther) {
   125  		preSelf := v.Prerelease()
   126  		preOther := other.Prerelease()
   127  		if preSelf == "" && preOther == "" {
   128  			return 0
   129  		}
   130  		if preSelf == "" {
   131  			return 1
   132  		}
   133  		if preOther == "" {
   134  			return -1
   135  		}
   136  
   137  		return comparePrereleases(preSelf, preOther)
   138  	}
   139  
   140  	// Get the highest specificity (hS), or if they're equal, just use segmentSelf length
   141  	lenSelf := len(segmentsSelf)
   142  	lenOther := len(segmentsOther)
   143  	hS := lenSelf
   144  	if lenSelf < lenOther {
   145  		hS = lenOther
   146  	}
   147  	// Compare the segments
   148  	// Because a constraint could have more/less specificity than the version it's
   149  	// checking, we need to account for a lopsided or jagged comparison
   150  	for i := 0; i < hS; i++ {
   151  		if i > lenSelf-1 {
   152  			// This means Self had the lower specificity
   153  			// Check to see if the remaining segments in Other are all zeros
   154  			if !allZero(segmentsOther[i:]) {
   155  				// if not, it means that Other has to be greater than Self
   156  				return -1
   157  			}
   158  			break
   159  		} else if i > lenOther-1 {
   160  			// this means Other had the lower specificity
   161  			// Check to see if the remaining segments in Self are all zeros -
   162  			if !allZero(segmentsSelf[i:]) {
   163  				//if not, it means that Self has to be greater than Other
   164  				return 1
   165  			}
   166  			break
   167  		}
   168  		lhs := segmentsSelf[i]
   169  		rhs := segmentsOther[i]
   170  		if lhs == rhs {
   171  			continue
   172  		} else if lhs < rhs {
   173  			return -1
   174  		}
   175  		// Otherwis, rhs was > lhs, they're not equal
   176  		return 1
   177  	}
   178  
   179  	// if we got this far, they're equal
   180  	return 0
   181  }
   182  
   183  func allZero(segs []int64) bool {
   184  	for _, s := range segs {
   185  		if s != 0 {
   186  			return false
   187  		}
   188  	}
   189  	return true
   190  }
   191  
   192  func comparePart(preSelf string, preOther string) int {
   193  	if preSelf == preOther {
   194  		return 0
   195  	}
   196  
   197  	var selfInt int64
   198  	selfNumeric := true
   199  	selfInt, err := strconv.ParseInt(preSelf, 10, 64)
   200  	if err != nil {
   201  		selfNumeric = false
   202  	}
   203  
   204  	var otherInt int64
   205  	otherNumeric := true
   206  	otherInt, err = strconv.ParseInt(preOther, 10, 64)
   207  	if err != nil {
   208  		otherNumeric = false
   209  	}
   210  
   211  	// if a part is empty, we use the other to decide
   212  	if preSelf == "" {
   213  		if otherNumeric {
   214  			return -1
   215  		}
   216  		return 1
   217  	}
   218  
   219  	if preOther == "" {
   220  		if selfNumeric {
   221  			return 1
   222  		}
   223  		return -1
   224  	}
   225  
   226  	if selfNumeric && !otherNumeric {
   227  		return -1
   228  	} else if !selfNumeric && otherNumeric {
   229  		return 1
   230  	} else if !selfNumeric && !otherNumeric && preSelf > preOther {
   231  		return 1
   232  	} else if selfInt > otherInt {
   233  		return 1
   234  	}
   235  
   236  	return -1
   237  }
   238  
   239  func comparePrereleases(v string, other string) int {
   240  	// the same pre release!
   241  	if v == other {
   242  		return 0
   243  	}
   244  
   245  	// split both pre releases for analyse their parts
   246  	selfPreReleaseMeta := strings.Split(v, ".")
   247  	otherPreReleaseMeta := strings.Split(other, ".")
   248  
   249  	selfPreReleaseLen := len(selfPreReleaseMeta)
   250  	otherPreReleaseLen := len(otherPreReleaseMeta)
   251  
   252  	biggestLen := otherPreReleaseLen
   253  	if selfPreReleaseLen > otherPreReleaseLen {
   254  		biggestLen = selfPreReleaseLen
   255  	}
   256  
   257  	// loop for parts to find the first difference
   258  	for i := 0; i < biggestLen; i = i + 1 {
   259  		partSelfPre := ""
   260  		if i < selfPreReleaseLen {
   261  			partSelfPre = selfPreReleaseMeta[i]
   262  		}
   263  
   264  		partOtherPre := ""
   265  		if i < otherPreReleaseLen {
   266  			partOtherPre = otherPreReleaseMeta[i]
   267  		}
   268  
   269  		compare := comparePart(partSelfPre, partOtherPre)
   270  		// if parts are equals, continue the loop
   271  		if compare != 0 {
   272  			return compare
   273  		}
   274  	}
   275  
   276  	return 0
   277  }
   278  
   279  // Core returns a new version constructed from only the MAJOR.MINOR.PATCH
   280  // segments of the version, without prerelease or metadata.
   281  func (v *Version) Core() *Version {
   282  	segments := v.Segments64()
   283  	segmentsOnly := fmt.Sprintf("%d.%d.%d", segments[0], segments[1], segments[2])
   284  	return Must(NewVersion(segmentsOnly))
   285  }
   286  
   287  // Equal tests if two versions are equal.
   288  func (v *Version) Equal(o *Version) bool {
   289  	if v == nil || o == nil {
   290  		return v == o
   291  	}
   292  
   293  	return v.Compare(o) == 0
   294  }
   295  
   296  // GreaterThan tests if this version is greater than another version.
   297  func (v *Version) GreaterThan(o *Version) bool {
   298  	return v.Compare(o) > 0
   299  }
   300  
   301  // GreaterThanOrEqual tests if this version is greater than or equal to another version.
   302  func (v *Version) GreaterThanOrEqual(o *Version) bool {
   303  	return v.Compare(o) >= 0
   304  }
   305  
   306  // LessThan tests if this version is less than another version.
   307  func (v *Version) LessThan(o *Version) bool {
   308  	return v.Compare(o) < 0
   309  }
   310  
   311  // LessThanOrEqual tests if this version is less than or equal to another version.
   312  func (v *Version) LessThanOrEqual(o *Version) bool {
   313  	return v.Compare(o) <= 0
   314  }
   315  
   316  // Metadata returns any metadata that was part of the version
   317  // string.
   318  //
   319  // Metadata is anything that comes after the "+" in the version.
   320  // For example, with "1.2.3+beta", the metadata is "beta".
   321  func (v *Version) Metadata() string {
   322  	return v.metadata
   323  }
   324  
   325  // Prerelease returns any prerelease data that is part of the version,
   326  // or blank if there is no prerelease data.
   327  //
   328  // Prerelease information is anything that comes after the "-" in the
   329  // version (but before any metadata). For example, with "1.2.3-beta",
   330  // the prerelease information is "beta".
   331  func (v *Version) Prerelease() string {
   332  	return v.pre
   333  }
   334  
   335  // Segments returns the numeric segments of the version as a slice of ints.
   336  //
   337  // This excludes any metadata or pre-release information. For example,
   338  // for a version "1.2.3-beta", segments will return a slice of
   339  // 1, 2, 3.
   340  func (v *Version) Segments() []int {
   341  	segmentSlice := make([]int, len(v.segments))
   342  	for i, v := range v.segments {
   343  		segmentSlice[i] = int(v)
   344  	}
   345  	return segmentSlice
   346  }
   347  
   348  // Segments64 returns the numeric segments of the version as a slice of int64s.
   349  //
   350  // This excludes any metadata or pre-release information. For example,
   351  // for a version "1.2.3-beta", segments will return a slice of
   352  // 1, 2, 3.
   353  func (v *Version) Segments64() []int64 {
   354  	result := make([]int64, len(v.segments))
   355  	copy(result, v.segments)
   356  	return result
   357  }
   358  
   359  // String returns the full version string included pre-release
   360  // and metadata information.
   361  //
   362  // This value is rebuilt according to the parsed segments and other
   363  // information. Therefore, ambiguities in the version string such as
   364  // prefixed zeroes (1.04.0 => 1.4.0), `v` prefix (v1.0.0 => 1.0.0), and
   365  // missing parts (1.0 => 1.0.0) will be made into a canonicalized form
   366  // as shown in the parenthesized examples.
   367  func (v *Version) String() string {
   368  	var buf bytes.Buffer
   369  	fmtParts := make([]string, len(v.segments))
   370  	for i, s := range v.segments {
   371  		// We can ignore err here since we've pre-parsed the values in segments
   372  		str := strconv.FormatInt(s, 10)
   373  		fmtParts[i] = str
   374  	}
   375  	fmt.Fprintf(&buf, strings.Join(fmtParts, "."))
   376  	if v.pre != "" {
   377  		fmt.Fprintf(&buf, "-%s", v.pre)
   378  	}
   379  	if v.metadata != "" {
   380  		fmt.Fprintf(&buf, "+%s", v.metadata)
   381  	}
   382  
   383  	return buf.String()
   384  }
   385  
   386  // Original returns the original parsed version as-is, including any
   387  // potential whitespace, `v` prefix, etc.
   388  func (v *Version) Original() string {
   389  	return v.original
   390  }
   391  
   392  // UnmarshalText implements encoding.TextUnmarshaler interface.
   393  func (v *Version) UnmarshalText(b []byte) error {
   394  	temp, err := NewVersion(string(b))
   395  	if err != nil {
   396  		return err
   397  	}
   398  
   399  	*v = *temp
   400  
   401  	return nil
   402  }
   403  
   404  // MarshalText implements encoding.TextMarshaler interface.
   405  func (v *Version) MarshalText() ([]byte, error) {
   406  	return []byte(v.String()), nil
   407  }
   408  

View as plain text