mirror of
https://github.com/letic/terraform-provider-google.git
synced 2024-09-18 17:19:58 +00:00
961c878e0d
Switch to using Go modules. This migrates our vendor.json to use Go 1.11's modules system, and replaces the vendor folder with the output of go mod vendor. The vendored code should remain basically the same; I believe some tree shaking of packages and support scripts/licenses/READMEs/etc. happened. This also fixes Travis and our Makefile to no longer use govendor.
348 lines
8.4 KiB
Go
348 lines
8.4 KiB
Go
package version
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"reflect"
|
|
"regexp"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
// The compiled regular expression used to test the validity of a version.
|
|
var versionRegexp *regexp.Regexp
|
|
|
|
// The raw regular expression string used for testing the validity
|
|
// of a version.
|
|
const VersionRegexpRaw string = `v?([0-9]+(\.[0-9]+)*?)` +
|
|
`(-([0-9]+[0-9A-Za-z\-~]*(\.[0-9A-Za-z\-~]+)*)|(-?([A-Za-z\-~]+[0-9A-Za-z\-~]*(\.[0-9A-Za-z\-~]+)*)))?` +
|
|
`(\+([0-9A-Za-z\-~]+(\.[0-9A-Za-z\-~]+)*))?` +
|
|
`?`
|
|
|
|
// Version represents a single version.
|
|
type Version struct {
|
|
metadata string
|
|
pre string
|
|
segments []int64
|
|
si int
|
|
original string
|
|
}
|
|
|
|
func init() {
|
|
versionRegexp = regexp.MustCompile("^" + VersionRegexpRaw + "$")
|
|
}
|
|
|
|
// NewVersion parses the given version and returns a new
|
|
// Version.
|
|
func NewVersion(v string) (*Version, error) {
|
|
matches := versionRegexp.FindStringSubmatch(v)
|
|
if matches == nil {
|
|
return nil, fmt.Errorf("Malformed version: %s", v)
|
|
}
|
|
segmentsStr := strings.Split(matches[1], ".")
|
|
segments := make([]int64, len(segmentsStr))
|
|
si := 0
|
|
for i, str := range segmentsStr {
|
|
val, err := strconv.ParseInt(str, 10, 64)
|
|
if err != nil {
|
|
return nil, fmt.Errorf(
|
|
"Error parsing version: %s", err)
|
|
}
|
|
|
|
segments[i] = int64(val)
|
|
si++
|
|
}
|
|
|
|
// Even though we could support more than three segments, if we
|
|
// got less than three, pad it with 0s. This is to cover the basic
|
|
// default usecase of semver, which is MAJOR.MINOR.PATCH at the minimum
|
|
for i := len(segments); i < 3; i++ {
|
|
segments = append(segments, 0)
|
|
}
|
|
|
|
pre := matches[7]
|
|
if pre == "" {
|
|
pre = matches[4]
|
|
}
|
|
|
|
return &Version{
|
|
metadata: matches[10],
|
|
pre: pre,
|
|
segments: segments,
|
|
si: si,
|
|
original: v,
|
|
}, nil
|
|
}
|
|
|
|
// Must is a helper that wraps a call to a function returning (*Version, error)
|
|
// and panics if error is non-nil.
|
|
func Must(v *Version, err error) *Version {
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
return v
|
|
}
|
|
|
|
// Compare compares this version to another version. This
|
|
// returns -1, 0, or 1 if this version is smaller, equal,
|
|
// or larger than the other version, respectively.
|
|
//
|
|
// If you want boolean results, use the LessThan, Equal,
|
|
// or GreaterThan methods.
|
|
func (v *Version) Compare(other *Version) int {
|
|
// A quick, efficient equality check
|
|
if v.String() == other.String() {
|
|
return 0
|
|
}
|
|
|
|
segmentsSelf := v.Segments64()
|
|
segmentsOther := other.Segments64()
|
|
|
|
// If the segments are the same, we must compare on prerelease info
|
|
if reflect.DeepEqual(segmentsSelf, segmentsOther) {
|
|
preSelf := v.Prerelease()
|
|
preOther := other.Prerelease()
|
|
if preSelf == "" && preOther == "" {
|
|
return 0
|
|
}
|
|
if preSelf == "" {
|
|
return 1
|
|
}
|
|
if preOther == "" {
|
|
return -1
|
|
}
|
|
|
|
return comparePrereleases(preSelf, preOther)
|
|
}
|
|
|
|
// Get the highest specificity (hS), or if they're equal, just use segmentSelf length
|
|
lenSelf := len(segmentsSelf)
|
|
lenOther := len(segmentsOther)
|
|
hS := lenSelf
|
|
if lenSelf < lenOther {
|
|
hS = lenOther
|
|
}
|
|
// Compare the segments
|
|
// Because a constraint could have more/less specificity than the version it's
|
|
// checking, we need to account for a lopsided or jagged comparison
|
|
for i := 0; i < hS; i++ {
|
|
if i > lenSelf-1 {
|
|
// This means Self had the lower specificity
|
|
// Check to see if the remaining segments in Other are all zeros
|
|
if !allZero(segmentsOther[i:]) {
|
|
// if not, it means that Other has to be greater than Self
|
|
return -1
|
|
}
|
|
break
|
|
} else if i > lenOther-1 {
|
|
// this means Other had the lower specificity
|
|
// Check to see if the remaining segments in Self are all zeros -
|
|
if !allZero(segmentsSelf[i:]) {
|
|
//if not, it means that Self has to be greater than Other
|
|
return 1
|
|
}
|
|
break
|
|
}
|
|
lhs := segmentsSelf[i]
|
|
rhs := segmentsOther[i]
|
|
if lhs == rhs {
|
|
continue
|
|
} else if lhs < rhs {
|
|
return -1
|
|
}
|
|
// Otherwis, rhs was > lhs, they're not equal
|
|
return 1
|
|
}
|
|
|
|
// if we got this far, they're equal
|
|
return 0
|
|
}
|
|
|
|
func allZero(segs []int64) bool {
|
|
for _, s := range segs {
|
|
if s != 0 {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
func comparePart(preSelf string, preOther string) int {
|
|
if preSelf == preOther {
|
|
return 0
|
|
}
|
|
|
|
var selfInt int64
|
|
selfNumeric := true
|
|
selfInt, err := strconv.ParseInt(preSelf, 10, 64)
|
|
if err != nil {
|
|
selfNumeric = false
|
|
}
|
|
|
|
var otherInt int64
|
|
otherNumeric := true
|
|
otherInt, err = strconv.ParseInt(preOther, 10, 64)
|
|
if err != nil {
|
|
otherNumeric = false
|
|
}
|
|
|
|
// if a part is empty, we use the other to decide
|
|
if preSelf == "" {
|
|
if otherNumeric {
|
|
return -1
|
|
}
|
|
return 1
|
|
}
|
|
|
|
if preOther == "" {
|
|
if selfNumeric {
|
|
return 1
|
|
}
|
|
return -1
|
|
}
|
|
|
|
if selfNumeric && !otherNumeric {
|
|
return -1
|
|
} else if !selfNumeric && otherNumeric {
|
|
return 1
|
|
} else if !selfNumeric && !otherNumeric && preSelf > preOther {
|
|
return 1
|
|
} else if selfInt > otherInt {
|
|
return 1
|
|
}
|
|
|
|
return -1
|
|
}
|
|
|
|
func comparePrereleases(v string, other string) int {
|
|
// the same pre release!
|
|
if v == other {
|
|
return 0
|
|
}
|
|
|
|
// split both pre releases for analyse their parts
|
|
selfPreReleaseMeta := strings.Split(v, ".")
|
|
otherPreReleaseMeta := strings.Split(other, ".")
|
|
|
|
selfPreReleaseLen := len(selfPreReleaseMeta)
|
|
otherPreReleaseLen := len(otherPreReleaseMeta)
|
|
|
|
biggestLen := otherPreReleaseLen
|
|
if selfPreReleaseLen > otherPreReleaseLen {
|
|
biggestLen = selfPreReleaseLen
|
|
}
|
|
|
|
// loop for parts to find the first difference
|
|
for i := 0; i < biggestLen; i = i + 1 {
|
|
partSelfPre := ""
|
|
if i < selfPreReleaseLen {
|
|
partSelfPre = selfPreReleaseMeta[i]
|
|
}
|
|
|
|
partOtherPre := ""
|
|
if i < otherPreReleaseLen {
|
|
partOtherPre = otherPreReleaseMeta[i]
|
|
}
|
|
|
|
compare := comparePart(partSelfPre, partOtherPre)
|
|
// if parts are equals, continue the loop
|
|
if compare != 0 {
|
|
return compare
|
|
}
|
|
}
|
|
|
|
return 0
|
|
}
|
|
|
|
// Equal tests if two versions are equal.
|
|
func (v *Version) Equal(o *Version) bool {
|
|
return v.Compare(o) == 0
|
|
}
|
|
|
|
// GreaterThan tests if this version is greater than another version.
|
|
func (v *Version) GreaterThan(o *Version) bool {
|
|
return v.Compare(o) > 0
|
|
}
|
|
|
|
// LessThan tests if this version is less than another version.
|
|
func (v *Version) LessThan(o *Version) bool {
|
|
return v.Compare(o) < 0
|
|
}
|
|
|
|
// Metadata returns any metadata that was part of the version
|
|
// string.
|
|
//
|
|
// Metadata is anything that comes after the "+" in the version.
|
|
// For example, with "1.2.3+beta", the metadata is "beta".
|
|
func (v *Version) Metadata() string {
|
|
return v.metadata
|
|
}
|
|
|
|
// Prerelease returns any prerelease data that is part of the version,
|
|
// or blank if there is no prerelease data.
|
|
//
|
|
// Prerelease information is anything that comes after the "-" in the
|
|
// version (but before any metadata). For example, with "1.2.3-beta",
|
|
// the prerelease information is "beta".
|
|
func (v *Version) Prerelease() string {
|
|
return v.pre
|
|
}
|
|
|
|
// Segments returns the numeric segments of the version as a slice of ints.
|
|
//
|
|
// This excludes any metadata or pre-release information. For example,
|
|
// for a version "1.2.3-beta", segments will return a slice of
|
|
// 1, 2, 3.
|
|
func (v *Version) Segments() []int {
|
|
segmentSlice := make([]int, len(v.segments))
|
|
for i, v := range v.segments {
|
|
segmentSlice[i] = int(v)
|
|
}
|
|
return segmentSlice
|
|
}
|
|
|
|
// Segments64 returns the numeric segments of the version as a slice of int64s.
|
|
//
|
|
// This excludes any metadata or pre-release information. For example,
|
|
// for a version "1.2.3-beta", segments will return a slice of
|
|
// 1, 2, 3.
|
|
func (v *Version) Segments64() []int64 {
|
|
result := make([]int64, len(v.segments))
|
|
copy(result, v.segments)
|
|
return result
|
|
}
|
|
|
|
// String returns the full version string included pre-release
|
|
// and metadata information.
|
|
//
|
|
// This value is rebuilt according to the parsed segments and other
|
|
// information. Therefore, ambiguities in the version string such as
|
|
// prefixed zeroes (1.04.0 => 1.4.0), `v` prefix (v1.0.0 => 1.0.0), and
|
|
// missing parts (1.0 => 1.0.0) will be made into a canonicalized form
|
|
// as shown in the parenthesized examples.
|
|
func (v *Version) String() string {
|
|
var buf bytes.Buffer
|
|
fmtParts := make([]string, len(v.segments))
|
|
for i, s := range v.segments {
|
|
// We can ignore err here since we've pre-parsed the values in segments
|
|
str := strconv.FormatInt(s, 10)
|
|
fmtParts[i] = str
|
|
}
|
|
fmt.Fprintf(&buf, strings.Join(fmtParts, "."))
|
|
if v.pre != "" {
|
|
fmt.Fprintf(&buf, "-%s", v.pre)
|
|
}
|
|
if v.metadata != "" {
|
|
fmt.Fprintf(&buf, "+%s", v.metadata)
|
|
}
|
|
|
|
return buf.String()
|
|
}
|
|
|
|
// Original returns the original parsed version as-is, including any
|
|
// potential whitespace, `v` prefix, etc.
|
|
func (v *Version) Original() string {
|
|
return v.original
|
|
}
|