github.com/spotmaxtech/k8s-apimachinery-v0260@v0.0.1/pkg/util/version/version.go (about)

     1  /*
     2  Copyright 2016 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package version
    18  
    19  import (
    20  	"bytes"
    21  	"fmt"
    22  	"regexp"
    23  	"strconv"
    24  	"strings"
    25  )
    26  
    27  // Version is an opaque representation of a version number
    28  type Version struct {
    29  	components    []uint
    30  	semver        bool
    31  	preRelease    string
    32  	buildMetadata string
    33  }
    34  
    35  var (
    36  	// versionMatchRE splits a version string into numeric and "extra" parts
    37  	versionMatchRE = regexp.MustCompile(`^\s*v?([0-9]+(?:\.[0-9]+)*)(.*)*$`)
    38  	// extraMatchRE splits the "extra" part of versionMatchRE into semver pre-release and build metadata; it does not validate the "no leading zeroes" constraint for pre-release
    39  	extraMatchRE = regexp.MustCompile(`^(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?\s*$`)
    40  )
    41  
    42  func parse(str string, semver bool) (*Version, error) {
    43  	parts := versionMatchRE.FindStringSubmatch(str)
    44  	if parts == nil {
    45  		return nil, fmt.Errorf("could not parse %q as version", str)
    46  	}
    47  	numbers, extra := parts[1], parts[2]
    48  
    49  	components := strings.Split(numbers, ".")
    50  	if (semver && len(components) != 3) || (!semver && len(components) < 2) {
    51  		return nil, fmt.Errorf("illegal version string %q", str)
    52  	}
    53  
    54  	v := &Version{
    55  		components: make([]uint, len(components)),
    56  		semver:     semver,
    57  	}
    58  	for i, comp := range components {
    59  		if (i == 0 || semver) && strings.HasPrefix(comp, "0") && comp != "0" {
    60  			return nil, fmt.Errorf("illegal zero-prefixed version component %q in %q", comp, str)
    61  		}
    62  		num, err := strconv.ParseUint(comp, 10, 0)
    63  		if err != nil {
    64  			return nil, fmt.Errorf("illegal non-numeric version component %q in %q: %v", comp, str, err)
    65  		}
    66  		v.components[i] = uint(num)
    67  	}
    68  
    69  	if semver && extra != "" {
    70  		extraParts := extraMatchRE.FindStringSubmatch(extra)
    71  		if extraParts == nil {
    72  			return nil, fmt.Errorf("could not parse pre-release/metadata (%s) in version %q", extra, str)
    73  		}
    74  		v.preRelease, v.buildMetadata = extraParts[1], extraParts[2]
    75  
    76  		for _, comp := range strings.Split(v.preRelease, ".") {
    77  			if _, err := strconv.ParseUint(comp, 10, 0); err == nil {
    78  				if strings.HasPrefix(comp, "0") && comp != "0" {
    79  					return nil, fmt.Errorf("illegal zero-prefixed version component %q in %q", comp, str)
    80  				}
    81  			}
    82  		}
    83  	}
    84  
    85  	return v, nil
    86  }
    87  
    88  // ParseGeneric parses a "generic" version string. The version string must consist of two
    89  // or more dot-separated numeric fields (the first of which can't have leading zeroes),
    90  // followed by arbitrary uninterpreted data (which need not be separated from the final
    91  // numeric field by punctuation). For convenience, leading and trailing whitespace is
    92  // ignored, and the version can be preceded by the letter "v". See also ParseSemantic.
    93  func ParseGeneric(str string) (*Version, error) {
    94  	return parse(str, false)
    95  }
    96  
    97  // MustParseGeneric is like ParseGeneric except that it panics on error
    98  func MustParseGeneric(str string) *Version {
    99  	v, err := ParseGeneric(str)
   100  	if err != nil {
   101  		panic(err)
   102  	}
   103  	return v
   104  }
   105  
   106  // ParseSemantic parses a version string that exactly obeys the syntax and semantics of
   107  // the "Semantic Versioning" specification (http://semver.org/) (although it ignores
   108  // leading and trailing whitespace, and allows the version to be preceded by "v"). For
   109  // version strings that are not guaranteed to obey the Semantic Versioning syntax, use
   110  // ParseGeneric.
   111  func ParseSemantic(str string) (*Version, error) {
   112  	return parse(str, true)
   113  }
   114  
   115  // MustParseSemantic is like ParseSemantic except that it panics on error
   116  func MustParseSemantic(str string) *Version {
   117  	v, err := ParseSemantic(str)
   118  	if err != nil {
   119  		panic(err)
   120  	}
   121  	return v
   122  }
   123  
   124  // Major returns the major release number
   125  func (v *Version) Major() uint {
   126  	return v.components[0]
   127  }
   128  
   129  // Minor returns the minor release number
   130  func (v *Version) Minor() uint {
   131  	return v.components[1]
   132  }
   133  
   134  // Patch returns the patch release number if v is a Semantic Version, or 0
   135  func (v *Version) Patch() uint {
   136  	if len(v.components) < 3 {
   137  		return 0
   138  	}
   139  	return v.components[2]
   140  }
   141  
   142  // BuildMetadata returns the build metadata, if v is a Semantic Version, or ""
   143  func (v *Version) BuildMetadata() string {
   144  	return v.buildMetadata
   145  }
   146  
   147  // PreRelease returns the prerelease metadata, if v is a Semantic Version, or ""
   148  func (v *Version) PreRelease() string {
   149  	return v.preRelease
   150  }
   151  
   152  // Components returns the version number components
   153  func (v *Version) Components() []uint {
   154  	return v.components
   155  }
   156  
   157  // WithMajor returns copy of the version object with requested major number
   158  func (v *Version) WithMajor(major uint) *Version {
   159  	result := *v
   160  	result.components = []uint{major, v.Minor(), v.Patch()}
   161  	return &result
   162  }
   163  
   164  // WithMinor returns copy of the version object with requested minor number
   165  func (v *Version) WithMinor(minor uint) *Version {
   166  	result := *v
   167  	result.components = []uint{v.Major(), minor, v.Patch()}
   168  	return &result
   169  }
   170  
   171  // WithPatch returns copy of the version object with requested patch number
   172  func (v *Version) WithPatch(patch uint) *Version {
   173  	result := *v
   174  	result.components = []uint{v.Major(), v.Minor(), patch}
   175  	return &result
   176  }
   177  
   178  // WithPreRelease returns copy of the version object with requested prerelease
   179  func (v *Version) WithPreRelease(preRelease string) *Version {
   180  	result := *v
   181  	result.components = []uint{v.Major(), v.Minor(), v.Patch()}
   182  	result.preRelease = preRelease
   183  	return &result
   184  }
   185  
   186  // WithBuildMetadata returns copy of the version object with requested buildMetadata
   187  func (v *Version) WithBuildMetadata(buildMetadata string) *Version {
   188  	result := *v
   189  	result.components = []uint{v.Major(), v.Minor(), v.Patch()}
   190  	result.buildMetadata = buildMetadata
   191  	return &result
   192  }
   193  
   194  // String converts a Version back to a string; note that for versions parsed with
   195  // ParseGeneric, this will not include the trailing uninterpreted portion of the version
   196  // number.
   197  func (v *Version) String() string {
   198  	if v == nil {
   199  		return "<nil>"
   200  	}
   201  	var buffer bytes.Buffer
   202  
   203  	for i, comp := range v.components {
   204  		if i > 0 {
   205  			buffer.WriteString(".")
   206  		}
   207  		buffer.WriteString(fmt.Sprintf("%d", comp))
   208  	}
   209  	if v.preRelease != "" {
   210  		buffer.WriteString("-")
   211  		buffer.WriteString(v.preRelease)
   212  	}
   213  	if v.buildMetadata != "" {
   214  		buffer.WriteString("+")
   215  		buffer.WriteString(v.buildMetadata)
   216  	}
   217  
   218  	return buffer.String()
   219  }
   220  
   221  // compareInternal returns -1 if v is less than other, 1 if it is greater than other, or 0
   222  // if they are equal
   223  func (v *Version) compareInternal(other *Version) int {
   224  
   225  	vLen := len(v.components)
   226  	oLen := len(other.components)
   227  	for i := 0; i < vLen && i < oLen; i++ {
   228  		switch {
   229  		case other.components[i] < v.components[i]:
   230  			return 1
   231  		case other.components[i] > v.components[i]:
   232  			return -1
   233  		}
   234  	}
   235  
   236  	// If components are common but one has more items and they are not zeros, it is bigger
   237  	switch {
   238  	case oLen < vLen && !onlyZeros(v.components[oLen:]):
   239  		return 1
   240  	case oLen > vLen && !onlyZeros(other.components[vLen:]):
   241  		return -1
   242  	}
   243  
   244  	if !v.semver || !other.semver {
   245  		return 0
   246  	}
   247  
   248  	switch {
   249  	case v.preRelease == "" && other.preRelease != "":
   250  		return 1
   251  	case v.preRelease != "" && other.preRelease == "":
   252  		return -1
   253  	case v.preRelease == other.preRelease: // includes case where both are ""
   254  		return 0
   255  	}
   256  
   257  	vPR := strings.Split(v.preRelease, ".")
   258  	oPR := strings.Split(other.preRelease, ".")
   259  	for i := 0; i < len(vPR) && i < len(oPR); i++ {
   260  		vNum, err := strconv.ParseUint(vPR[i], 10, 0)
   261  		if err == nil {
   262  			oNum, err := strconv.ParseUint(oPR[i], 10, 0)
   263  			if err == nil {
   264  				switch {
   265  				case oNum < vNum:
   266  					return 1
   267  				case oNum > vNum:
   268  					return -1
   269  				default:
   270  					continue
   271  				}
   272  			}
   273  		}
   274  		if oPR[i] < vPR[i] {
   275  			return 1
   276  		} else if oPR[i] > vPR[i] {
   277  			return -1
   278  		}
   279  	}
   280  
   281  	switch {
   282  	case len(oPR) < len(vPR):
   283  		return 1
   284  	case len(oPR) > len(vPR):
   285  		return -1
   286  	}
   287  
   288  	return 0
   289  }
   290  
   291  // returns false if array contain any non-zero element
   292  func onlyZeros(array []uint) bool {
   293  	for _, num := range array {
   294  		if num != 0 {
   295  			return false
   296  		}
   297  	}
   298  	return true
   299  }
   300  
   301  // AtLeast tests if a version is at least equal to a given minimum version. If both
   302  // Versions are Semantic Versions, this will use the Semantic Version comparison
   303  // algorithm. Otherwise, it will compare only the numeric components, with non-present
   304  // components being considered "0" (ie, "1.4" is equal to "1.4.0").
   305  func (v *Version) AtLeast(min *Version) bool {
   306  	return v.compareInternal(min) != -1
   307  }
   308  
   309  // LessThan tests if a version is less than a given version. (It is exactly the opposite
   310  // of AtLeast, for situations where asking "is v too old?" makes more sense than asking
   311  // "is v new enough?".)
   312  func (v *Version) LessThan(other *Version) bool {
   313  	return v.compareInternal(other) == -1
   314  }
   315  
   316  // Compare compares v against a version string (which will be parsed as either Semantic
   317  // or non-Semantic depending on v). On success it returns -1 if v is less than other, 1 if
   318  // it is greater than other, or 0 if they are equal.
   319  func (v *Version) Compare(other string) (int, error) {
   320  	ov, err := parse(other, v.semver)
   321  	if err != nil {
   322  		return 0, err
   323  	}
   324  	return v.compareInternal(ov), nil
   325  }