go get github.com/apparentlymart/go-versions@master
This commit is contained in:
parent
18dd1bb4d6
commit
eda57670ce
1
go.mod
1
go.mod
|
@ -13,6 +13,7 @@ require (
|
|||
github.com/aliyun/aliyun-tablestore-go-sdk v4.1.2+incompatible
|
||||
github.com/apparentlymart/go-cidr v1.0.1
|
||||
github.com/apparentlymart/go-dump v0.0.0-20190214190832-042adf3cf4a0
|
||||
github.com/apparentlymart/go-versions v0.0.2-0.20180815153302-64b99f7cb171
|
||||
github.com/armon/circbuf v0.0.0-20190214190532-5111143e8da2
|
||||
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da // indirect
|
||||
github.com/armon/go-radix v1.0.0 // indirect
|
||||
|
|
3
go.sum
3
go.sum
|
@ -69,6 +69,8 @@ github.com/apparentlymart/go-dump v0.0.0-20190214190832-042adf3cf4a0 h1:MzVXffFU
|
|||
github.com/apparentlymart/go-dump v0.0.0-20190214190832-042adf3cf4a0/go.mod h1:oL81AME2rN47vu18xqj1S1jPIPuN7afo62yKTNn3XMM=
|
||||
github.com/apparentlymart/go-textseg v1.0.0 h1:rRmlIsPEEhUTIKQb7T++Nz/A5Q6C9IuX2wFoYVvnCs0=
|
||||
github.com/apparentlymart/go-textseg v1.0.0/go.mod h1:z96Txxhf3xSFMPmb5X/1W05FF/Nj9VFpLOpjS5yuumk=
|
||||
github.com/apparentlymart/go-versions v0.0.2-0.20180815153302-64b99f7cb171 h1:19Seu/H5gq3Ugtx+CGenwF89SDG3S1REX5i6PJj3RK4=
|
||||
github.com/apparentlymart/go-versions v0.0.2-0.20180815153302-64b99f7cb171/go.mod h1:JXY95WvQrPJQtudvNARshgWajS7jNNlM90altXIPNyI=
|
||||
github.com/armon/circbuf v0.0.0-20190214190532-5111143e8da2 h1:7Ip0wMmLHLRJdrloDxZfhMm0xrLXZS8+COSu2bXmEQs=
|
||||
github.com/armon/circbuf v0.0.0-20190214190532-5111143e8da2/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
||||
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da h1:8GUt8eRujhVEGZFFEjBj46YV4rDjvGrNxb0KMWYkL2I=
|
||||
|
@ -131,6 +133,7 @@ github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeME
|
|||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/go-test/deep v1.0.1/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
|
||||
github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68=
|
||||
github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2018 Martin Atkins
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
352
vendor/github.com/apparentlymart/go-versions/versions/constraints/canon_style.go
generated
vendored
Normal file
352
vendor/github.com/apparentlymart/go-versions/versions/constraints/canon_style.go
generated
vendored
Normal file
|
@ -0,0 +1,352 @@
|
|||
package constraints
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Parse parses a constraint string using a syntax similar to that used by
|
||||
// npm, Go "dep", Rust's "cargo", etc. Exact compatibility with any of these
|
||||
// systems is not guaranteed, but instead we aim for familiarity in the choice
|
||||
// of operators and their meanings. The syntax described here is considered the
|
||||
// canonical syntax for this package, but a Ruby-style syntax is also offered
|
||||
// via the function "ParseRubyStyle".
|
||||
//
|
||||
// A constraint string is a sequence of selection sets delimited by ||, with
|
||||
// each selection set being a whitespace-delimited sequence of selections.
|
||||
// Each selection is then the combination of a matching operator and a boundary
|
||||
// version. The following is an example of a complex constraint string
|
||||
// illustrating all of these features:
|
||||
//
|
||||
// >=1.0.0 <2.0.0 || 1.0.0-beta1 || =2.0.2
|
||||
//
|
||||
// In practice constraint strings are usually simpler than this, but this
|
||||
// complex example allows us to identify each of the parts by example:
|
||||
//
|
||||
// Selection Sets: ">=1.0.0 <2.0.0"
|
||||
// "1.0.0-beta1"
|
||||
// "=2.0.2"
|
||||
// Selections: ">=1.0.0"
|
||||
// "<2.0.0"
|
||||
// "1.0.0-beta1"
|
||||
// "=2.0.2"
|
||||
// Matching Operators: ">=", "<", "=" are explicit operators
|
||||
// "1.0.0-beta1" has an implicit "=" operator
|
||||
// Boundary Versions: "1.0.0", "2.0.0", "1.0.0-beta1", "2.0.2"
|
||||
//
|
||||
// A constraint string describes the members of a version set by adding exact
|
||||
// versions or ranges of versions to that set. A version is in the set if
|
||||
// any one of the selection sets match that version. A selection set matches
|
||||
// a version if all of its selections match that version. A selection matches
|
||||
// a version if the version has the indicated relationship with the given
|
||||
// boundary version.
|
||||
//
|
||||
// In the above example, the first selection set matches all released versions
|
||||
// whose major segment is 1, since both selections must apply. However, the
|
||||
// remaining two selection sets describe two specific versions outside of that
|
||||
// range that are also admitted, in addition to those in the indicated range.
|
||||
//
|
||||
// The available matching operators are:
|
||||
//
|
||||
// < Less than
|
||||
// <= Less than or equal
|
||||
// > Greater than
|
||||
// >= Greater than or equal
|
||||
// = Equal
|
||||
// ! Not equal
|
||||
// ~ Greater than with implied upper limit (described below)
|
||||
// ^ Greater than excluding new major releases (described below)
|
||||
//
|
||||
// If no operator is specified, the operator is implied to be "equal" for a
|
||||
// full version specification, or a special additional "match" operator for
|
||||
// a version containing wildcards as described below.
|
||||
//
|
||||
// The "~" matching operator is a shorthand for expressing both a lower and
|
||||
// upper limit within a single expression. The effect of this operator depends
|
||||
// on how many segments are specified in the boundary version: if only one
|
||||
// segment is specified then new minor and patch versions are accepted, whereas
|
||||
// if two or three segments are specified then only patch versions are accepted.
|
||||
// For example:
|
||||
//
|
||||
// ~1 is equivalent to >=1.0.0 <2.0.0
|
||||
// ~1.0 is equivalent to >=1.0.0 <1.1.0
|
||||
// ~1.2 is equivalent to >=1.2.0 <1.3.0
|
||||
// ~1.2.0 is equivalent to >=1.2.0 <1.3.0
|
||||
// ~1.2.3 is equivalent to >=1.2.3 <1.3.0
|
||||
//
|
||||
// The "^" matching operator is similar to "~" except that it always constrains
|
||||
// only the major version number. It has an additional special behavior for
|
||||
// when the major version number is zero: in that case, the minor release
|
||||
// number is constrained, reflecting the common semver convention that initial
|
||||
// development releases mark breaking changes by incrementing the minor version.
|
||||
// For example:
|
||||
//
|
||||
// ^1 is equivalent to >=1.0.0 <2.0.0
|
||||
// ^1.2 is equivalent to >=1.2.0 <2.0.0
|
||||
// ^1.2.3 is equivalent to >=1.2.3 <2.0.0
|
||||
// ^0.1.0 is equivalent to >=0.1.0 <0.2.0
|
||||
// ^0.1.2 is equivalent to >=0.1.2 <0.2.0
|
||||
//
|
||||
// The boundary version can contain wildcards for the major, minor or patch
|
||||
// segments, which are specified using the markers "*", "x", or "X". When used
|
||||
// in a selection with no explicit operator, these specify the implied "match"
|
||||
// operator and define ranges with similar meaning to the "~" and "^" operators:
|
||||
//
|
||||
// 1.* is equivalent to >=1.0.0 <2.0.0
|
||||
// 1.*.* is equivalent to >=1.0.0 <2.0.0
|
||||
// 1.0.* is equivalent to >=1.0.0 <1.1.0
|
||||
//
|
||||
// When wildcards are used, the first segment specified as a wildcard implies
|
||||
// that all of the following segments are also wildcards. A version
|
||||
// specification like "1.*.2" is invalid, because a wildcard minor version
|
||||
// implies that the patch version must also be a wildcard.
|
||||
//
|
||||
// Wildcards have no special meaning when used with explicit operators, and so
|
||||
// they are merely replaced with zeros in such cases.
|
||||
//
|
||||
// Explicit range syntax using a hyphen creates inclusive upper and lower
|
||||
// bounds:
|
||||
//
|
||||
// 1.0.0 - 2.0.0 is equivalent to >=1.0.0 <=2.0.0
|
||||
// 1.2.3 - 2.3.4 is equivalent to >=1.2.3 <=2.3.4
|
||||
//
|
||||
// Requests of exact pre-release versions with the equals operator have
|
||||
// no special meaning to the constraint parser, but are interpreted as explicit
|
||||
// requests for those versions when interpreted by the MeetingConstraints
|
||||
// function (and related functions) in the "versions" package, in the parent
|
||||
// directory. Pre-release versions that are not explicitly requested are
|
||||
// excluded from selection so that e.g. "^1.0.0" will not match a version
|
||||
// "2.0.0-beta.1".
|
||||
//
|
||||
// The result is always a UnionSpec, whose members are IntersectionSpecs
|
||||
// each describing one selection set. In the common case where a string
|
||||
// contains only one selection, both the UnionSpec and the IntersectionSpec
|
||||
// will have only one element and can thus be effectively ignored by the
|
||||
// caller. (Union and intersection of single sets are both no-op.)
|
||||
// A valid string must contain at least one selection; if an empty selection
|
||||
// is to be considered as either "no versions" or "all versions" then this
|
||||
// special case must be handled by the caller prior to calling this function.
|
||||
//
|
||||
// If there are syntax errors or ambiguities in the provided string then an
|
||||
// error is returned. All errors returned by this function are suitable for
|
||||
// display to English-speaking end-users, and avoid any Go-specific
|
||||
// terminology.
|
||||
func Parse(str string) (UnionSpec, error) {
|
||||
str = strings.TrimSpace(str)
|
||||
|
||||
if str == "" {
|
||||
return nil, fmt.Errorf("empty specification")
|
||||
}
|
||||
|
||||
// Most constraint strings contain only one selection, so we'll
|
||||
// allocate under that assumption and re-allocate if needed.
|
||||
uspec := make(UnionSpec, 0, 1)
|
||||
ispec := make(IntersectionSpec, 0, 1)
|
||||
|
||||
remain := str
|
||||
for {
|
||||
var selection SelectionSpec
|
||||
var err error
|
||||
selection, remain, err = parseSelection(remain)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
remain = strings.TrimSpace(remain)
|
||||
|
||||
if len(remain) > 0 && remain[0] == '-' {
|
||||
// Looks like user wants to make a range expression, so we'll
|
||||
// look for another selection.
|
||||
remain = strings.TrimSpace(remain[1:])
|
||||
if remain == "" {
|
||||
return nil, fmt.Errorf(`operator "-" must be followed by another version selection to specify the upper limit of the range`)
|
||||
}
|
||||
|
||||
var lower, upper SelectionSpec
|
||||
lower = selection
|
||||
upper, remain, err = parseSelection(remain)
|
||||
remain = strings.TrimSpace(remain)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if lower.Operator != OpUnconstrained {
|
||||
return nil, fmt.Errorf(`lower bound of range specified with "-" operator must be an exact version`)
|
||||
}
|
||||
if upper.Operator != OpUnconstrained {
|
||||
return nil, fmt.Errorf(`upper bound of range specified with "-" operator must be an exact version`)
|
||||
}
|
||||
|
||||
lower.Operator = OpGreaterThanOrEqual
|
||||
lower.Boundary = lower.Boundary.ConstrainToZero()
|
||||
if upper.Boundary.IsExact() {
|
||||
upper.Operator = OpLessThanOrEqual
|
||||
} else {
|
||||
upper.Operator = OpLessThan
|
||||
upper.Boundary = upper.Boundary.ConstrainToUpperBound()
|
||||
}
|
||||
ispec = append(ispec, lower, upper)
|
||||
} else {
|
||||
if selection.Operator == OpUnconstrained {
|
||||
// Select a default operator based on whether the version
|
||||
// specification contains wildcards.
|
||||
if selection.Boundary.IsExact() {
|
||||
selection.Operator = OpEqual
|
||||
} else {
|
||||
selection.Operator = OpMatch
|
||||
}
|
||||
}
|
||||
if selection.Operator != OpMatch {
|
||||
switch selection.Operator {
|
||||
case OpMatch:
|
||||
// nothing to do
|
||||
case OpLessThanOrEqual:
|
||||
if !selection.Boundary.IsExact() {
|
||||
selection.Operator = OpLessThan
|
||||
selection.Boundary = selection.Boundary.ConstrainToUpperBound()
|
||||
}
|
||||
case OpGreaterThan:
|
||||
if !selection.Boundary.IsExact() {
|
||||
// If "greater than" has an imprecise boundary then we'll
|
||||
// turn it into a "greater than or equal to" and use the
|
||||
// upper bound of the boundary, so e.g.:
|
||||
// >1.*.* means >=2.0.0, because that's greater than
|
||||
// everything matched by 1.*.*.
|
||||
selection.Operator = OpGreaterThanOrEqual
|
||||
selection.Boundary = selection.Boundary.ConstrainToUpperBound()
|
||||
}
|
||||
default:
|
||||
selection.Boundary = selection.Boundary.ConstrainToZero()
|
||||
}
|
||||
}
|
||||
ispec = append(ispec, selection)
|
||||
}
|
||||
|
||||
if len(remain) == 0 {
|
||||
// All done!
|
||||
break
|
||||
}
|
||||
|
||||
if remain[0] == ',' {
|
||||
return nil, fmt.Errorf(`commas are not needed to separate version selections; separate with spaces instead`)
|
||||
}
|
||||
|
||||
if remain[0] == '|' {
|
||||
if !strings.HasPrefix(remain, "||") {
|
||||
// User was probably trying for "||", so we'll produce a specialized error
|
||||
return nil, fmt.Errorf(`single "|" is not a valid operator; did you mean "||" to specify an alternative?`)
|
||||
}
|
||||
remain = strings.TrimSpace(remain[2:])
|
||||
if remain == "" {
|
||||
return nil, fmt.Errorf(`operator "||" must be followed by another version selection`)
|
||||
}
|
||||
|
||||
// Begin a new IntersectionSpec, added to our single UnionSpec
|
||||
uspec = append(uspec, ispec)
|
||||
ispec = make(IntersectionSpec, 0, 1)
|
||||
}
|
||||
}
|
||||
|
||||
uspec = append(uspec, ispec)
|
||||
|
||||
return uspec, nil
|
||||
}
|
||||
|
||||
// parseSelection parses one canon-style selection from the prefix of the
|
||||
// given string, returning the result along with the remaining unconsumed
|
||||
// string for the caller to use for further processing.
|
||||
func parseSelection(str string) (SelectionSpec, string, error) {
|
||||
raw, remain := scanConstraint(str)
|
||||
var spec SelectionSpec
|
||||
|
||||
if len(str) == len(remain) {
|
||||
if len(remain) > 0 && remain[0] == 'v' {
|
||||
// User seems to be trying to use a "v" prefix, like "v1.0.0"
|
||||
return spec, remain, fmt.Errorf(`a "v" prefix should not be used when specifying versions`)
|
||||
}
|
||||
|
||||
// If we made no progress at all then the selection must be entirely invalid.
|
||||
return spec, remain, fmt.Errorf("the sequence %q is not valid", remain)
|
||||
}
|
||||
|
||||
switch raw.op {
|
||||
case "":
|
||||
// We'll deal with this situation in the caller
|
||||
spec.Operator = OpUnconstrained
|
||||
case "=":
|
||||
spec.Operator = OpEqual
|
||||
case "!":
|
||||
spec.Operator = OpNotEqual
|
||||
case ">":
|
||||
spec.Operator = OpGreaterThan
|
||||
case ">=":
|
||||
spec.Operator = OpGreaterThanOrEqual
|
||||
case "<":
|
||||
spec.Operator = OpLessThan
|
||||
case "<=":
|
||||
spec.Operator = OpLessThanOrEqual
|
||||
case "~":
|
||||
if raw.numCt > 1 {
|
||||
spec.Operator = OpGreaterThanOrEqualPatchOnly
|
||||
} else {
|
||||
spec.Operator = OpGreaterThanOrEqualMinorOnly
|
||||
}
|
||||
case "^":
|
||||
if len(raw.nums[0]) > 0 && raw.nums[0][0] == '0' {
|
||||
// Special case for major version 0, which is initial development:
|
||||
// we treat the minor number as if it's the major number.
|
||||
spec.Operator = OpGreaterThanOrEqualPatchOnly
|
||||
} else {
|
||||
spec.Operator = OpGreaterThanOrEqualMinorOnly
|
||||
}
|
||||
case "=<":
|
||||
return spec, remain, fmt.Errorf("invalid constraint operator %q; did you mean \"<=\"?", raw.op)
|
||||
case "=>":
|
||||
return spec, remain, fmt.Errorf("invalid constraint operator %q; did you mean \">=\"?", raw.op)
|
||||
default:
|
||||
return spec, remain, fmt.Errorf("invalid constraint operator %q", raw.op)
|
||||
}
|
||||
|
||||
if raw.sep != "" {
|
||||
return spec, remain, fmt.Errorf("no spaces allowed after operator %q", raw.op)
|
||||
}
|
||||
|
||||
if raw.numCt > 3 {
|
||||
return spec, remain, fmt.Errorf("too many numbered portions; only three are allowed (major, minor, patch)")
|
||||
}
|
||||
|
||||
// Unspecified portions are either zero or wildcard depending on whether
|
||||
// any explicit wildcards are present.
|
||||
seenWild := false
|
||||
for i, s := range raw.nums {
|
||||
switch {
|
||||
case isWildcardNum(s):
|
||||
seenWild = true
|
||||
case i >= raw.numCt:
|
||||
if seenWild {
|
||||
raw.nums[i] = "*"
|
||||
} else {
|
||||
raw.nums[i] = "0"
|
||||
}
|
||||
default:
|
||||
// If we find a non-wildcard after we've already seen a wildcard
|
||||
// then this specification is inconsistent, which is an error.
|
||||
if seenWild {
|
||||
return spec, remain, fmt.Errorf("can't use exact %s segment after a previous segment was wildcard", rawNumNames[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if seenWild {
|
||||
if raw.pre != "" {
|
||||
return spec, remain, fmt.Errorf(`can't use prerelease segment (introduced by "-") in a version with wildcards`)
|
||||
}
|
||||
if raw.meta != "" {
|
||||
return spec, remain, fmt.Errorf(`can't use build metadata segment (introduced by "+") in a version with wildcards`)
|
||||
}
|
||||
}
|
||||
|
||||
spec.Boundary = raw.VersionSpec()
|
||||
|
||||
return spec, remain, nil
|
||||
}
|
16
vendor/github.com/apparentlymart/go-versions/versions/constraints/constraintdepth_string.go
generated
vendored
Normal file
16
vendor/github.com/apparentlymart/go-versions/versions/constraints/constraintdepth_string.go
generated
vendored
Normal file
|
@ -0,0 +1,16 @@
|
|||
// Code generated by "stringer -type ConstraintDepth"; DO NOT EDIT.
|
||||
|
||||
package constraints
|
||||
|
||||
import "strconv"
|
||||
|
||||
const _ConstraintDepth_name = "UnconstrainedConstrainedMajorConstrainedMinorConstrainedPatch"
|
||||
|
||||
var _ConstraintDepth_index = [...]uint8{0, 13, 29, 45, 61}
|
||||
|
||||
func (i ConstraintDepth) String() string {
|
||||
if i < 0 || i >= ConstraintDepth(len(_ConstraintDepth_index)-1) {
|
||||
return "ConstraintDepth(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||
}
|
||||
return _ConstraintDepth_name[_ConstraintDepth_index[i]:_ConstraintDepth_index[i+1]]
|
||||
}
|
13
vendor/github.com/apparentlymart/go-versions/versions/constraints/doc.go
generated
vendored
Normal file
13
vendor/github.com/apparentlymart/go-versions/versions/constraints/doc.go
generated
vendored
Normal file
|
@ -0,0 +1,13 @@
|
|||
// Package constraints contains a high-level representation of version
|
||||
// constraints that retains enough information for direct analysis and
|
||||
// serialization as a string.
|
||||
//
|
||||
// The package also contains parsers to produce that representation from
|
||||
// various compact constraint specification formats.
|
||||
//
|
||||
// The main "versions" package, available in the parent directory, can consume
|
||||
// the high-level constraint representation from this package to construct
|
||||
// a version set that contains all versions meeting the given constraints.
|
||||
// Package "constraints" does not contain any functionalty for checking versions
|
||||
// against constraints since that is provided by package "versions".
|
||||
package constraints
|
74
vendor/github.com/apparentlymart/go-versions/versions/constraints/raw.go
generated
vendored
Normal file
74
vendor/github.com/apparentlymart/go-versions/versions/constraints/raw.go
generated
vendored
Normal file
|
@ -0,0 +1,74 @@
|
|||
package constraints
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
)
|
||||
|
||||
//go:generate ragel -G1 -Z raw_scan.rl
|
||||
//go:generate gofmt -w raw_scan.go
|
||||
|
||||
// rawConstraint is a tokenization of a constraint string, used internally
|
||||
// as the first layer of parsing.
|
||||
type rawConstraint struct {
|
||||
op string
|
||||
sep string
|
||||
nums [3]string
|
||||
numCt int
|
||||
pre string
|
||||
meta string
|
||||
}
|
||||
|
||||
// VersionSpec turns the receiver into a VersionSpec in a reasonable
|
||||
// default way. This method assumes that the raw constraint was already
|
||||
// validated, and will panic or produce undefined results if it contains
|
||||
// anything invalid.
|
||||
//
|
||||
// In particular, numbers are automatically marked as unconstrained if they
|
||||
// are omitted or set to wildcards, so the caller must apply any additional
|
||||
// validation rules on the usage of unconstrained numbers before calling.
|
||||
func (raw rawConstraint) VersionSpec() VersionSpec {
|
||||
return VersionSpec{
|
||||
Major: parseRawNumConstraint(raw.nums[0]),
|
||||
Minor: parseRawNumConstraint(raw.nums[1]),
|
||||
Patch: parseRawNumConstraint(raw.nums[2]),
|
||||
Prerelease: raw.pre,
|
||||
Metadata: raw.meta,
|
||||
}
|
||||
}
|
||||
|
||||
var rawNumNames = [...]string{"major", "minor", "patch"}
|
||||
|
||||
func isWildcardNum(s string) bool {
|
||||
switch s {
|
||||
case "*", "x", "X":
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// parseRawNum parses a raw number string which the caller has already
|
||||
// determined is non-empty and non-wildcard. If the string is not numeric
|
||||
// then this function will panic.
|
||||
func parseRawNum(s string) uint64 {
|
||||
v, err := strconv.ParseUint(s, 10, 64)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// parseRawNumConstraint parses a raw number into a NumConstraint, setting it
|
||||
// to unconstrained if the value is empty or a wildcard.
|
||||
func parseRawNumConstraint(s string) NumConstraint {
|
||||
switch {
|
||||
case s == "" || isWildcardNum(s):
|
||||
return NumConstraint{
|
||||
Unconstrained: true,
|
||||
}
|
||||
default:
|
||||
return NumConstraint{
|
||||
Num: parseRawNum(s),
|
||||
}
|
||||
}
|
||||
}
|
623
vendor/github.com/apparentlymart/go-versions/versions/constraints/raw_scan.go
generated
vendored
Normal file
623
vendor/github.com/apparentlymart/go-versions/versions/constraints/raw_scan.go
generated
vendored
Normal file
|
@ -0,0 +1,623 @@
|
|||
// line 1 "raw_scan.rl"
|
||||
// This file is generated from raw_scan.rl. DO NOT EDIT.
|
||||
|
||||
// line 5 "raw_scan.rl"
|
||||
|
||||
package constraints
|
||||
|
||||
// line 12 "raw_scan.go"
|
||||
var _scan_eof_actions []byte = []byte{
|
||||
0, 1, 1, 7, 9, 9, 9, 11,
|
||||
14, 15, 11,
|
||||
}
|
||||
|
||||
const scan_start int = 1
|
||||
const scan_first_final int = 7
|
||||
const scan_error int = 0
|
||||
|
||||
const scan_en_main int = 1
|
||||
|
||||
// line 11 "raw_scan.rl"
|
||||
|
||||
func scanConstraint(data string) (rawConstraint, string) {
|
||||
var constraint rawConstraint
|
||||
var numIdx int
|
||||
var extra string
|
||||
|
||||
// Ragel state
|
||||
p := 0 // "Pointer" into data
|
||||
pe := len(data) // End-of-data "pointer"
|
||||
cs := 0 // constraint state (will be initialized by ragel-generated code)
|
||||
ts := 0
|
||||
te := 0
|
||||
eof := pe
|
||||
|
||||
// Keep Go compiler happy even if generated code doesn't use these
|
||||
_ = ts
|
||||
_ = te
|
||||
_ = eof
|
||||
|
||||
// line 47 "raw_scan.go"
|
||||
{
|
||||
cs = scan_start
|
||||
}
|
||||
|
||||
// line 52 "raw_scan.go"
|
||||
{
|
||||
if p == pe {
|
||||
goto _test_eof
|
||||
}
|
||||
if cs == 0 {
|
||||
goto _out
|
||||
}
|
||||
_resume:
|
||||
switch cs {
|
||||
case 1:
|
||||
switch data[p] {
|
||||
case 32:
|
||||
goto tr1
|
||||
case 42:
|
||||
goto tr2
|
||||
case 46:
|
||||
goto tr3
|
||||
case 88:
|
||||
goto tr2
|
||||
case 120:
|
||||
goto tr2
|
||||
}
|
||||
switch {
|
||||
case data[p] < 48:
|
||||
if 9 <= data[p] && data[p] <= 13 {
|
||||
goto tr1
|
||||
}
|
||||
case data[p] > 57:
|
||||
switch {
|
||||
case data[p] > 90:
|
||||
if 97 <= data[p] && data[p] <= 122 {
|
||||
goto tr3
|
||||
}
|
||||
case data[p] >= 65:
|
||||
goto tr3
|
||||
}
|
||||
default:
|
||||
goto tr4
|
||||
}
|
||||
goto tr0
|
||||
case 2:
|
||||
switch data[p] {
|
||||
case 32:
|
||||
goto tr6
|
||||
case 42:
|
||||
goto tr7
|
||||
case 46:
|
||||
goto tr3
|
||||
case 88:
|
||||
goto tr7
|
||||
case 120:
|
||||
goto tr7
|
||||
}
|
||||
switch {
|
||||
case data[p] < 48:
|
||||
if 9 <= data[p] && data[p] <= 13 {
|
||||
goto tr6
|
||||
}
|
||||
case data[p] > 57:
|
||||
switch {
|
||||
case data[p] > 90:
|
||||
if 97 <= data[p] && data[p] <= 122 {
|
||||
goto tr3
|
||||
}
|
||||
case data[p] >= 65:
|
||||
goto tr3
|
||||
}
|
||||
default:
|
||||
goto tr8
|
||||
}
|
||||
goto tr5
|
||||
case 3:
|
||||
switch data[p] {
|
||||
case 32:
|
||||
goto tr10
|
||||
case 42:
|
||||
goto tr11
|
||||
case 88:
|
||||
goto tr11
|
||||
case 120:
|
||||
goto tr11
|
||||
}
|
||||
switch {
|
||||
case data[p] > 13:
|
||||
if 48 <= data[p] && data[p] <= 57 {
|
||||
goto tr12
|
||||
}
|
||||
case data[p] >= 9:
|
||||
goto tr10
|
||||
}
|
||||
goto tr9
|
||||
case 0:
|
||||
goto _out
|
||||
case 7:
|
||||
switch data[p] {
|
||||
case 43:
|
||||
goto tr19
|
||||
case 45:
|
||||
goto tr20
|
||||
case 46:
|
||||
goto tr21
|
||||
}
|
||||
goto tr18
|
||||
case 4:
|
||||
switch {
|
||||
case data[p] < 48:
|
||||
if 45 <= data[p] && data[p] <= 46 {
|
||||
goto tr14
|
||||
}
|
||||
case data[p] > 57:
|
||||
switch {
|
||||
case data[p] > 90:
|
||||
if 97 <= data[p] && data[p] <= 122 {
|
||||
goto tr14
|
||||
}
|
||||
case data[p] >= 65:
|
||||
goto tr14
|
||||
}
|
||||
default:
|
||||
goto tr14
|
||||
}
|
||||
goto tr13
|
||||
case 8:
|
||||
switch {
|
||||
case data[p] < 48:
|
||||
if 45 <= data[p] && data[p] <= 46 {
|
||||
goto tr14
|
||||
}
|
||||
case data[p] > 57:
|
||||
switch {
|
||||
case data[p] > 90:
|
||||
if 97 <= data[p] && data[p] <= 122 {
|
||||
goto tr14
|
||||
}
|
||||
case data[p] >= 65:
|
||||
goto tr14
|
||||
}
|
||||
default:
|
||||
goto tr14
|
||||
}
|
||||
goto tr22
|
||||
case 5:
|
||||
switch {
|
||||
case data[p] < 48:
|
||||
if 45 <= data[p] && data[p] <= 46 {
|
||||
goto tr15
|
||||
}
|
||||
case data[p] > 57:
|
||||
switch {
|
||||
case data[p] > 90:
|
||||
if 97 <= data[p] && data[p] <= 122 {
|
||||
goto tr15
|
||||
}
|
||||
case data[p] >= 65:
|
||||
goto tr15
|
||||
}
|
||||
default:
|
||||
goto tr15
|
||||
}
|
||||
goto tr13
|
||||
case 9:
|
||||
if data[p] == 43 {
|
||||
goto tr24
|
||||
}
|
||||
switch {
|
||||
case data[p] < 48:
|
||||
if 45 <= data[p] && data[p] <= 46 {
|
||||
goto tr15
|
||||
}
|
||||
case data[p] > 57:
|
||||
switch {
|
||||
case data[p] > 90:
|
||||
if 97 <= data[p] && data[p] <= 122 {
|
||||
goto tr15
|
||||
}
|
||||
case data[p] >= 65:
|
||||
goto tr15
|
||||
}
|
||||
default:
|
||||
goto tr15
|
||||
}
|
||||
goto tr23
|
||||
case 6:
|
||||
switch data[p] {
|
||||
case 42:
|
||||
goto tr16
|
||||
case 88:
|
||||
goto tr16
|
||||
case 120:
|
||||
goto tr16
|
||||
}
|
||||
if 48 <= data[p] && data[p] <= 57 {
|
||||
goto tr17
|
||||
}
|
||||
goto tr13
|
||||
case 10:
|
||||
switch data[p] {
|
||||
case 43:
|
||||
goto tr19
|
||||
case 45:
|
||||
goto tr20
|
||||
case 46:
|
||||
goto tr21
|
||||
}
|
||||
if 48 <= data[p] && data[p] <= 57 {
|
||||
goto tr25
|
||||
}
|
||||
goto tr18
|
||||
}
|
||||
|
||||
tr3:
|
||||
cs = 0
|
||||
goto f0
|
||||
tr9:
|
||||
cs = 0
|
||||
goto f6
|
||||
tr13:
|
||||
cs = 0
|
||||
goto f8
|
||||
tr18:
|
||||
cs = 0
|
||||
goto f10
|
||||
tr22:
|
||||
cs = 0
|
||||
goto f13
|
||||
tr23:
|
||||
cs = 0
|
||||
goto f14
|
||||
tr5:
|
||||
cs = 2
|
||||
goto _again
|
||||
tr0:
|
||||
cs = 2
|
||||
goto f1
|
||||
tr10:
|
||||
cs = 3
|
||||
goto _again
|
||||
tr1:
|
||||
cs = 3
|
||||
goto f2
|
||||
tr6:
|
||||
cs = 3
|
||||
goto f4
|
||||
tr19:
|
||||
cs = 4
|
||||
goto f11
|
||||
tr24:
|
||||
cs = 4
|
||||
goto f15
|
||||
tr20:
|
||||
cs = 5
|
||||
goto f11
|
||||
tr21:
|
||||
cs = 6
|
||||
goto f12
|
||||
tr2:
|
||||
cs = 7
|
||||
goto f3
|
||||
tr7:
|
||||
cs = 7
|
||||
goto f5
|
||||
tr11:
|
||||
cs = 7
|
||||
goto f7
|
||||
tr16:
|
||||
cs = 7
|
||||
goto f9
|
||||
tr14:
|
||||
cs = 8
|
||||
goto _again
|
||||
tr15:
|
||||
cs = 9
|
||||
goto _again
|
||||
tr25:
|
||||
cs = 10
|
||||
goto _again
|
||||
tr4:
|
||||
cs = 10
|
||||
goto f3
|
||||
tr8:
|
||||
cs = 10
|
||||
goto f5
|
||||
tr12:
|
||||
cs = 10
|
||||
goto f7
|
||||
tr17:
|
||||
cs = 10
|
||||
goto f9
|
||||
|
||||
f9:
|
||||
// line 38 "raw_scan.rl"
|
||||
|
||||
ts = p
|
||||
|
||||
goto _again
|
||||
f12:
|
||||
// line 52 "raw_scan.rl"
|
||||
|
||||
te = p
|
||||
constraint.numCt++
|
||||
if numIdx < len(constraint.nums) {
|
||||
constraint.nums[numIdx] = data[ts:p]
|
||||
numIdx++
|
||||
}
|
||||
|
||||
goto _again
|
||||
f8:
|
||||
// line 71 "raw_scan.rl"
|
||||
|
||||
extra = data[p:]
|
||||
|
||||
goto _again
|
||||
f1:
|
||||
// line 33 "raw_scan.rl"
|
||||
|
||||
numIdx = 0
|
||||
constraint = rawConstraint{}
|
||||
|
||||
// line 38 "raw_scan.rl"
|
||||
|
||||
ts = p
|
||||
|
||||
goto _again
|
||||
f4:
|
||||
// line 42 "raw_scan.rl"
|
||||
|
||||
te = p
|
||||
constraint.op = data[ts:p]
|
||||
|
||||
// line 38 "raw_scan.rl"
|
||||
|
||||
ts = p
|
||||
|
||||
goto _again
|
||||
f7:
|
||||
// line 47 "raw_scan.rl"
|
||||
|
||||
te = p
|
||||
constraint.sep = data[ts:p]
|
||||
|
||||
// line 38 "raw_scan.rl"
|
||||
|
||||
ts = p
|
||||
|
||||
goto _again
|
||||
f6:
|
||||
// line 47 "raw_scan.rl"
|
||||
|
||||
te = p
|
||||
constraint.sep = data[ts:p]
|
||||
|
||||
// line 71 "raw_scan.rl"
|
||||
|
||||
extra = data[p:]
|
||||
|
||||
goto _again
|
||||
f11:
|
||||
// line 52 "raw_scan.rl"
|
||||
|
||||
te = p
|
||||
constraint.numCt++
|
||||
if numIdx < len(constraint.nums) {
|
||||
constraint.nums[numIdx] = data[ts:p]
|
||||
numIdx++
|
||||
}
|
||||
|
||||
// line 38 "raw_scan.rl"
|
||||
|
||||
ts = p
|
||||
|
||||
goto _again
|
||||
f10:
|
||||
// line 52 "raw_scan.rl"
|
||||
|
||||
te = p
|
||||
constraint.numCt++
|
||||
if numIdx < len(constraint.nums) {
|
||||
constraint.nums[numIdx] = data[ts:p]
|
||||
numIdx++
|
||||
}
|
||||
|
||||
// line 71 "raw_scan.rl"
|
||||
|
||||
extra = data[p:]
|
||||
|
||||
goto _again
|
||||
f15:
|
||||
// line 61 "raw_scan.rl"
|
||||
|
||||
te = p
|
||||
constraint.pre = data[ts+1 : p]
|
||||
|
||||
// line 38 "raw_scan.rl"
|
||||
|
||||
ts = p
|
||||
|
||||
goto _again
|
||||
f14:
|
||||
// line 61 "raw_scan.rl"
|
||||
|
||||
te = p
|
||||
constraint.pre = data[ts+1 : p]
|
||||
|
||||
// line 71 "raw_scan.rl"
|
||||
|
||||
extra = data[p:]
|
||||
|
||||
goto _again
|
||||
f13:
|
||||
// line 66 "raw_scan.rl"
|
||||
|
||||
te = p
|
||||
constraint.meta = data[ts+1 : p]
|
||||
|
||||
// line 71 "raw_scan.rl"
|
||||
|
||||
extra = data[p:]
|
||||
|
||||
goto _again
|
||||
f2:
|
||||
// line 33 "raw_scan.rl"
|
||||
|
||||
numIdx = 0
|
||||
constraint = rawConstraint{}
|
||||
|
||||
// line 38 "raw_scan.rl"
|
||||
|
||||
ts = p
|
||||
|
||||
// line 42 "raw_scan.rl"
|
||||
|
||||
te = p
|
||||
constraint.op = data[ts:p]
|
||||
|
||||
goto _again
|
||||
f5:
|
||||
// line 42 "raw_scan.rl"
|
||||
|
||||
te = p
|
||||
constraint.op = data[ts:p]
|
||||
|
||||
// line 38 "raw_scan.rl"
|
||||
|
||||
ts = p
|
||||
|
||||
// line 47 "raw_scan.rl"
|
||||
|
||||
te = p
|
||||
constraint.sep = data[ts:p]
|
||||
|
||||
goto _again
|
||||
f0:
|
||||
// line 42 "raw_scan.rl"
|
||||
|
||||
te = p
|
||||
constraint.op = data[ts:p]
|
||||
|
||||
// line 47 "raw_scan.rl"
|
||||
|
||||
te = p
|
||||
constraint.sep = data[ts:p]
|
||||
|
||||
// line 71 "raw_scan.rl"
|
||||
|
||||
extra = data[p:]
|
||||
|
||||
goto _again
|
||||
f3:
|
||||
// line 33 "raw_scan.rl"
|
||||
|
||||
numIdx = 0
|
||||
constraint = rawConstraint{}
|
||||
|
||||
// line 38 "raw_scan.rl"
|
||||
|
||||
ts = p
|
||||
|
||||
// line 42 "raw_scan.rl"
|
||||
|
||||
te = p
|
||||
constraint.op = data[ts:p]
|
||||
|
||||
// line 47 "raw_scan.rl"
|
||||
|
||||
te = p
|
||||
constraint.sep = data[ts:p]
|
||||
|
||||
goto _again
|
||||
|
||||
_again:
|
||||
if cs == 0 {
|
||||
goto _out
|
||||
}
|
||||
if p++; p != pe {
|
||||
goto _resume
|
||||
}
|
||||
_test_eof:
|
||||
{
|
||||
}
|
||||
if p == eof {
|
||||
switch _scan_eof_actions[cs] {
|
||||
case 9:
|
||||
// line 71 "raw_scan.rl"
|
||||
|
||||
extra = data[p:]
|
||||
|
||||
case 7:
|
||||
// line 47 "raw_scan.rl"
|
||||
|
||||
te = p
|
||||
constraint.sep = data[ts:p]
|
||||
|
||||
// line 71 "raw_scan.rl"
|
||||
|
||||
extra = data[p:]
|
||||
|
||||
case 11:
|
||||
// line 52 "raw_scan.rl"
|
||||
|
||||
te = p
|
||||
constraint.numCt++
|
||||
if numIdx < len(constraint.nums) {
|
||||
constraint.nums[numIdx] = data[ts:p]
|
||||
numIdx++
|
||||
}
|
||||
|
||||
// line 71 "raw_scan.rl"
|
||||
|
||||
extra = data[p:]
|
||||
|
||||
case 15:
|
||||
// line 61 "raw_scan.rl"
|
||||
|
||||
te = p
|
||||
constraint.pre = data[ts+1 : p]
|
||||
|
||||
// line 71 "raw_scan.rl"
|
||||
|
||||
extra = data[p:]
|
||||
|
||||
case 14:
|
||||
// line 66 "raw_scan.rl"
|
||||
|
||||
te = p
|
||||
constraint.meta = data[ts+1 : p]
|
||||
|
||||
// line 71 "raw_scan.rl"
|
||||
|
||||
extra = data[p:]
|
||||
|
||||
case 1:
|
||||
// line 42 "raw_scan.rl"
|
||||
|
||||
te = p
|
||||
constraint.op = data[ts:p]
|
||||
|
||||
// line 47 "raw_scan.rl"
|
||||
|
||||
te = p
|
||||
constraint.sep = data[ts:p]
|
||||
|
||||
// line 71 "raw_scan.rl"
|
||||
|
||||
extra = data[p:]
|
||||
|
||||
// line 610 "raw_scan.go"
|
||||
}
|
||||
}
|
||||
|
||||
_out:
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
// line 92 "raw_scan.rl"
|
||||
|
||||
return constraint, extra
|
||||
}
|
95
vendor/github.com/apparentlymart/go-versions/versions/constraints/raw_scan.rl
generated
vendored
Normal file
95
vendor/github.com/apparentlymart/go-versions/versions/constraints/raw_scan.rl
generated
vendored
Normal file
|
@ -0,0 +1,95 @@
|
|||
// This file is generated from raw_scan.rl. DO NOT EDIT.
|
||||
%%{
|
||||
# (except you are actually in raw_scan.rl here, so edit away!)
|
||||
machine scan;
|
||||
}%%
|
||||
|
||||
package constraints
|
||||
|
||||
%%{
|
||||
write data;
|
||||
}%%
|
||||
|
||||
func scanConstraint(data string) (rawConstraint, string) {
|
||||
var constraint rawConstraint
|
||||
var numIdx int
|
||||
var extra string
|
||||
|
||||
// Ragel state
|
||||
p := 0 // "Pointer" into data
|
||||
pe := len(data) // End-of-data "pointer"
|
||||
cs := 0 // constraint state (will be initialized by ragel-generated code)
|
||||
ts := 0
|
||||
te := 0
|
||||
eof := pe
|
||||
|
||||
// Keep Go compiler happy even if generated code doesn't use these
|
||||
_ = ts
|
||||
_ = te
|
||||
_ = eof
|
||||
|
||||
%%{
|
||||
|
||||
action enterConstraint {
|
||||
numIdx = 0
|
||||
constraint = rawConstraint{}
|
||||
}
|
||||
|
||||
action ts {
|
||||
ts = p
|
||||
}
|
||||
|
||||
action finishOp {
|
||||
te = p
|
||||
constraint.op = data[ts:p]
|
||||
}
|
||||
|
||||
action finishSep {
|
||||
te = p
|
||||
constraint.sep = data[ts:p]
|
||||
}
|
||||
|
||||
action finishNum {
|
||||
te = p
|
||||
constraint.numCt++
|
||||
if numIdx < len(constraint.nums) {
|
||||
constraint.nums[numIdx] = data[ts:p]
|
||||
numIdx++
|
||||
}
|
||||
}
|
||||
|
||||
action finishPre {
|
||||
te = p
|
||||
constraint.pre = data[ts+1:p]
|
||||
}
|
||||
|
||||
action finishMeta {
|
||||
te = p
|
||||
constraint.meta = data[ts+1:p]
|
||||
}
|
||||
|
||||
action finishExtra {
|
||||
extra = data[p:]
|
||||
}
|
||||
|
||||
num = (digit+ | '*' | 'x' | 'X') >ts %finishNum %err(finishNum) %eof(finishNum);
|
||||
|
||||
op = ((any - (digit | space | alpha | '.' | '*'))**) >ts %finishOp %err(finishOp) %eof(finishOp);
|
||||
likelyOp = ('^' | '>' | '<' | '-' | '~' | '!');
|
||||
sep = (space**) >ts %finishSep %err(finishSep) %eof(finishSep);
|
||||
nums = (num ('.' num)*);
|
||||
extraStr = (alnum | '.' | '-')+;
|
||||
pre = ('-' extraStr) >ts %finishPre %err(finishPre) %eof(finishPre);
|
||||
meta = ('+' extraStr) >ts %finishMeta %err(finishMeta) %eof(finishMeta);
|
||||
|
||||
constraint = (op sep nums pre? meta?) >enterConstraint;
|
||||
|
||||
main := (constraint) @/finishExtra %/finishExtra $!finishExtra;
|
||||
|
||||
write init;
|
||||
write exec;
|
||||
|
||||
}%%
|
||||
|
||||
return constraint, extra
|
||||
}
|
181
vendor/github.com/apparentlymart/go-versions/versions/constraints/ruby_style.go
generated
vendored
Normal file
181
vendor/github.com/apparentlymart/go-versions/versions/constraints/ruby_style.go
generated
vendored
Normal file
|
@ -0,0 +1,181 @@
|
|||
package constraints
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ParseRubyStyle parses a single selection constraint using a syntax similar
|
||||
// to that used by rubygems and other Ruby tools.
|
||||
//
|
||||
// Exact compatibility with rubygems is not guaranteed; "ruby-style" here
|
||||
// just means that users familiar with rubygems should find familiar the choice
|
||||
// of operators and their meanings.
|
||||
//
|
||||
// ParseRubyStyle parses only a single specification, mimicking the usual
|
||||
// rubygems approach of providing each selection as a separate string.
|
||||
// The result can be combined with other results to create an IntersectionSpec
|
||||
// that describes the effect of multiple such constraints.
|
||||
func ParseRubyStyle(str string) (SelectionSpec, error) {
|
||||
if strings.TrimSpace(str) == "" {
|
||||
return SelectionSpec{}, fmt.Errorf("empty specification")
|
||||
}
|
||||
spec, remain, err := parseRubyStyle(str)
|
||||
if err != nil {
|
||||
return spec, err
|
||||
}
|
||||
if remain != "" {
|
||||
remain = strings.TrimSpace(remain)
|
||||
switch {
|
||||
case remain == "":
|
||||
return spec, fmt.Errorf("extraneous spaces at end of specification")
|
||||
case strings.HasPrefix(remain, "v"):
|
||||
// User seems to be trying to use a "v" prefix, like "v1.0.0"
|
||||
return spec, fmt.Errorf(`a "v" prefix should not be used`)
|
||||
case strings.HasPrefix(remain, "||") || strings.HasPrefix(remain, ","):
|
||||
// User seems to be trying to specify multiple constraints
|
||||
return spec, fmt.Errorf(`only one constraint may be specified`)
|
||||
case strings.HasPrefix(remain, "-"):
|
||||
// User seems to be trying to use npm-style range constraints
|
||||
return spec, fmt.Errorf(`range constraints are not supported`)
|
||||
default:
|
||||
return spec, fmt.Errorf("invalid characters %q", remain)
|
||||
}
|
||||
}
|
||||
|
||||
return spec, nil
|
||||
}
|
||||
|
||||
// ParseRubyStyleAll is a helper wrapper around ParseRubyStyle that accepts
|
||||
// multiple selection strings and combines them together into a single
|
||||
// IntersectionSpec.
|
||||
func ParseRubyStyleAll(strs ...string) (IntersectionSpec, error) {
|
||||
spec := make(IntersectionSpec, 0, len(strs))
|
||||
for _, str := range strs {
|
||||
subSpec, err := ParseRubyStyle(str)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid specification %q: %s", str, err)
|
||||
}
|
||||
spec = append(spec, subSpec)
|
||||
}
|
||||
return spec, nil
|
||||
}
|
||||
|
||||
// ParseRubyStyleMulti is similar to ParseRubyStyle, but rather than parsing
|
||||
// only a single selection specification it instead expects one or more
|
||||
// comma-separated specifications, returning the result as an
|
||||
// IntersectionSpec.
|
||||
func ParseRubyStyleMulti(str string) (IntersectionSpec, error) {
|
||||
var spec IntersectionSpec
|
||||
remain := strings.TrimSpace(str)
|
||||
for remain != "" {
|
||||
if strings.TrimSpace(remain) == "" {
|
||||
break
|
||||
}
|
||||
|
||||
var subSpec SelectionSpec
|
||||
var err error
|
||||
var newRemain string
|
||||
subSpec, newRemain, err = parseRubyStyle(remain)
|
||||
consumed := remain[:len(remain)-len(newRemain)]
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid specification %q: %s", consumed, err)
|
||||
}
|
||||
remain = strings.TrimSpace(newRemain)
|
||||
|
||||
if remain != "" {
|
||||
if !strings.HasPrefix(remain, ",") {
|
||||
return nil, fmt.Errorf("missing comma after %q", consumed)
|
||||
}
|
||||
// Eat the separator comma
|
||||
remain = strings.TrimSpace(remain[1:])
|
||||
}
|
||||
|
||||
spec = append(spec, subSpec)
|
||||
}
|
||||
|
||||
return spec, nil
|
||||
}
|
||||
|
||||
// parseRubyStyle parses a ruby-style constraint from the prefix of the given
|
||||
// string and returns the remaining unconsumed string for the caller to use
|
||||
// for further processing.
|
||||
func parseRubyStyle(str string) (SelectionSpec, string, error) {
|
||||
raw, remain := scanConstraint(str)
|
||||
var spec SelectionSpec
|
||||
|
||||
switch raw.op {
|
||||
case "=", "":
|
||||
spec.Operator = OpEqual
|
||||
case "!=":
|
||||
spec.Operator = OpNotEqual
|
||||
case ">":
|
||||
spec.Operator = OpGreaterThan
|
||||
case ">=":
|
||||
spec.Operator = OpGreaterThanOrEqual
|
||||
case "<":
|
||||
spec.Operator = OpLessThan
|
||||
case "<=":
|
||||
spec.Operator = OpLessThanOrEqual
|
||||
case "~>":
|
||||
// Ruby-style pessimistic can be either a minor-only or patch-only
|
||||
// constraint, depending on how many digits were given.
|
||||
switch raw.numCt {
|
||||
case 3:
|
||||
spec.Operator = OpGreaterThanOrEqualPatchOnly
|
||||
default:
|
||||
spec.Operator = OpGreaterThanOrEqualMinorOnly
|
||||
}
|
||||
case "=<":
|
||||
return spec, remain, fmt.Errorf("invalid constraint operator %q; did you mean \"<=\"?", raw.op)
|
||||
case "=>":
|
||||
return spec, remain, fmt.Errorf("invalid constraint operator %q; did you mean \">=\"?", raw.op)
|
||||
default:
|
||||
return spec, remain, fmt.Errorf("invalid constraint operator %q", raw.op)
|
||||
}
|
||||
|
||||
switch raw.sep {
|
||||
case "":
|
||||
if raw.op != "" {
|
||||
return spec, remain, fmt.Errorf("a space separator is required after the operator %q", raw.op)
|
||||
}
|
||||
case " ":
|
||||
if raw.op == "" {
|
||||
return spec, remain, fmt.Errorf("extraneous spaces at start of specification")
|
||||
}
|
||||
default:
|
||||
if raw.op == "" {
|
||||
return spec, remain, fmt.Errorf("extraneous spaces at start of specification")
|
||||
} else {
|
||||
return spec, remain, fmt.Errorf("only one space is expected after the operator %q", raw.op)
|
||||
}
|
||||
}
|
||||
|
||||
if raw.numCt > 3 {
|
||||
return spec, remain, fmt.Errorf("too many numbered portions; only three are allowed (major, minor, patch)")
|
||||
}
|
||||
|
||||
// Ruby-style doesn't use explicit wildcards
|
||||
for i, s := range raw.nums {
|
||||
switch {
|
||||
case isWildcardNum(s):
|
||||
// Can't use wildcards in an exact specification
|
||||
return spec, remain, fmt.Errorf("can't use wildcard for %s number; omit segments that should be unconstrained", rawNumNames[i])
|
||||
}
|
||||
}
|
||||
|
||||
if raw.pre != "" || raw.meta != "" {
|
||||
// If either the prerelease or meta portions are set then any unconstrained
|
||||
// segments are implied to be zero in order to guarantee constraint
|
||||
// consistency.
|
||||
for i, s := range raw.nums {
|
||||
if s == "" {
|
||||
raw.nums[i] = "0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
spec.Boundary = raw.VersionSpec()
|
||||
|
||||
return spec, remain, nil
|
||||
}
|
43
vendor/github.com/apparentlymart/go-versions/versions/constraints/selectionop_string.go
generated
vendored
Normal file
43
vendor/github.com/apparentlymart/go-versions/versions/constraints/selectionop_string.go
generated
vendored
Normal file
|
@ -0,0 +1,43 @@
|
|||
// Code generated by "stringer -type SelectionOp"; DO NOT EDIT.
|
||||
|
||||
package constraints
|
||||
|
||||
import "strconv"
|
||||
|
||||
const (
|
||||
_SelectionOp_name_0 = "OpUnconstrained"
|
||||
_SelectionOp_name_1 = "OpMatch"
|
||||
_SelectionOp_name_2 = "OpLessThanOpEqualOpGreaterThan"
|
||||
_SelectionOp_name_3 = "OpGreaterThanOrEqualMinorOnly"
|
||||
_SelectionOp_name_4 = "OpGreaterThanOrEqualPatchOnly"
|
||||
_SelectionOp_name_5 = "OpNotEqual"
|
||||
_SelectionOp_name_6 = "OpLessThanOrEqualOpGreaterThanOrEqual"
|
||||
)
|
||||
|
||||
var (
|
||||
_SelectionOp_index_2 = [...]uint8{0, 10, 17, 30}
|
||||
_SelectionOp_index_6 = [...]uint8{0, 17, 37}
|
||||
)
|
||||
|
||||
func (i SelectionOp) String() string {
|
||||
switch {
|
||||
case i == 0:
|
||||
return _SelectionOp_name_0
|
||||
case i == 42:
|
||||
return _SelectionOp_name_1
|
||||
case 60 <= i && i <= 62:
|
||||
i -= 60
|
||||
return _SelectionOp_name_2[_SelectionOp_index_2[i]:_SelectionOp_index_2[i+1]]
|
||||
case i == 94:
|
||||
return _SelectionOp_name_3
|
||||
case i == 126:
|
||||
return _SelectionOp_name_4
|
||||
case i == 8800:
|
||||
return _SelectionOp_name_5
|
||||
case 8804 <= i && i <= 8805:
|
||||
i -= 8804
|
||||
return _SelectionOp_name_6[_SelectionOp_index_6[i]:_SelectionOp_index_6[i+1]]
|
||||
default:
|
||||
return "SelectionOp(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||
}
|
||||
}
|
249
vendor/github.com/apparentlymart/go-versions/versions/constraints/spec.go
generated
vendored
Normal file
249
vendor/github.com/apparentlymart/go-versions/versions/constraints/spec.go
generated
vendored
Normal file
|
@ -0,0 +1,249 @@
|
|||
package constraints
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// Spec is an interface type that UnionSpec, IntersectionSpec, SelectionSpec,
|
||||
// and VersionSpec all belong to.
|
||||
//
|
||||
// It's provided to allow generic code to be written that accepts and operates
|
||||
// on all specs, but such code must still handle each type separately using
|
||||
// e.g. a type switch. This is a closed type that will not have any new
|
||||
// implementations added in future.
|
||||
type Spec interface {
|
||||
isSpec()
|
||||
}
|
||||
|
||||
// UnionSpec represents an "or" operation on nested version constraints.
|
||||
//
|
||||
// This is not directly representable in all of our supported constraint
|
||||
// syntaxes.
|
||||
type UnionSpec []IntersectionSpec
|
||||
|
||||
func (s UnionSpec) isSpec() {}
|
||||
|
||||
// IntersectionSpec represents an "and" operation on nested version constraints.
|
||||
type IntersectionSpec []SelectionSpec
|
||||
|
||||
func (s IntersectionSpec) isSpec() {}
|
||||
|
||||
// SelectionSpec represents applying a single operator to a particular
|
||||
// "boundary" version.
|
||||
type SelectionSpec struct {
|
||||
Boundary VersionSpec
|
||||
Operator SelectionOp
|
||||
}
|
||||
|
||||
func (s SelectionSpec) isSpec() {}
|
||||
|
||||
// VersionSpec represents the boundary within a SelectionSpec.
|
||||
type VersionSpec struct {
|
||||
Major NumConstraint
|
||||
Minor NumConstraint
|
||||
Patch NumConstraint
|
||||
Prerelease string
|
||||
Metadata string
|
||||
}
|
||||
|
||||
func (s VersionSpec) isSpec() {}
|
||||
|
||||
// IsExact returns bool if all of the version numbers in the receiver are
|
||||
// fully-constrained. This is the same as s.ConstraintDepth() == ConstrainedPatch
|
||||
func (s VersionSpec) IsExact() bool {
|
||||
return s.ConstraintDepth() == ConstrainedPatch
|
||||
}
|
||||
|
||||
// ConstraintDepth returns the constraint depth of the receiver, which is
|
||||
// the most specifc version number segment that is exactly constrained.
|
||||
//
|
||||
// The constraints must be consistent, which means that if a given segment
|
||||
// is unconstrained then all of the deeper segments must also be unconstrained.
|
||||
// If not, this method will panic. Version specs produced by the parsers in
|
||||
// this package are guaranteed to be consistent.
|
||||
func (s VersionSpec) ConstraintDepth() ConstraintDepth {
|
||||
if s == (VersionSpec{}) {
|
||||
// zero value is a degenerate case meaning completely unconstrained
|
||||
return Unconstrained
|
||||
}
|
||||
|
||||
switch {
|
||||
case s.Major.Unconstrained:
|
||||
if !(s.Minor.Unconstrained && s.Patch.Unconstrained && s.Prerelease == "" && s.Metadata == "") {
|
||||
panic("inconsistent constraint depth")
|
||||
}
|
||||
return Unconstrained
|
||||
case s.Minor.Unconstrained:
|
||||
if !(s.Patch.Unconstrained && s.Prerelease == "" && s.Metadata == "") {
|
||||
panic("inconsistent constraint depth")
|
||||
}
|
||||
return ConstrainedMajor
|
||||
case s.Patch.Unconstrained:
|
||||
if s.Prerelease != "" || s.Metadata != "" {
|
||||
panic(fmt.Errorf("inconsistent constraint depth: wildcard major, minor and patch followed by prerelease %q and metadata %q", s.Prerelease, s.Metadata))
|
||||
}
|
||||
return ConstrainedMinor
|
||||
default:
|
||||
return ConstrainedPatch
|
||||
}
|
||||
}
|
||||
|
||||
// ConstraintBounds returns two exact VersionSpecs that represent the upper
|
||||
// and lower bounds of the possibly-inexact receiver. If the receiver
|
||||
// is already exact then the two bounds are identical and have operator
|
||||
// OpEqual. If they are different then the lower bound is OpGreaterThanOrEqual
|
||||
// and the upper bound is OpLessThan.
|
||||
//
|
||||
// As a special case, if the version spec is entirely unconstrained the
|
||||
// two bounds will be identical and the zero value of SelectionSpec. For
|
||||
// consistency, this result is also returned if the receiver is already
|
||||
// the zero value of VersionSpec, since a zero spec represents a lack of
|
||||
// constraint.
|
||||
//
|
||||
// The constraints must be consistent as defined by ConstraintDepth, or this
|
||||
// method will panic.
|
||||
func (s VersionSpec) ConstraintBounds() (SelectionSpec, SelectionSpec) {
|
||||
switch s.ConstraintDepth() {
|
||||
case Unconstrained:
|
||||
return SelectionSpec{}, SelectionSpec{}
|
||||
case ConstrainedMajor:
|
||||
lowerBound := s.ConstrainToZero()
|
||||
lowerBound.Metadata = ""
|
||||
upperBound := lowerBound
|
||||
upperBound.Major.Num++
|
||||
upperBound.Minor.Num = 0
|
||||
upperBound.Patch.Num = 0
|
||||
upperBound.Prerelease = ""
|
||||
upperBound.Metadata = ""
|
||||
return SelectionSpec{
|
||||
Operator: OpGreaterThanOrEqual,
|
||||
Boundary: lowerBound,
|
||||
}, SelectionSpec{
|
||||
Operator: OpLessThan,
|
||||
Boundary: upperBound,
|
||||
}
|
||||
case ConstrainedMinor:
|
||||
lowerBound := s.ConstrainToZero()
|
||||
lowerBound.Metadata = ""
|
||||
upperBound := lowerBound
|
||||
upperBound.Minor.Num++
|
||||
upperBound.Patch.Num = 0
|
||||
upperBound.Metadata = ""
|
||||
return SelectionSpec{
|
||||
Operator: OpGreaterThanOrEqual,
|
||||
Boundary: lowerBound,
|
||||
}, SelectionSpec{
|
||||
Operator: OpLessThan,
|
||||
Boundary: upperBound,
|
||||
}
|
||||
default:
|
||||
eq := SelectionSpec{
|
||||
Operator: OpEqual,
|
||||
Boundary: s,
|
||||
}
|
||||
return eq, eq
|
||||
}
|
||||
}
|
||||
|
||||
// ConstrainToZero returns a copy of the receiver with all of its
|
||||
// unconstrained numeric segments constrained to zero.
|
||||
func (s VersionSpec) ConstrainToZero() VersionSpec {
|
||||
switch s.ConstraintDepth() {
|
||||
case Unconstrained:
|
||||
s.Major = NumConstraint{Num: 0}
|
||||
s.Minor = NumConstraint{Num: 0}
|
||||
s.Patch = NumConstraint{Num: 0}
|
||||
s.Prerelease = ""
|
||||
s.Metadata = ""
|
||||
case ConstrainedMajor:
|
||||
s.Minor = NumConstraint{Num: 0}
|
||||
s.Patch = NumConstraint{Num: 0}
|
||||
s.Prerelease = ""
|
||||
s.Metadata = ""
|
||||
case ConstrainedMinor:
|
||||
s.Patch = NumConstraint{Num: 0}
|
||||
s.Prerelease = ""
|
||||
s.Metadata = ""
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// ConstrainToUpperBound returns a copy of the receiver with all of its
|
||||
// unconstrained numeric segments constrained to zero and its last
|
||||
// constrained segment increased by one.
|
||||
//
|
||||
// This operation is not meaningful for an entirely unconstrained VersionSpec,
|
||||
// so will return the zero value of the type in that case.
|
||||
func (s VersionSpec) ConstrainToUpperBound() VersionSpec {
|
||||
switch s.ConstraintDepth() {
|
||||
case Unconstrained:
|
||||
return VersionSpec{}
|
||||
case ConstrainedMajor:
|
||||
s.Major.Num++
|
||||
s.Minor = NumConstraint{Num: 0}
|
||||
s.Patch = NumConstraint{Num: 0}
|
||||
s.Prerelease = ""
|
||||
s.Metadata = ""
|
||||
case ConstrainedMinor:
|
||||
s.Minor.Num++
|
||||
s.Patch = NumConstraint{Num: 0}
|
||||
s.Prerelease = ""
|
||||
s.Metadata = ""
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func (s VersionSpec) String() string {
|
||||
var buf bytes.Buffer
|
||||
fmt.Fprintf(&buf, "%s.%s.%s", s.Major, s.Minor, s.Patch)
|
||||
if s.Prerelease != "" {
|
||||
fmt.Fprintf(&buf, "-%s", s.Prerelease)
|
||||
}
|
||||
if s.Metadata != "" {
|
||||
fmt.Fprintf(&buf, "+%s", s.Metadata)
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
type SelectionOp rune
|
||||
|
||||
//go:generate stringer -type SelectionOp
|
||||
|
||||
const (
|
||||
OpUnconstrained SelectionOp = 0
|
||||
OpGreaterThan SelectionOp = '>'
|
||||
OpLessThan SelectionOp = '<'
|
||||
OpGreaterThanOrEqual SelectionOp = '≥'
|
||||
OpGreaterThanOrEqualPatchOnly SelectionOp = '~'
|
||||
OpGreaterThanOrEqualMinorOnly SelectionOp = '^'
|
||||
OpLessThanOrEqual SelectionOp = '≤'
|
||||
OpEqual SelectionOp = '='
|
||||
OpNotEqual SelectionOp = '≠'
|
||||
OpMatch SelectionOp = '*'
|
||||
)
|
||||
|
||||
type NumConstraint struct {
|
||||
Num uint64
|
||||
Unconstrained bool
|
||||
}
|
||||
|
||||
func (c NumConstraint) String() string {
|
||||
if c.Unconstrained {
|
||||
return "*"
|
||||
} else {
|
||||
return strconv.FormatUint(c.Num, 10)
|
||||
}
|
||||
}
|
||||
|
||||
type ConstraintDepth int
|
||||
|
||||
//go:generate stringer -type ConstraintDepth
|
||||
|
||||
const (
|
||||
Unconstrained ConstraintDepth = 0
|
||||
ConstrainedMajor ConstraintDepth = 1
|
||||
ConstrainedMinor ConstraintDepth = 2
|
||||
ConstrainedPatch ConstraintDepth = 3
|
||||
)
|
81
vendor/github.com/apparentlymart/go-versions/versions/constraints/version.go
generated
vendored
Normal file
81
vendor/github.com/apparentlymart/go-versions/versions/constraints/version.go
generated
vendored
Normal file
|
@ -0,0 +1,81 @@
|
|||
package constraints
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ParseExactVersion parses a string that must contain the specification of a
|
||||
// single, exact version, and then returns it as a VersionSpec.
|
||||
//
|
||||
// This is primarily here to allow versions.ParseVersion to re-use the
|
||||
// constraint grammar, and isn't very useful for direct use from calling
|
||||
// applications.
|
||||
func ParseExactVersion(vs string) (VersionSpec, error) {
|
||||
spec := VersionSpec{}
|
||||
|
||||
if strings.TrimSpace(vs) == "" {
|
||||
return spec, fmt.Errorf("empty specification")
|
||||
}
|
||||
|
||||
raw, remain := scanConstraint(vs)
|
||||
|
||||
switch strings.TrimSpace(raw.op) {
|
||||
case ">", ">=", "<", "<=", "!", "!=", "~>", "^", "~":
|
||||
// If it looks like the user was trying to write a constraint string
|
||||
// then we'll help them out with a more specialized error.
|
||||
return spec, fmt.Errorf("can't use constraint operator %q; an exact version is required", raw.op)
|
||||
case "":
|
||||
// Empty operator is okay as long as we don't also have separator spaces.
|
||||
// (Caller can trim off spaces beforehand if they want to tolerate this.)
|
||||
if raw.sep != "" {
|
||||
return spec, fmt.Errorf("extraneous spaces at start of specification")
|
||||
}
|
||||
default:
|
||||
return spec, fmt.Errorf("invalid sequence %q at start of specification", raw.op)
|
||||
}
|
||||
|
||||
if remain != "" {
|
||||
remain = strings.TrimSpace(remain)
|
||||
switch {
|
||||
case remain == "":
|
||||
return spec, fmt.Errorf("extraneous spaces at end of specification")
|
||||
case strings.HasPrefix(vs, "v"):
|
||||
// User seems to be trying to use a "v" prefix, like "v1.0.0"
|
||||
return spec, fmt.Errorf(`a "v" prefix should not be used`)
|
||||
case strings.HasPrefix(remain, ",") || strings.HasPrefix(remain, "|"):
|
||||
// User seems to be trying to list/combine multiple versions
|
||||
return spec, fmt.Errorf("can't specify multiple versions; a single exact version is required")
|
||||
case strings.HasPrefix(remain, "-"):
|
||||
// User seems to be trying to use the npm-style range operator
|
||||
return spec, fmt.Errorf("can't specify version range; a single exact version is required")
|
||||
case strings.HasPrefix(strings.TrimSpace(vs), remain):
|
||||
// Whole string is invalid, then.
|
||||
return spec, fmt.Errorf("invalid specification; required format is three positive integers separated by periods")
|
||||
default:
|
||||
return spec, fmt.Errorf("invalid characters %q", remain)
|
||||
}
|
||||
}
|
||||
|
||||
if raw.numCt > 3 {
|
||||
return spec, fmt.Errorf("too many numbered portions; only three are allowed (major, minor, patch)")
|
||||
}
|
||||
|
||||
for i := raw.numCt; i < len(raw.nums); i++ {
|
||||
raw.nums[i] = "0"
|
||||
}
|
||||
|
||||
for i, s := range raw.nums {
|
||||
switch {
|
||||
case isWildcardNum(s):
|
||||
// Can't use wildcards in an exact specification
|
||||
return spec, fmt.Errorf("can't use wildcard for %s number; an exact version is required", rawNumNames[i])
|
||||
}
|
||||
}
|
||||
|
||||
// Since we eliminated all of the unconstrained cases above, either by normalizing
|
||||
// or returning an error, we are guaranteed to get constrained numbers here.
|
||||
spec = raw.VersionSpec()
|
||||
|
||||
return spec, nil
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
// Package versions is a library for wrangling version numbers in Go.
|
||||
//
|
||||
// There are many libraries offering some or all of this functionality.
|
||||
// This package aims to distinguish itself by offering a more convenient and
|
||||
// ergonomic API than seen in some other libraries. Code that is resolving
|
||||
// versions and version constraints tends to be hairy and complex already, so
|
||||
// an expressive API for talking about these concepts will hopefully help to
|
||||
// make that code more readable.
|
||||
//
|
||||
// The version model is based on Semantic Versioning as defined at
|
||||
// https://semver.org/ . Semantic Versioning does not include any specification
|
||||
// for constraints, so the constraint model is based on that used by rubygems,
|
||||
// allowing for upper and lower bounds as well as individual version exclusions.
|
||||
package versions
|
|
@ -0,0 +1,149 @@
|
|||
package versions
|
||||
|
||||
import (
|
||||
"sort"
|
||||
)
|
||||
|
||||
// List is a slice of Version that implements sort.Interface, and also includes
|
||||
// some other helper functions.
|
||||
type List []Version
|
||||
|
||||
// Filter removes from the receiver any elements that are not in the given
|
||||
// set, moving retained elements to lower indices to close any gaps and
|
||||
// modifying the underlying array in-place. The return value is a slice
|
||||
// describing the new bounds within the existing backing array. The relative
|
||||
// ordering of the retained elements is preserved.
|
||||
//
|
||||
// The result must always be either the same length or shorter than the
|
||||
// initial value, so no allocation is required.
|
||||
//
|
||||
// As a special case, if the result would be a slice of length zero then a
|
||||
// nil slice is returned instead, leaving the backing array untouched.
|
||||
func (l List) Filter(set Set) List {
|
||||
writeI := 0
|
||||
|
||||
for readI := range l {
|
||||
if set.Has(l[readI]) {
|
||||
l[writeI] = l[readI]
|
||||
writeI++
|
||||
}
|
||||
}
|
||||
|
||||
if writeI == 0 {
|
||||
return nil
|
||||
}
|
||||
return l[:writeI:len(l)]
|
||||
}
|
||||
|
||||
// Newest returns the newest version in the list, or Unspecified if the list
|
||||
// is empty.
|
||||
//
|
||||
// Since build metadata does not participate in precedence, it is possible
|
||||
// that a given list may have multiple equally-new versions; in that case
|
||||
// Newest will return an arbitrary version from that subset.
|
||||
func (l List) Newest() Version {
|
||||
ret := Unspecified
|
||||
for i := len(l) - 1; i >= 0; i-- {
|
||||
if l[i].GreaterThan(ret) {
|
||||
ret = l[i]
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// NewestInSet is like Filter followed by Newest, except that it does not
|
||||
// modify the underlying array. This is convenient for the common case of
|
||||
// selecting the newest version from a set derived from a user-supplied
|
||||
// constraint.
|
||||
//
|
||||
// Similar to Newest, the result is Unspecified if the list is empty or if
|
||||
// none of the items are in the given set. Also similar to newest, if there
|
||||
// are multiple newest versions (possibly differentiated only by metadata)
|
||||
// then one is arbitrarily chosen.
|
||||
func (l List) NewestInSet(set Set) Version {
|
||||
ret := Unspecified
|
||||
for i := len(l) - 1; i >= 0; i-- {
|
||||
if l[i].GreaterThan(ret) && set.Has(l[i]) {
|
||||
ret = l[i]
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// NewestList returns a List containing all of the list items that have the
|
||||
// highest precedence.
|
||||
//
|
||||
// For an already-sorted list, the returned slice is a sub-slice of the
|
||||
// receiver, sharing the same backing array. For an unsorted list, a new
|
||||
// array is allocated for the result. For an empty list, the result is always
|
||||
// nil.
|
||||
//
|
||||
// Relative ordering of elements in the receiver is preserved in the output.
|
||||
func (l List) NewestList() List {
|
||||
if len(l) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if l.IsSorted() {
|
||||
// This is a happy path since we can just count off items from the
|
||||
// end of our existing list until we find one that is not the same
|
||||
// as the last.
|
||||
var i int
|
||||
n := len(l)
|
||||
for i = n - 1; i >= 0; i-- {
|
||||
if !l[i].Same(l[n-1]) {
|
||||
break
|
||||
}
|
||||
}
|
||||
if i < 0 {
|
||||
i = 0
|
||||
}
|
||||
return l[i:]
|
||||
}
|
||||
|
||||
// For an unsorted list we'll allocate so that we can construct a new,
|
||||
// filtered slice.
|
||||
ret := make(List, 0, 1) // one item is the common case, in the absense of build metadata
|
||||
example := l.Newest()
|
||||
for _, v := range l {
|
||||
if v.Same(example) {
|
||||
ret = append(ret, v)
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// Set returns a finite Set containing the versions in the receiver.
|
||||
//
|
||||
// Although it is possible to recover a list from the return value using
|
||||
// its List method, the result may be in a different order and will have
|
||||
// any duplicate elements from the receiving list consolidated.
|
||||
func (l List) Set() Set {
|
||||
return Selection(l...)
|
||||
}
|
||||
|
||||
func (l List) Len() int {
|
||||
return len(l)
|
||||
}
|
||||
|
||||
func (l List) Less(i, j int) bool {
|
||||
return l[i].LessThan(l[j])
|
||||
}
|
||||
|
||||
func (l List) Swap(i, j int) {
|
||||
l[i], l[j] = l[j], l[i]
|
||||
}
|
||||
|
||||
// Sort applies an in-place sort on the list, preserving the relative order of
|
||||
// any elements that differ only in build metadata. Earlier versions sort
|
||||
// first, so the newest versions will be at the highest indices in the list
|
||||
// once this method returns.
|
||||
func (l List) Sort() {
|
||||
sort.Stable(l)
|
||||
}
|
||||
|
||||
// IsSorted returns true if the list is already in ascending order by
|
||||
// version priority.
|
||||
func (l List) IsSorted() bool {
|
||||
return sort.IsSorted(l)
|
||||
}
|
|
@ -0,0 +1,243 @@
|
|||
package versions
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/apparentlymart/go-versions/versions/constraints"
|
||||
)
|
||||
|
||||
// ParseVersion attempts to parse the given string as a semantic version
|
||||
// specification, and returns the result if successful.
|
||||
//
|
||||
// If the given string is not parseable then an error is returned that is
|
||||
// suitable for display directly to a hypothetical end-user that provided this
|
||||
// version string, as long as they can read English.
|
||||
func ParseVersion(s string) (Version, error) {
|
||||
spec, err := constraints.ParseExactVersion(s)
|
||||
if err != nil {
|
||||
return Unspecified, err
|
||||
}
|
||||
return versionFromExactVersionSpec(spec), nil
|
||||
}
|
||||
|
||||
// MustParseVersion is the same as ParseVersion except that it will panic
|
||||
// instead of returning an error.
|
||||
func MustParseVersion(s string) Version {
|
||||
v, err := ParseVersion(s)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// MeetingConstraints returns a version set that contains all of the versions
|
||||
// that meet the given constraints, specified using the Spec type from the
|
||||
// constraints package.
|
||||
//
|
||||
// The resulting Set has all pre-release versions excluded, except any that
|
||||
// are explicitly mentioned as exact selections. For example, the constraint
|
||||
// "2.0.0-beta1 || >2" contains 2.0.0-beta1 but not 2.0.0-beta2 or 3.0.0-beta1.
|
||||
// This additional constraint on pre-releases can be avoided by calling
|
||||
// MeetingConstraintsExact instead, at which point the caller can apply other
|
||||
// logic to deal with prereleases.
|
||||
//
|
||||
// This function expects an internally-consistent Spec like what would be
|
||||
// generated by that package's constraint parsers. Behavior is undefined --
|
||||
// including the possibility of panics -- if specs are hand-created and the
|
||||
// expected invariants aren't met.
|
||||
func MeetingConstraints(spec constraints.Spec) Set {
|
||||
exact := MeetingConstraintsExact(spec)
|
||||
reqd := exact.AllRequested().List()
|
||||
set := Intersection(Released, exact)
|
||||
reqd = reqd.Filter(Prerelease)
|
||||
if len(reqd) != 0 {
|
||||
set = Union(Selection(reqd...), set)
|
||||
}
|
||||
return set
|
||||
}
|
||||
|
||||
// MeetingConstraintsExact is like MeetingConstraints except that it doesn't
|
||||
// apply the extra rules to exclude pre-release versions that are not
|
||||
// explicitly requested.
|
||||
//
|
||||
// This means that given a constraint ">=1.0.0 <2.0.0" a hypothetical version
|
||||
// 2.0.0-beta1 _is_ in the returned set, because prerelease versions have
|
||||
// lower precedence than their corresponding release.
|
||||
//
|
||||
// A caller can use this to implement its own specialized handling of
|
||||
// pre-release versions by applying additional set operations to the result,
|
||||
// such as intersecting it with the predefined set versions.Released to
|
||||
// remove prerelease versions altogether.
|
||||
func MeetingConstraintsExact(spec constraints.Spec) Set {
|
||||
if spec == nil {
|
||||
return All
|
||||
}
|
||||
|
||||
switch ts := spec.(type) {
|
||||
|
||||
case constraints.VersionSpec:
|
||||
lowerBound, upperBound := ts.ConstraintBounds()
|
||||
switch lowerBound.Operator {
|
||||
case constraints.OpUnconstrained:
|
||||
return All
|
||||
case constraints.OpEqual:
|
||||
return Only(versionFromExactVersionSpec(lowerBound.Boundary))
|
||||
default:
|
||||
return AtLeast(
|
||||
versionFromExactVersionSpec(lowerBound.Boundary),
|
||||
).Intersection(
|
||||
OlderThan(versionFromExactVersionSpec(upperBound.Boundary)))
|
||||
}
|
||||
|
||||
case constraints.SelectionSpec:
|
||||
lower := ts.Boundary.ConstrainToZero()
|
||||
if ts.Operator != constraints.OpEqual && ts.Operator != constraints.OpNotEqual {
|
||||
lower.Metadata = "" // metadata is only considered for exact matches
|
||||
}
|
||||
|
||||
switch ts.Operator {
|
||||
case constraints.OpUnconstrained:
|
||||
// Degenerate case, but we'll allow it.
|
||||
return All
|
||||
case constraints.OpMatch:
|
||||
// The match operator uses the constraints implied by the
|
||||
// Boundary version spec as the specification.
|
||||
// Note that we discard "lower" in this case, because we do want
|
||||
// to match our metadata if it's specified.
|
||||
return MeetingConstraintsExact(ts.Boundary)
|
||||
case constraints.OpEqual, constraints.OpNotEqual:
|
||||
set := Only(versionFromExactVersionSpec(lower))
|
||||
if ts.Operator == constraints.OpNotEqual {
|
||||
// We want everything _except_ what's in our set, then.
|
||||
set = All.Subtract(set)
|
||||
}
|
||||
return set
|
||||
case constraints.OpGreaterThan:
|
||||
return NewerThan(versionFromExactVersionSpec(lower))
|
||||
case constraints.OpGreaterThanOrEqual:
|
||||
return AtLeast(versionFromExactVersionSpec(lower))
|
||||
case constraints.OpLessThan:
|
||||
return OlderThan(versionFromExactVersionSpec(lower))
|
||||
case constraints.OpLessThanOrEqual:
|
||||
return AtMost(versionFromExactVersionSpec(lower))
|
||||
case constraints.OpGreaterThanOrEqualMinorOnly:
|
||||
upper := lower
|
||||
upper.Major.Num++
|
||||
upper.Minor.Num = 0
|
||||
upper.Patch.Num = 0
|
||||
upper.Prerelease = ""
|
||||
return AtLeast(
|
||||
versionFromExactVersionSpec(lower),
|
||||
).Intersection(
|
||||
OlderThan(versionFromExactVersionSpec(upper)))
|
||||
case constraints.OpGreaterThanOrEqualPatchOnly:
|
||||
upper := lower
|
||||
upper.Minor.Num++
|
||||
upper.Patch.Num = 0
|
||||
upper.Prerelease = ""
|
||||
return AtLeast(
|
||||
versionFromExactVersionSpec(lower),
|
||||
).Intersection(
|
||||
OlderThan(versionFromExactVersionSpec(upper)))
|
||||
default:
|
||||
panic(fmt.Errorf("unsupported constraints.SelectionOp %s", ts.Operator))
|
||||
}
|
||||
|
||||
case constraints.UnionSpec:
|
||||
if len(ts) == 0 {
|
||||
return All
|
||||
}
|
||||
if len(ts) == 1 {
|
||||
return MeetingConstraintsExact(ts[0])
|
||||
}
|
||||
union := make(setUnion, len(ts))
|
||||
for i, subSpec := range ts {
|
||||
union[i] = MeetingConstraintsExact(subSpec).setI
|
||||
}
|
||||
return Set{setI: union}
|
||||
|
||||
case constraints.IntersectionSpec:
|
||||
if len(ts) == 0 {
|
||||
return All
|
||||
}
|
||||
if len(ts) == 1 {
|
||||
return MeetingConstraintsExact(ts[0])
|
||||
}
|
||||
intersection := make(setIntersection, len(ts))
|
||||
for i, subSpec := range ts {
|
||||
intersection[i] = MeetingConstraintsExact(subSpec).setI
|
||||
}
|
||||
return Set{setI: intersection}
|
||||
|
||||
default:
|
||||
// should never happen because the above cases are exhaustive for
|
||||
// all valid constraint implementations.
|
||||
panic(fmt.Errorf("unsupported constraints.Spec implementation %T", spec))
|
||||
}
|
||||
}
|
||||
|
||||
// MeetingConstraintsString attempts to parse the given spec as a constraints
|
||||
// string in our canonical format, which is most similar to the syntax used by
|
||||
// npm, Go's "dep" tool, Rust's "cargo", etc.
|
||||
//
|
||||
// This is a covenience wrapper around calling constraints.Parse and then
|
||||
// passing the result to MeetingConstraints. Call into the constraints package
|
||||
// yourself for access to the constraint tree.
|
||||
//
|
||||
// If unsuccessful, the error from the underlying parser is returned verbatim.
|
||||
// Parser errors are suitable for showing to an end-user in situations where
|
||||
// the given spec came from user input.
|
||||
func MeetingConstraintsString(spec string) (Set, error) {
|
||||
s, err := constraints.Parse(spec)
|
||||
if err != nil {
|
||||
return None, err
|
||||
}
|
||||
return MeetingConstraints(s), nil
|
||||
}
|
||||
|
||||
// MeetingConstraintsStringRuby attempts to parse the given spec as a
|
||||
// "Ruby-style" version constraint string, and returns the set of versions
|
||||
// that match the constraint if successful.
|
||||
//
|
||||
// If unsuccessful, the error from the underlying parser is returned verbatim.
|
||||
// Parser errors are suitable for showing to an end-user in situations where
|
||||
// the given spec came from user input.
|
||||
//
|
||||
// "Ruby-style" here is not a promise of exact compatibility with rubygems
|
||||
// or any other Ruby tools. Rather, it refers to this parser using a syntax
|
||||
// that is intended to feel familiar to those who are familiar with rubygems
|
||||
// syntax.
|
||||
//
|
||||
// Constraints are parsed in "multi" mode, allowing multiple comma-separated
|
||||
// constraints that are combined with the Intersection operator. For more
|
||||
// control over the parsing process, use the constraints package API directly
|
||||
// and then call MeetingConstraints.
|
||||
func MeetingConstraintsStringRuby(spec string) (Set, error) {
|
||||
s, err := constraints.ParseRubyStyleMulti(spec)
|
||||
if err != nil {
|
||||
return None, err
|
||||
}
|
||||
return MeetingConstraints(s), nil
|
||||
}
|
||||
|
||||
// MustMakeSet can be used to wrap any function that returns a set and an error
|
||||
// to make it panic if an error occurs and return the set otherwise.
|
||||
//
|
||||
// This is intended for tests and other situations where input is from
|
||||
// known-good constants.
|
||||
func MustMakeSet(set Set, err error) Set {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return set
|
||||
}
|
||||
|
||||
func versionFromExactVersionSpec(spec constraints.VersionSpec) Version {
|
||||
return Version{
|
||||
Major: spec.Major.Num,
|
||||
Minor: spec.Minor.Num,
|
||||
Patch: spec.Patch.Num,
|
||||
Prerelease: VersionExtra(spec.Prerelease),
|
||||
Metadata: VersionExtra(spec.Metadata),
|
||||
}
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
package versions
|
||||
|
||||
// Set is a set of versions, usually created by parsing a constraint string.
|
||||
type Set struct {
|
||||
setI
|
||||
}
|
||||
|
||||
// setI is the private interface implemented by our various constraint
|
||||
// operators.
|
||||
type setI interface {
|
||||
Has(v Version) bool
|
||||
AllRequested() Set
|
||||
GoString() string
|
||||
}
|
||||
|
||||
// Has returns true if the given version is a member of the receiving set.
|
||||
func (s Set) Has(v Version) bool {
|
||||
// The special Unspecified version is excluded as soon as any sort of
|
||||
// constraint is applied, and so the only set it is a member of is
|
||||
// the special All set.
|
||||
if v == Unspecified {
|
||||
return s == All
|
||||
}
|
||||
|
||||
return s.setI.Has(v)
|
||||
}
|
||||
|
||||
// Requests returns true if the given version is specifically requested by
|
||||
// the receiving set.
|
||||
//
|
||||
// Requesting is a stronger form of set membership that represents an explicit
|
||||
// request for a particular version, as opposed to the version just happening
|
||||
// to match some criteria.
|
||||
//
|
||||
// The functions Only and Selection mark their arguments as requested in
|
||||
// their returned sets. Exact version constraints given in constraint strings
|
||||
// also mark their versions as requested.
|
||||
//
|
||||
// The concept of requesting is intended to help deal with pre-release versions
|
||||
// in a safe and convenient way. When given generic version constraints like
|
||||
// ">= 1.0.0" the user generally does not intend to match a pre-release version
|
||||
// like "2.0.0-beta1", but it is important to stil be able to use that
|
||||
// version if explicitly requested using the constraint string "2.0.0-beta1".
|
||||
func (s Set) Requests(v Version) bool {
|
||||
return s.AllRequested().Has(v)
|
||||
}
|
||||
|
||||
// AllRequested returns a subset of the receiver containing only the requested
|
||||
// versions, as defined in the documentation for the method Requests.
|
||||
//
|
||||
// This can be used in conjunction with the predefined set "Released" to
|
||||
// include pre-release versions only by explicit request, which is supported
|
||||
// via the helper method WithoutUnrequestedPrereleases.
|
||||
//
|
||||
// The result of AllRequested is always a finite set.
|
||||
func (s Set) AllRequested() Set {
|
||||
return s.setI.AllRequested()
|
||||
}
|
||||
|
||||
// WithoutUnrequestedPrereleases returns a new set that includes all released
|
||||
// versions from the receiving set, plus any explicitly-requested pre-releases,
|
||||
// but does not include any unrequested pre-releases.
|
||||
//
|
||||
// "Requested" here is as defined in the documentation for the "Requests" method.
|
||||
//
|
||||
// This method is equivalent to the following set operations:
|
||||
//
|
||||
// versions.Union(s.AllRequested(), s.Intersection(versions.Released))
|
||||
func (s Set) WithoutUnrequestedPrereleases() Set {
|
||||
return Union(s.AllRequested(), Released.Intersection(s))
|
||||
}
|
||||
|
||||
// UnmarshalText is an implementation of encoding.TextUnmarshaler, allowing
|
||||
// sets to be automatically unmarshalled from strings in text-based
|
||||
// serialization formats, including encoding/json.
|
||||
//
|
||||
// The format expected is what is accepted by MeetingConstraintsString. Any
|
||||
// parser errors are passed on verbatim to the caller.
|
||||
func (s *Set) UnmarshalText(text []byte) error {
|
||||
str := string(text)
|
||||
new, err := MeetingConstraintsString(str)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*s = new
|
||||
return nil
|
||||
}
|
||||
|
||||
var InitialDevelopment Set = OlderThan(MustParseVersion("1.0.0"))
|
98
vendor/github.com/apparentlymart/go-versions/versions/set_bound.go
generated
vendored
Normal file
98
vendor/github.com/apparentlymart/go-versions/versions/set_bound.go
generated
vendored
Normal file
|
@ -0,0 +1,98 @@
|
|||
package versions
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type setBound struct {
|
||||
v Version
|
||||
op setBoundOp
|
||||
}
|
||||
|
||||
func (s setBound) Has(v Version) bool {
|
||||
switch s.op {
|
||||
case setBoundGT:
|
||||
return v.GreaterThan(s.v)
|
||||
case setBoundGTE:
|
||||
return v.GreaterThan(s.v) || v.Same(s.v)
|
||||
case setBoundLT:
|
||||
return v.LessThan(s.v)
|
||||
case setBoundLTE:
|
||||
return v.LessThan(s.v) || v.Same(s.v)
|
||||
default:
|
||||
// Should never happen because the above is exhaustive
|
||||
panic("invalid setBound operator")
|
||||
}
|
||||
}
|
||||
|
||||
func (s setBound) AllRequested() Set {
|
||||
// Inequalities request nothing.
|
||||
return None
|
||||
}
|
||||
|
||||
func (s setBound) GoString() string {
|
||||
switch s.op {
|
||||
case setBoundGT:
|
||||
return fmt.Sprintf("versions.NewerThan(%#v)", s.v)
|
||||
case setBoundGTE:
|
||||
return fmt.Sprintf("versions.AtLeast(%#v)", s.v)
|
||||
case setBoundLT:
|
||||
return fmt.Sprintf("versions.OlderThan(%#v)", s.v)
|
||||
case setBoundLTE:
|
||||
return fmt.Sprintf("versions.AtMost(%#v)", s.v)
|
||||
default:
|
||||
// Should never happen because the above is exhaustive
|
||||
return fmt.Sprintf("versions.Set{versions.setBound{v:%#v,op:%#v}}", s.v, s.op)
|
||||
}
|
||||
}
|
||||
|
||||
// NewerThan returns a set containing all versions greater than the given
|
||||
// version, non-inclusive.
|
||||
func NewerThan(v Version) Set {
|
||||
return Set{
|
||||
setI: setBound{
|
||||
v: v,
|
||||
op: setBoundGT,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// OlderThan returns a set containing all versions lower than the given
|
||||
// version, non-inclusive.
|
||||
func OlderThan(v Version) Set {
|
||||
return Set{
|
||||
setI: setBound{
|
||||
v: v,
|
||||
op: setBoundLT,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// AtLeast returns a set containing all versions greater than or equal to
|
||||
// the given version.
|
||||
func AtLeast(v Version) Set {
|
||||
return Set{
|
||||
setI: setBound{
|
||||
v: v,
|
||||
op: setBoundGTE,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// AtMost returns a set containing all versions less than or equal to the given
|
||||
// version, non-inclusive.
|
||||
func AtMost(v Version) Set {
|
||||
return Set{
|
||||
setI: setBound{
|
||||
v: v,
|
||||
op: setBoundLTE,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type setBoundOp rune
|
||||
|
||||
const setBoundGT = '>'
|
||||
const setBoundGTE = '≥'
|
||||
const setBoundLT = '<'
|
||||
const setBoundLTE = '≤'
|
103
vendor/github.com/apparentlymart/go-versions/versions/set_exact.go
generated
vendored
Normal file
103
vendor/github.com/apparentlymart/go-versions/versions/set_exact.go
generated
vendored
Normal file
|
@ -0,0 +1,103 @@
|
|||
package versions
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type setExact map[Version]struct{}
|
||||
|
||||
func (s setExact) Has(v Version) bool {
|
||||
_, has := s[v]
|
||||
return has
|
||||
}
|
||||
|
||||
func (s setExact) AllRequested() Set {
|
||||
// We just return the receiver verbatim here, because everything in it
|
||||
// is explicitly requested.
|
||||
return Set{setI: s}
|
||||
}
|
||||
|
||||
func (s setExact) GoString() string {
|
||||
if len(s) == 0 {
|
||||
// Degenerate case; caller should use None instead
|
||||
return "versions.Set{setExact{}}"
|
||||
}
|
||||
|
||||
if len(s) == 1 {
|
||||
var first Version
|
||||
for v := range s {
|
||||
first = v
|
||||
break
|
||||
}
|
||||
return fmt.Sprintf("versions.Only(%#v)", first)
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
fmt.Fprint(&buf, "versions.Selection(")
|
||||
versions := s.listVersions()
|
||||
versions.Sort()
|
||||
for i, version := range versions {
|
||||
if i == 0 {
|
||||
fmt.Fprint(&buf, version.GoString())
|
||||
} else {
|
||||
fmt.Fprintf(&buf, ", %#v", version)
|
||||
}
|
||||
}
|
||||
fmt.Fprint(&buf, ")")
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// Only returns a version set containing only the given version.
|
||||
//
|
||||
// This function is guaranteed to produce a finite set.
|
||||
func Only(v Version) Set {
|
||||
return Set{
|
||||
setI: setExact{v: struct{}{}},
|
||||
}
|
||||
}
|
||||
|
||||
// Selection returns a version set containing only the versions given
|
||||
// as arguments.
|
||||
//
|
||||
// This function is guaranteed to produce a finite set.
|
||||
func Selection(vs ...Version) Set {
|
||||
if len(vs) == 0 {
|
||||
return None
|
||||
}
|
||||
ret := make(setExact)
|
||||
for _, v := range vs {
|
||||
ret[v] = struct{}{}
|
||||
}
|
||||
return Set{setI: ret}
|
||||
}
|
||||
|
||||
// Exactly returns true if and only if the receiving set is finite and
|
||||
// contains only a single version that is the same as the version given.
|
||||
func (s Set) Exactly(v Version) bool {
|
||||
if !s.IsFinite() {
|
||||
return false
|
||||
}
|
||||
l := s.List()
|
||||
if len(l) != 1 {
|
||||
return false
|
||||
}
|
||||
return v.Same(l[0])
|
||||
}
|
||||
|
||||
var _ setFinite = setExact(nil)
|
||||
|
||||
func (s setExact) isFinite() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (s setExact) listVersions() List {
|
||||
if len(s) == 0 {
|
||||
return nil
|
||||
}
|
||||
ret := make(List, 0, len(s))
|
||||
for v := range s {
|
||||
ret = append(ret, v)
|
||||
}
|
||||
return ret
|
||||
}
|
49
vendor/github.com/apparentlymart/go-versions/versions/set_extremes.go
generated
vendored
Normal file
49
vendor/github.com/apparentlymart/go-versions/versions/set_extremes.go
generated
vendored
Normal file
|
@ -0,0 +1,49 @@
|
|||
package versions
|
||||
|
||||
// All is an infinite set containing all possible versions.
|
||||
var All Set
|
||||
|
||||
// None is a finite set containing no versions.
|
||||
var None Set
|
||||
|
||||
type setExtreme bool
|
||||
|
||||
func (s setExtreme) Has(v Version) bool {
|
||||
return bool(s)
|
||||
}
|
||||
|
||||
func (s setExtreme) AllRequested() Set {
|
||||
// The extreme sets request nothing.
|
||||
return None
|
||||
}
|
||||
|
||||
func (s setExtreme) GoString() string {
|
||||
switch bool(s) {
|
||||
case true:
|
||||
return "versions.All"
|
||||
case false:
|
||||
return "versions.None"
|
||||
default:
|
||||
panic("strange new boolean value")
|
||||
}
|
||||
}
|
||||
|
||||
var _ setFinite = setExtreme(false)
|
||||
|
||||
func (s setExtreme) isFinite() bool {
|
||||
// Only None is finite
|
||||
return !bool(s)
|
||||
}
|
||||
|
||||
func (s setExtreme) listVersions() List {
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
All = Set{
|
||||
setI: setExtreme(true),
|
||||
}
|
||||
None = Set{
|
||||
setI: setExtreme(false),
|
||||
}
|
||||
}
|
34
vendor/github.com/apparentlymart/go-versions/versions/set_finite.go
generated
vendored
Normal file
34
vendor/github.com/apparentlymart/go-versions/versions/set_finite.go
generated
vendored
Normal file
|
@ -0,0 +1,34 @@
|
|||
package versions
|
||||
|
||||
// setFinite is the interface implemented by set implementations that
|
||||
// represent a finite number of versions, and can thus list those versions.
|
||||
type setFinite interface {
|
||||
isFinite() bool
|
||||
listVersions() List
|
||||
}
|
||||
|
||||
// IsFinite returns true if the set represents a finite number of versions,
|
||||
// and can thus support List without panicking.
|
||||
func (s Set) IsFinite() bool {
|
||||
return isFinite(s.setI)
|
||||
}
|
||||
|
||||
// List returns the specific versions represented by a finite list, in an
|
||||
// undefined order. If desired, the caller can sort the resulting list
|
||||
// using its Sort method.
|
||||
//
|
||||
// If the set is not finite, this method will panic. Use IsFinite to check
|
||||
// unless a finite set was guaranteed by whatever operation(s) constructed
|
||||
// the set.
|
||||
func (s Set) List() List {
|
||||
finite, ok := s.setI.(setFinite)
|
||||
if !ok || !finite.isFinite() {
|
||||
panic("List called on infinite set")
|
||||
}
|
||||
return finite.listVersions()
|
||||
}
|
||||
|
||||
func isFinite(s setI) bool {
|
||||
finite, ok := s.(setFinite)
|
||||
return ok && finite.isFinite()
|
||||
}
|
132
vendor/github.com/apparentlymart/go-versions/versions/set_intersection.go
generated
vendored
Normal file
132
vendor/github.com/apparentlymart/go-versions/versions/set_intersection.go
generated
vendored
Normal file
|
@ -0,0 +1,132 @@
|
|||
package versions
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type setIntersection []setI
|
||||
|
||||
func (s setIntersection) Has(v Version) bool {
|
||||
if len(s) == 0 {
|
||||
// Weird to have an intersection with no elements, but we'll
|
||||
// allow it and return something sensible.
|
||||
return false
|
||||
}
|
||||
for _, ss := range s {
|
||||
if !ss.Has(v) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (s setIntersection) AllRequested() Set {
|
||||
// The requested set for an intersection is the union of all of its
|
||||
// members requested sets intersection the receiver. Therefore we'll
|
||||
// borrow the same logic from setUnion's implementation here but
|
||||
// then wrap it up in a setIntersection before we return.
|
||||
|
||||
asUnion := setUnion(s)
|
||||
ar := asUnion.AllRequested()
|
||||
si := make(setIntersection, len(s)+1)
|
||||
si[0] = ar.setI
|
||||
copy(si[1:], s)
|
||||
return Set{setI: si}
|
||||
}
|
||||
|
||||
func (s setIntersection) GoString() string {
|
||||
var buf bytes.Buffer
|
||||
fmt.Fprint(&buf, "versions.Intersection(")
|
||||
for i, ss := range s {
|
||||
if i == 0 {
|
||||
fmt.Fprint(&buf, ss.GoString())
|
||||
} else {
|
||||
fmt.Fprintf(&buf, ", %#v", ss)
|
||||
}
|
||||
}
|
||||
fmt.Fprint(&buf, ")")
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// Intersection creates a new set that contains the versions that all of the
|
||||
// given sets have in common.
|
||||
//
|
||||
// The result is finite if any of the given sets are finite.
|
||||
func Intersection(sets ...Set) Set {
|
||||
if len(sets) == 0 {
|
||||
return None
|
||||
}
|
||||
|
||||
r := make(setIntersection, 0, len(sets))
|
||||
for _, set := range sets {
|
||||
if set == All {
|
||||
continue
|
||||
}
|
||||
if set == None {
|
||||
return None
|
||||
}
|
||||
if su, ok := set.setI.(setIntersection); ok {
|
||||
r = append(r, su...)
|
||||
} else {
|
||||
r = append(r, set.setI)
|
||||
}
|
||||
}
|
||||
if len(r) == 1 {
|
||||
return Set{setI: r[0]}
|
||||
}
|
||||
return Set{setI: r}
|
||||
}
|
||||
|
||||
// Intersection returns a new set that contains all of the versions that
|
||||
// the receiver and the given sets have in common.
|
||||
//
|
||||
// The result is a finite set if the receiver or any of the given sets are
|
||||
// finite.
|
||||
func (s Set) Intersection(others ...Set) Set {
|
||||
r := make(setIntersection, 1, len(others)+1)
|
||||
r[0] = s.setI
|
||||
for _, ss := range others {
|
||||
if ss == All {
|
||||
continue
|
||||
}
|
||||
if ss == None {
|
||||
return None
|
||||
}
|
||||
if su, ok := ss.setI.(setIntersection); ok {
|
||||
r = append(r, su...)
|
||||
} else {
|
||||
r = append(r, ss.setI)
|
||||
}
|
||||
}
|
||||
if len(r) == 1 {
|
||||
return Set{setI: r[0]}
|
||||
}
|
||||
return Set{setI: r}
|
||||
}
|
||||
|
||||
var _ setFinite = setIntersection{}
|
||||
|
||||
func (s setIntersection) isFinite() bool {
|
||||
// intersection is finite if any of its members are, or if it is empty
|
||||
if len(s) == 0 {
|
||||
return true
|
||||
}
|
||||
for _, ss := range s {
|
||||
if isFinite(ss) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (s setIntersection) listVersions() List {
|
||||
var ret List
|
||||
for _, ss := range s {
|
||||
if isFinite(ss) {
|
||||
ret = append(ret, ss.(setFinite).listVersions()...)
|
||||
}
|
||||
}
|
||||
ret.Filter(Set{setI: s})
|
||||
return ret
|
||||
}
|
30
vendor/github.com/apparentlymart/go-versions/versions/set_released.go
generated
vendored
Normal file
30
vendor/github.com/apparentlymart/go-versions/versions/set_released.go
generated
vendored
Normal file
|
@ -0,0 +1,30 @@
|
|||
package versions
|
||||
|
||||
type setReleased struct{}
|
||||
|
||||
func (s setReleased) Has(v Version) bool {
|
||||
return v.Prerelease == ""
|
||||
}
|
||||
|
||||
func (s setReleased) AllRequested() Set {
|
||||
// The set of all released versions requests nothing.
|
||||
return None
|
||||
}
|
||||
|
||||
func (s setReleased) GoString() string {
|
||||
return "versions.Released"
|
||||
}
|
||||
|
||||
// Released is a set containing all versions that have an empty prerelease
|
||||
// string.
|
||||
var Released Set
|
||||
|
||||
// Prerelease is a set containing all versions that have a prerelease marker.
|
||||
// This is the complement of Released, or in other words it is
|
||||
// All.Subtract(Released).
|
||||
var Prerelease Set
|
||||
|
||||
func init() {
|
||||
Released = Set{setI: setReleased{}}
|
||||
Prerelease = All.Subtract(Released)
|
||||
}
|
56
vendor/github.com/apparentlymart/go-versions/versions/set_subtract.go
generated
vendored
Normal file
56
vendor/github.com/apparentlymart/go-versions/versions/set_subtract.go
generated
vendored
Normal file
|
@ -0,0 +1,56 @@
|
|||
package versions
|
||||
|
||||
import "fmt"
|
||||
|
||||
type setSubtract struct {
|
||||
from setI
|
||||
sub setI
|
||||
}
|
||||
|
||||
func (s setSubtract) Has(v Version) bool {
|
||||
return s.from.Has(v) && !s.sub.Has(v)
|
||||
}
|
||||
|
||||
func (s setSubtract) AllRequested() Set {
|
||||
// Our set requests anything that is requested by "from", unless it'd
|
||||
// be excluded by "sub". Notice that the whole of "sub" is used, rather
|
||||
// than just the requested parts, because requesting is a positive
|
||||
// action only.
|
||||
return Set{setI: s.from}.AllRequested().Subtract(Set{setI: s.sub})
|
||||
}
|
||||
|
||||
func (s setSubtract) GoString() string {
|
||||
return fmt.Sprintf("(%#v).Subtract(%#v)", s.from, s.sub)
|
||||
}
|
||||
|
||||
// Subtract returns a new set that has all of the versions from the receiver
|
||||
// except for any versions in the other given set.
|
||||
//
|
||||
// If the receiver is finite then the returned set is also finite.
|
||||
func (s Set) Subtract(other Set) Set {
|
||||
if other == None || s == None {
|
||||
return s
|
||||
}
|
||||
if other == All {
|
||||
return None
|
||||
}
|
||||
return Set{
|
||||
setI: setSubtract{
|
||||
from: s.setI,
|
||||
sub: other.setI,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
var _ setFinite = setSubtract{}
|
||||
|
||||
func (s setSubtract) isFinite() bool {
|
||||
// subtract is finite if its "from" is finite
|
||||
return isFinite(s.from)
|
||||
}
|
||||
|
||||
func (s setSubtract) listVersions() List {
|
||||
ret := s.from.(setFinite).listVersions()
|
||||
ret = ret.Filter(Set{setI: s.sub})
|
||||
return ret
|
||||
}
|
121
vendor/github.com/apparentlymart/go-versions/versions/set_union.go
generated
vendored
Normal file
121
vendor/github.com/apparentlymart/go-versions/versions/set_union.go
generated
vendored
Normal file
|
@ -0,0 +1,121 @@
|
|||
package versions
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type setUnion []setI
|
||||
|
||||
func (s setUnion) Has(v Version) bool {
|
||||
for _, ss := range s {
|
||||
if ss.Has(v) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (s setUnion) AllRequested() Set {
|
||||
// Since a union includes everything from its members, it includes all
|
||||
// of the requested versions from its members too.
|
||||
if len(s) == 0 {
|
||||
return None
|
||||
}
|
||||
si := make(setUnion, 0, len(s))
|
||||
for _, ss := range s {
|
||||
ar := ss.AllRequested()
|
||||
if ar == None {
|
||||
continue
|
||||
}
|
||||
si = append(si, ar.setI)
|
||||
}
|
||||
if len(si) == 1 {
|
||||
return Set{setI: si[0]}
|
||||
}
|
||||
return Set{setI: si}
|
||||
}
|
||||
|
||||
func (s setUnion) GoString() string {
|
||||
var buf bytes.Buffer
|
||||
fmt.Fprint(&buf, "versions.Union(")
|
||||
for i, ss := range s {
|
||||
if i == 0 {
|
||||
fmt.Fprint(&buf, ss.GoString())
|
||||
} else {
|
||||
fmt.Fprintf(&buf, ", %#v", ss)
|
||||
}
|
||||
}
|
||||
fmt.Fprint(&buf, ")")
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// Union creates a new set that contains all of the given versions.
|
||||
//
|
||||
// The result is finite only if the receiver and all of the other given sets
|
||||
// are finite.
|
||||
func Union(sets ...Set) Set {
|
||||
if len(sets) == 0 {
|
||||
return None
|
||||
}
|
||||
|
||||
r := make(setUnion, 0, len(sets))
|
||||
for _, set := range sets {
|
||||
if set == None {
|
||||
continue
|
||||
}
|
||||
if su, ok := set.setI.(setUnion); ok {
|
||||
r = append(r, su...)
|
||||
} else {
|
||||
r = append(r, set.setI)
|
||||
}
|
||||
}
|
||||
if len(r) == 1 {
|
||||
return Set{setI: r[0]}
|
||||
}
|
||||
return Set{setI: r}
|
||||
}
|
||||
|
||||
// Union returns a new set that contains all of the versions from the
|
||||
// receiver and all of the versions from each of the other given sets.
|
||||
//
|
||||
// The result is finite only if the receiver and all of the other given sets
|
||||
// are finite.
|
||||
func (s Set) Union(others ...Set) Set {
|
||||
r := make(setUnion, 1, len(others)+1)
|
||||
r[0] = s.setI
|
||||
for _, ss := range others {
|
||||
if ss == None {
|
||||
continue
|
||||
}
|
||||
if su, ok := ss.setI.(setUnion); ok {
|
||||
r = append(r, su...)
|
||||
} else {
|
||||
r = append(r, ss.setI)
|
||||
}
|
||||
}
|
||||
if len(r) == 1 {
|
||||
return Set{setI: r[0]}
|
||||
}
|
||||
return Set{setI: r}
|
||||
}
|
||||
|
||||
var _ setFinite = setUnion{}
|
||||
|
||||
func (s setUnion) isFinite() bool {
|
||||
// union is finite only if all of its members are finite
|
||||
for _, ss := range s {
|
||||
if !isFinite(ss) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (s setUnion) listVersions() List {
|
||||
var ret List
|
||||
for _, ss := range s {
|
||||
ret = append(ret, ss.(setFinite).listVersions()...)
|
||||
}
|
||||
return ret
|
||||
}
|
|
@ -0,0 +1,222 @@
|
|||
package versions
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Version represents a single version.
|
||||
type Version struct {
|
||||
Major uint64
|
||||
Minor uint64
|
||||
Patch uint64
|
||||
Prerelease VersionExtra
|
||||
Metadata VersionExtra
|
||||
}
|
||||
|
||||
// Unspecified is the zero value of Version and represents the absense of a
|
||||
// version number.
|
||||
//
|
||||
// Note that this is indistinguishable from the explicit version that
|
||||
// results from parsing the string "0.0.0".
|
||||
var Unspecified Version
|
||||
|
||||
// Same returns true if the receiver has the same precedence as the other
|
||||
// given version. In other words, it has the same major, minor and patch
|
||||
// version number and an identical prerelease portion. The Metadata, if
|
||||
// any, is not considered.
|
||||
func (v Version) Same(other Version) bool {
|
||||
return (v.Major == other.Major &&
|
||||
v.Minor == other.Minor &&
|
||||
v.Patch == other.Patch &&
|
||||
v.Prerelease == other.Prerelease)
|
||||
}
|
||||
|
||||
// Comparable returns a version that is the same as the receiver but its
|
||||
// metadata is the empty string. For Comparable versions, the standard
|
||||
// equality operator == is equivalent to method Same.
|
||||
func (v Version) Comparable() Version {
|
||||
v.Metadata = ""
|
||||
return v
|
||||
}
|
||||
|
||||
// String is an implementation of fmt.Stringer that returns the receiver
|
||||
// in the canonical "semver" format.
|
||||
func (v Version) String() string {
|
||||
s := fmt.Sprintf("%d.%d.%d", v.Major, v.Minor, v.Patch)
|
||||
if v.Prerelease != "" {
|
||||
s = fmt.Sprintf("%s-%s", s, v.Prerelease)
|
||||
}
|
||||
if v.Metadata != "" {
|
||||
s = fmt.Sprintf("%s+%s", s, v.Metadata)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func (v Version) GoString() string {
|
||||
return fmt.Sprintf("versions.MustParseVersion(%q)", v.String())
|
||||
}
|
||||
|
||||
// LessThan returns true if the receiver has a lower precedence than the
|
||||
// other given version, as defined by the semantic versioning specification.
|
||||
func (v Version) LessThan(other Version) bool {
|
||||
switch {
|
||||
case v.Major != other.Major:
|
||||
return v.Major < other.Major
|
||||
case v.Minor != other.Minor:
|
||||
return v.Minor < other.Minor
|
||||
case v.Patch != other.Patch:
|
||||
return v.Patch < other.Patch
|
||||
case v.Prerelease != other.Prerelease:
|
||||
if v.Prerelease == "" {
|
||||
return false
|
||||
}
|
||||
if other.Prerelease == "" {
|
||||
return true
|
||||
}
|
||||
return v.Prerelease.LessThan(other.Prerelease)
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// GreaterThan returns true if the receiver has a higher precedence than the
|
||||
// other given version, as defined by the semantic versioning specification.
|
||||
func (v Version) GreaterThan(other Version) bool {
|
||||
switch {
|
||||
case v.Major != other.Major:
|
||||
return v.Major > other.Major
|
||||
case v.Minor != other.Minor:
|
||||
return v.Minor > other.Minor
|
||||
case v.Patch != other.Patch:
|
||||
return v.Patch > other.Patch
|
||||
case v.Prerelease != other.Prerelease:
|
||||
if v.Prerelease == "" {
|
||||
return true
|
||||
}
|
||||
if other.Prerelease == "" {
|
||||
return false
|
||||
}
|
||||
return !v.Prerelease.LessThan(other.Prerelease)
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// MarshalText is an implementation of encoding.TextMarshaler, allowing versions
|
||||
// to be automatically marshalled for text-based serialization formats,
|
||||
// including encoding/json.
|
||||
//
|
||||
// The format used is that returned by String, which can be parsed using
|
||||
// ParseVersion.
|
||||
func (v Version) MarshalText() (text []byte, err error) {
|
||||
return []byte(v.String()), nil
|
||||
}
|
||||
|
||||
// UnmarshalText is an implementation of encoding.TextUnmarshaler, allowing
|
||||
// versions to be automatically unmarshalled from strings in text-based
|
||||
// serialization formats, including encoding/json.
|
||||
//
|
||||
// The format expected is what is accepted by ParseVersion. Any parser errors
|
||||
// are passed on verbatim to the caller.
|
||||
func (v *Version) UnmarshalText(text []byte) error {
|
||||
str := string(text)
|
||||
new, err := ParseVersion(str)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*v = new
|
||||
return nil
|
||||
}
|
||||
|
||||
// VersionExtra represents a string containing dot-delimited tokens, as used
|
||||
// in the pre-release and build metadata portions of a Semantic Versioning
|
||||
// version expression.
|
||||
type VersionExtra string
|
||||
|
||||
// Parts tokenizes the string into its separate parts by splitting on dots.
|
||||
//
|
||||
// The result is undefined if the receiver is not valid per the semver spec,
|
||||
func (e VersionExtra) Parts() []string {
|
||||
return strings.Split(string(e), ".")
|
||||
}
|
||||
|
||||
func (e VersionExtra) Raw() string {
|
||||
return string(e)
|
||||
}
|
||||
|
||||
// LessThan returns true if the receiever has lower precedence than the
|
||||
// other given VersionExtra string, per the rules defined in the semver
|
||||
// spec for pre-release versions.
|
||||
//
|
||||
// Build metadata has no defined precedence rules, so it is not meaningful
|
||||
// to call this method on a VersionExtra representing build metadata.
|
||||
func (e VersionExtra) LessThan(other VersionExtra) bool {
|
||||
if e == other {
|
||||
// Easy path
|
||||
return false
|
||||
}
|
||||
|
||||
s1 := string(e)
|
||||
s2 := string(other)
|
||||
for {
|
||||
d1 := strings.IndexByte(s1, '.')
|
||||
d2 := strings.IndexByte(s2, '.')
|
||||
|
||||
switch {
|
||||
case d1 == -1 && d2 != -1:
|
||||
// s1 has fewer parts, so it precedes s2
|
||||
return true
|
||||
case d2 == -1 && d1 != -1:
|
||||
// s1 has more parts, so it succeeds s2
|
||||
return false
|
||||
case d1 == -1: // d2 must be -1 too, because of the above
|
||||
// this is our last portion to compare
|
||||
return lessThanStr(s1, s2)
|
||||
default:
|
||||
s1s := s1[:d1]
|
||||
s2s := s2[:d2]
|
||||
if s1s != s2s {
|
||||
return lessThanStr(s1s, s2s)
|
||||
}
|
||||
s1 = s1[d1+1:]
|
||||
s2 = s2[d2+1:]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func lessThanStr(s1, s2 string) bool {
|
||||
// How we compare here depends on whether the string is entirely consistent of digits
|
||||
s1Numeric := true
|
||||
s2Numeric := true
|
||||
for _, c := range s1 {
|
||||
if c < '0' || c > '9' {
|
||||
s1Numeric = false
|
||||
break
|
||||
}
|
||||
}
|
||||
for _, c := range s2 {
|
||||
if c < '0' || c > '9' {
|
||||
s2Numeric = false
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
switch {
|
||||
case s1Numeric && !s2Numeric:
|
||||
return true
|
||||
case s2Numeric && !s1Numeric:
|
||||
return false
|
||||
case s1Numeric: // s2Numeric must also be true
|
||||
switch {
|
||||
case len(s1) < len(s2):
|
||||
return true
|
||||
case len(s2) < len(s1):
|
||||
return false
|
||||
default:
|
||||
return s1 < s2
|
||||
}
|
||||
default:
|
||||
return s1 < s2
|
||||
}
|
||||
}
|
|
@ -84,6 +84,9 @@ github.com/apparentlymart/go-cidr/cidr
|
|||
github.com/apparentlymart/go-dump/dump
|
||||
# github.com/apparentlymart/go-textseg v1.0.0
|
||||
github.com/apparentlymart/go-textseg/textseg
|
||||
# github.com/apparentlymart/go-versions v0.0.2-0.20180815153302-64b99f7cb171
|
||||
github.com/apparentlymart/go-versions/versions
|
||||
github.com/apparentlymart/go-versions/versions/constraints
|
||||
# github.com/armon/circbuf v0.0.0-20190214190532-5111143e8da2
|
||||
github.com/armon/circbuf
|
||||
# github.com/armon/go-radix v1.0.0
|
||||
|
|
Loading…
Reference in New Issue