OSDN Git Service

Hulk did something
[bytom/vapor.git] / vendor / github.com / hashicorp / go-version / constraint.go
diff --git a/vendor/github.com/hashicorp/go-version/constraint.go b/vendor/github.com/hashicorp/go-version/constraint.go
new file mode 100644 (file)
index 0000000..d055759
--- /dev/null
@@ -0,0 +1,204 @@
+package version
+
+import (
+       "fmt"
+       "reflect"
+       "regexp"
+       "strings"
+)
+
+// Constraint represents a single constraint for a version, such as
+// ">= 1.0".
+type Constraint struct {
+       f        constraintFunc
+       check    *Version
+       original string
+}
+
+// Constraints is a slice of constraints. We make a custom type so that
+// we can add methods to it.
+type Constraints []*Constraint
+
+type constraintFunc func(v, c *Version) bool
+
+var constraintOperators map[string]constraintFunc
+
+var constraintRegexp *regexp.Regexp
+
+func init() {
+       constraintOperators = map[string]constraintFunc{
+               "":   constraintEqual,
+               "=":  constraintEqual,
+               "!=": constraintNotEqual,
+               ">":  constraintGreaterThan,
+               "<":  constraintLessThan,
+               ">=": constraintGreaterThanEqual,
+               "<=": constraintLessThanEqual,
+               "~>": constraintPessimistic,
+       }
+
+       ops := make([]string, 0, len(constraintOperators))
+       for k := range constraintOperators {
+               ops = append(ops, regexp.QuoteMeta(k))
+       }
+
+       constraintRegexp = regexp.MustCompile(fmt.Sprintf(
+               `^\s*(%s)\s*(%s)\s*$`,
+               strings.Join(ops, "|"),
+               VersionRegexpRaw))
+}
+
+// NewConstraint will parse one or more constraints from the given
+// constraint string. The string must be a comma-separated list of
+// constraints.
+func NewConstraint(v string) (Constraints, error) {
+       vs := strings.Split(v, ",")
+       result := make([]*Constraint, len(vs))
+       for i, single := range vs {
+               c, err := parseSingle(single)
+               if err != nil {
+                       return nil, err
+               }
+
+               result[i] = c
+       }
+
+       return Constraints(result), nil
+}
+
+// Check tests if a version satisfies all the constraints.
+func (cs Constraints) Check(v *Version) bool {
+       for _, c := range cs {
+               if !c.Check(v) {
+                       return false
+               }
+       }
+
+       return true
+}
+
+// Returns the string format of the constraints
+func (cs Constraints) String() string {
+       csStr := make([]string, len(cs))
+       for i, c := range cs {
+               csStr[i] = c.String()
+       }
+
+       return strings.Join(csStr, ",")
+}
+
+// Check tests if a constraint is validated by the given version.
+func (c *Constraint) Check(v *Version) bool {
+       return c.f(v, c.check)
+}
+
+func (c *Constraint) String() string {
+       return c.original
+}
+
+func parseSingle(v string) (*Constraint, error) {
+       matches := constraintRegexp.FindStringSubmatch(v)
+       if matches == nil {
+               return nil, fmt.Errorf("Malformed constraint: %s", v)
+       }
+
+       check, err := NewVersion(matches[2])
+       if err != nil {
+               return nil, err
+       }
+
+       return &Constraint{
+               f:        constraintOperators[matches[1]],
+               check:    check,
+               original: v,
+       }, nil
+}
+
+func prereleaseCheck(v, c *Version) bool {
+       switch vPre, cPre := v.Prerelease() != "", c.Prerelease() != ""; {
+       case cPre && vPre:
+               // A constraint with a pre-release can only match a pre-release version
+               // with the same base segments.
+               return reflect.DeepEqual(c.Segments64(), v.Segments64())
+
+       case !cPre && vPre:
+               // A constraint without a pre-release can only match a version without a
+               // pre-release.
+               return false
+
+       case cPre && !vPre:
+               // OK, except with the pessimistic operator
+       case !cPre && !vPre:
+               // OK
+       }
+       return true
+}
+
+//-------------------------------------------------------------------
+// Constraint functions
+//-------------------------------------------------------------------
+
+func constraintEqual(v, c *Version) bool {
+       return v.Equal(c)
+}
+
+func constraintNotEqual(v, c *Version) bool {
+       return !v.Equal(c)
+}
+
+func constraintGreaterThan(v, c *Version) bool {
+       return prereleaseCheck(v, c) && v.Compare(c) == 1
+}
+
+func constraintLessThan(v, c *Version) bool {
+       return prereleaseCheck(v, c) && v.Compare(c) == -1
+}
+
+func constraintGreaterThanEqual(v, c *Version) bool {
+       return prereleaseCheck(v, c) && v.Compare(c) >= 0
+}
+
+func constraintLessThanEqual(v, c *Version) bool {
+       return prereleaseCheck(v, c) && v.Compare(c) <= 0
+}
+
+func constraintPessimistic(v, c *Version) bool {
+       // Using a pessimistic constraint with a pre-release, restricts versions to pre-releases
+       if !prereleaseCheck(v, c) || (c.Prerelease() != "" && v.Prerelease() == "") {
+               return false
+       }
+
+       // If the version being checked is naturally less than the constraint, then there
+       // is no way for the version to be valid against the constraint
+       if v.LessThan(c) {
+               return false
+       }
+       // We'll use this more than once, so grab the length now so it's a little cleaner
+       // to write the later checks
+       cs := len(c.segments)
+
+       // If the version being checked has less specificity than the constraint, then there
+       // is no way for the version to be valid against the constraint
+       if cs > len(v.segments) {
+               return false
+       }
+
+       // Check the segments in the constraint against those in the version. If the version
+       // being checked, at any point, does not have the same values in each index of the
+       // constraints segments, then it cannot be valid against the constraint.
+       for i := 0; i < c.si-1; i++ {
+               if v.segments[i] != c.segments[i] {
+                       return false
+               }
+       }
+
+       // Check the last part of the segment in the constraint. If the version segment at
+       // this index is less than the constraints segment at this index, then it cannot
+       // be valid against the constraint
+       if c.segments[cs-1] > v.segments[cs-1] {
+               return false
+       }
+
+       // If nothing has rejected the version by now, it's valid
+       return true
+}