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/aliyun/aliyun-tablestore-go-sdk v4.1.2+incompatible
|
||||||
github.com/apparentlymart/go-cidr v1.0.1
|
github.com/apparentlymart/go-cidr v1.0.1
|
||||||
github.com/apparentlymart/go-dump v0.0.0-20190214190832-042adf3cf4a0
|
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/circbuf v0.0.0-20190214190532-5111143e8da2
|
||||||
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da // indirect
|
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da // indirect
|
||||||
github.com/armon/go-radix v1.0.0 // 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-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 h1:rRmlIsPEEhUTIKQb7T++Nz/A5Q6C9IuX2wFoYVvnCs0=
|
||||||
github.com/apparentlymart/go-textseg v1.0.0/go.mod h1:z96Txxhf3xSFMPmb5X/1W05FF/Nj9VFpLOpjS5yuumk=
|
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 h1:7Ip0wMmLHLRJdrloDxZfhMm0xrLXZS8+COSu2bXmEQs=
|
||||||
github.com/armon/circbuf v0.0.0-20190214190532-5111143e8da2/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
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=
|
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-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-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-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 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68=
|
||||||
github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
|
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=
|
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-dump/dump
|
||||||
# github.com/apparentlymart/go-textseg v1.0.0
|
# github.com/apparentlymart/go-textseg v1.0.0
|
||||||
github.com/apparentlymart/go-textseg/textseg
|
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 v0.0.0-20190214190532-5111143e8da2
|
||||||
github.com/armon/circbuf
|
github.com/armon/circbuf
|
||||||
# github.com/armon/go-radix v1.0.0
|
# github.com/armon/go-radix v1.0.0
|
||||||
|
|
Loading…
Reference in New Issue