From eda57670ce02e3d5a4b85b88a7b0bccaf83300db Mon Sep 17 00:00:00 2001 From: Martin Atkins Date: Thu, 19 Dec 2019 14:57:33 -0800 Subject: [PATCH] go get github.com/apparentlymart/go-versions@master --- go.mod | 1 + go.sum | 3 + .../apparentlymart/go-versions/LICENSE | 21 + .../versions/constraints/canon_style.go | 352 ++++++++++ .../constraints/constraintdepth_string.go | 16 + .../go-versions/versions/constraints/doc.go | 13 + .../go-versions/versions/constraints/raw.go | 74 +++ .../versions/constraints/raw_scan.go | 623 ++++++++++++++++++ .../versions/constraints/raw_scan.rl | 95 +++ .../versions/constraints/ruby_style.go | 181 +++++ .../constraints/selectionop_string.go | 43 ++ .../go-versions/versions/constraints/spec.go | 249 +++++++ .../versions/constraints/version.go | 81 +++ .../go-versions/versions/doc.go | 14 + .../go-versions/versions/list.go | 149 +++++ .../go-versions/versions/parse.go | 243 +++++++ .../go-versions/versions/set.go | 89 +++ .../go-versions/versions/set_bound.go | 98 +++ .../go-versions/versions/set_exact.go | 103 +++ .../go-versions/versions/set_extremes.go | 49 ++ .../go-versions/versions/set_finite.go | 34 + .../go-versions/versions/set_intersection.go | 132 ++++ .../go-versions/versions/set_released.go | 30 + .../go-versions/versions/set_subtract.go | 56 ++ .../go-versions/versions/set_union.go | 121 ++++ .../go-versions/versions/version.go | 222 +++++++ vendor/modules.txt | 3 + 27 files changed, 3095 insertions(+) create mode 100644 vendor/github.com/apparentlymart/go-versions/LICENSE create mode 100644 vendor/github.com/apparentlymart/go-versions/versions/constraints/canon_style.go create mode 100644 vendor/github.com/apparentlymart/go-versions/versions/constraints/constraintdepth_string.go create mode 100644 vendor/github.com/apparentlymart/go-versions/versions/constraints/doc.go create mode 100644 vendor/github.com/apparentlymart/go-versions/versions/constraints/raw.go create mode 100644 vendor/github.com/apparentlymart/go-versions/versions/constraints/raw_scan.go create mode 100644 vendor/github.com/apparentlymart/go-versions/versions/constraints/raw_scan.rl create mode 100644 vendor/github.com/apparentlymart/go-versions/versions/constraints/ruby_style.go create mode 100644 vendor/github.com/apparentlymart/go-versions/versions/constraints/selectionop_string.go create mode 100644 vendor/github.com/apparentlymart/go-versions/versions/constraints/spec.go create mode 100644 vendor/github.com/apparentlymart/go-versions/versions/constraints/version.go create mode 100644 vendor/github.com/apparentlymart/go-versions/versions/doc.go create mode 100644 vendor/github.com/apparentlymart/go-versions/versions/list.go create mode 100644 vendor/github.com/apparentlymart/go-versions/versions/parse.go create mode 100644 vendor/github.com/apparentlymart/go-versions/versions/set.go create mode 100644 vendor/github.com/apparentlymart/go-versions/versions/set_bound.go create mode 100644 vendor/github.com/apparentlymart/go-versions/versions/set_exact.go create mode 100644 vendor/github.com/apparentlymart/go-versions/versions/set_extremes.go create mode 100644 vendor/github.com/apparentlymart/go-versions/versions/set_finite.go create mode 100644 vendor/github.com/apparentlymart/go-versions/versions/set_intersection.go create mode 100644 vendor/github.com/apparentlymart/go-versions/versions/set_released.go create mode 100644 vendor/github.com/apparentlymart/go-versions/versions/set_subtract.go create mode 100644 vendor/github.com/apparentlymart/go-versions/versions/set_union.go create mode 100644 vendor/github.com/apparentlymart/go-versions/versions/version.go diff --git a/go.mod b/go.mod index 7b88bd23a..5d1d904e1 100644 --- a/go.mod +++ b/go.mod @@ -13,6 +13,7 @@ require ( github.com/aliyun/aliyun-tablestore-go-sdk v4.1.2+incompatible github.com/apparentlymart/go-cidr v1.0.1 github.com/apparentlymart/go-dump v0.0.0-20190214190832-042adf3cf4a0 + github.com/apparentlymart/go-versions v0.0.2-0.20180815153302-64b99f7cb171 github.com/armon/circbuf v0.0.0-20190214190532-5111143e8da2 github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da // indirect github.com/armon/go-radix v1.0.0 // indirect diff --git a/go.sum b/go.sum index ea3e00856..25890a318 100644 --- a/go.sum +++ b/go.sum @@ -69,6 +69,8 @@ github.com/apparentlymart/go-dump v0.0.0-20190214190832-042adf3cf4a0 h1:MzVXffFU github.com/apparentlymart/go-dump v0.0.0-20190214190832-042adf3cf4a0/go.mod h1:oL81AME2rN47vu18xqj1S1jPIPuN7afo62yKTNn3XMM= github.com/apparentlymart/go-textseg v1.0.0 h1:rRmlIsPEEhUTIKQb7T++Nz/A5Q6C9IuX2wFoYVvnCs0= github.com/apparentlymart/go-textseg v1.0.0/go.mod h1:z96Txxhf3xSFMPmb5X/1W05FF/Nj9VFpLOpjS5yuumk= +github.com/apparentlymart/go-versions v0.0.2-0.20180815153302-64b99f7cb171 h1:19Seu/H5gq3Ugtx+CGenwF89SDG3S1REX5i6PJj3RK4= +github.com/apparentlymart/go-versions v0.0.2-0.20180815153302-64b99f7cb171/go.mod h1:JXY95WvQrPJQtudvNARshgWajS7jNNlM90altXIPNyI= github.com/armon/circbuf v0.0.0-20190214190532-5111143e8da2 h1:7Ip0wMmLHLRJdrloDxZfhMm0xrLXZS8+COSu2bXmEQs= github.com/armon/circbuf v0.0.0-20190214190532-5111143e8da2/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da h1:8GUt8eRujhVEGZFFEjBj46YV4rDjvGrNxb0KMWYkL2I= @@ -131,6 +133,7 @@ github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeME github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-test/deep v1.0.1/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68= github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= diff --git a/vendor/github.com/apparentlymart/go-versions/LICENSE b/vendor/github.com/apparentlymart/go-versions/LICENSE new file mode 100644 index 000000000..83fe416ba --- /dev/null +++ b/vendor/github.com/apparentlymart/go-versions/LICENSE @@ -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. diff --git a/vendor/github.com/apparentlymart/go-versions/versions/constraints/canon_style.go b/vendor/github.com/apparentlymart/go-versions/versions/constraints/canon_style.go new file mode 100644 index 000000000..aa5e87cb7 --- /dev/null +++ b/vendor/github.com/apparentlymart/go-versions/versions/constraints/canon_style.go @@ -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 +} diff --git a/vendor/github.com/apparentlymart/go-versions/versions/constraints/constraintdepth_string.go b/vendor/github.com/apparentlymart/go-versions/versions/constraints/constraintdepth_string.go new file mode 100644 index 000000000..0f808f912 --- /dev/null +++ b/vendor/github.com/apparentlymart/go-versions/versions/constraints/constraintdepth_string.go @@ -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]] +} diff --git a/vendor/github.com/apparentlymart/go-versions/versions/constraints/doc.go b/vendor/github.com/apparentlymart/go-versions/versions/constraints/doc.go new file mode 100644 index 000000000..17b8b90f2 --- /dev/null +++ b/vendor/github.com/apparentlymart/go-versions/versions/constraints/doc.go @@ -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 diff --git a/vendor/github.com/apparentlymart/go-versions/versions/constraints/raw.go b/vendor/github.com/apparentlymart/go-versions/versions/constraints/raw.go new file mode 100644 index 000000000..fdea80a35 --- /dev/null +++ b/vendor/github.com/apparentlymart/go-versions/versions/constraints/raw.go @@ -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), + } + } +} diff --git a/vendor/github.com/apparentlymart/go-versions/versions/constraints/raw_scan.go b/vendor/github.com/apparentlymart/go-versions/versions/constraints/raw_scan.go new file mode 100644 index 000000000..2f7a81c47 --- /dev/null +++ b/vendor/github.com/apparentlymart/go-versions/versions/constraints/raw_scan.go @@ -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 +} diff --git a/vendor/github.com/apparentlymart/go-versions/versions/constraints/raw_scan.rl b/vendor/github.com/apparentlymart/go-versions/versions/constraints/raw_scan.rl new file mode 100644 index 000000000..da2151da9 --- /dev/null +++ b/vendor/github.com/apparentlymart/go-versions/versions/constraints/raw_scan.rl @@ -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 +} diff --git a/vendor/github.com/apparentlymart/go-versions/versions/constraints/ruby_style.go b/vendor/github.com/apparentlymart/go-versions/versions/constraints/ruby_style.go new file mode 100644 index 000000000..59b2ecd34 --- /dev/null +++ b/vendor/github.com/apparentlymart/go-versions/versions/constraints/ruby_style.go @@ -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 +} diff --git a/vendor/github.com/apparentlymart/go-versions/versions/constraints/selectionop_string.go b/vendor/github.com/apparentlymart/go-versions/versions/constraints/selectionop_string.go new file mode 100644 index 000000000..e3c2b129c --- /dev/null +++ b/vendor/github.com/apparentlymart/go-versions/versions/constraints/selectionop_string.go @@ -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) + ")" + } +} diff --git a/vendor/github.com/apparentlymart/go-versions/versions/constraints/spec.go b/vendor/github.com/apparentlymart/go-versions/versions/constraints/spec.go new file mode 100644 index 000000000..2cce04555 --- /dev/null +++ b/vendor/github.com/apparentlymart/go-versions/versions/constraints/spec.go @@ -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 +) diff --git a/vendor/github.com/apparentlymart/go-versions/versions/constraints/version.go b/vendor/github.com/apparentlymart/go-versions/versions/constraints/version.go new file mode 100644 index 000000000..9e6f24ea7 --- /dev/null +++ b/vendor/github.com/apparentlymart/go-versions/versions/constraints/version.go @@ -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 +} diff --git a/vendor/github.com/apparentlymart/go-versions/versions/doc.go b/vendor/github.com/apparentlymart/go-versions/versions/doc.go new file mode 100644 index 000000000..6e4ffe520 --- /dev/null +++ b/vendor/github.com/apparentlymart/go-versions/versions/doc.go @@ -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 diff --git a/vendor/github.com/apparentlymart/go-versions/versions/list.go b/vendor/github.com/apparentlymart/go-versions/versions/list.go new file mode 100644 index 000000000..083e68589 --- /dev/null +++ b/vendor/github.com/apparentlymart/go-versions/versions/list.go @@ -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) +} diff --git a/vendor/github.com/apparentlymart/go-versions/versions/parse.go b/vendor/github.com/apparentlymart/go-versions/versions/parse.go new file mode 100644 index 000000000..66150e337 --- /dev/null +++ b/vendor/github.com/apparentlymart/go-versions/versions/parse.go @@ -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), + } +} diff --git a/vendor/github.com/apparentlymart/go-versions/versions/set.go b/vendor/github.com/apparentlymart/go-versions/versions/set.go new file mode 100644 index 000000000..ad4d5efe5 --- /dev/null +++ b/vendor/github.com/apparentlymart/go-versions/versions/set.go @@ -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")) diff --git a/vendor/github.com/apparentlymart/go-versions/versions/set_bound.go b/vendor/github.com/apparentlymart/go-versions/versions/set_bound.go new file mode 100644 index 000000000..2e2ba09ce --- /dev/null +++ b/vendor/github.com/apparentlymart/go-versions/versions/set_bound.go @@ -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 = '≤' diff --git a/vendor/github.com/apparentlymart/go-versions/versions/set_exact.go b/vendor/github.com/apparentlymart/go-versions/versions/set_exact.go new file mode 100644 index 000000000..4b2fa7bc4 --- /dev/null +++ b/vendor/github.com/apparentlymart/go-versions/versions/set_exact.go @@ -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 +} diff --git a/vendor/github.com/apparentlymart/go-versions/versions/set_extremes.go b/vendor/github.com/apparentlymart/go-versions/versions/set_extremes.go new file mode 100644 index 000000000..cae13f99b --- /dev/null +++ b/vendor/github.com/apparentlymart/go-versions/versions/set_extremes.go @@ -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), + } +} diff --git a/vendor/github.com/apparentlymart/go-versions/versions/set_finite.go b/vendor/github.com/apparentlymart/go-versions/versions/set_finite.go new file mode 100644 index 000000000..eb1a5dc2f --- /dev/null +++ b/vendor/github.com/apparentlymart/go-versions/versions/set_finite.go @@ -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() +} diff --git a/vendor/github.com/apparentlymart/go-versions/versions/set_intersection.go b/vendor/github.com/apparentlymart/go-versions/versions/set_intersection.go new file mode 100644 index 000000000..4afd1b423 --- /dev/null +++ b/vendor/github.com/apparentlymart/go-versions/versions/set_intersection.go @@ -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 +} diff --git a/vendor/github.com/apparentlymart/go-versions/versions/set_released.go b/vendor/github.com/apparentlymart/go-versions/versions/set_released.go new file mode 100644 index 000000000..dea240363 --- /dev/null +++ b/vendor/github.com/apparentlymart/go-versions/versions/set_released.go @@ -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) +} diff --git a/vendor/github.com/apparentlymart/go-versions/versions/set_subtract.go b/vendor/github.com/apparentlymart/go-versions/versions/set_subtract.go new file mode 100644 index 000000000..19a9c01e2 --- /dev/null +++ b/vendor/github.com/apparentlymart/go-versions/versions/set_subtract.go @@ -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 +} diff --git a/vendor/github.com/apparentlymart/go-versions/versions/set_union.go b/vendor/github.com/apparentlymart/go-versions/versions/set_union.go new file mode 100644 index 000000000..1482f690f --- /dev/null +++ b/vendor/github.com/apparentlymart/go-versions/versions/set_union.go @@ -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 +} diff --git a/vendor/github.com/apparentlymart/go-versions/versions/version.go b/vendor/github.com/apparentlymart/go-versions/versions/version.go new file mode 100644 index 000000000..8cd0eb5a0 --- /dev/null +++ b/vendor/github.com/apparentlymart/go-versions/versions/version.go @@ -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 + } +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 438b9f912..a3f94c63f 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -84,6 +84,9 @@ github.com/apparentlymart/go-cidr/cidr github.com/apparentlymart/go-dump/dump # github.com/apparentlymart/go-textseg v1.0.0 github.com/apparentlymart/go-textseg/textseg +# github.com/apparentlymart/go-versions v0.0.2-0.20180815153302-64b99f7cb171 +github.com/apparentlymart/go-versions/versions +github.com/apparentlymart/go-versions/versions/constraints # github.com/armon/circbuf v0.0.0-20190214190532-5111143e8da2 github.com/armon/circbuf # github.com/armon/go-radix v1.0.0