github.com/hernad/nomad@v1.6.112/helper/constraints/semver/constraints.go (about) 1 // Copyright (c) HashiCorp, Inc. 2 // SPDX-License-Identifier: MPL-2.0 3 4 // semver is a Semver Constraints package copied from 5 // github.com/hashicorp/go-version @ 2046c9d0f0b03c779670f5186a2a4b2c85493a71 6 // 7 // Unlike Constraints in go-version, Semver constraints use Semver 2.0 ordering 8 // rules and only accept properly formatted Semver versions. 9 package semver 10 11 import ( 12 "fmt" 13 "regexp" 14 "strings" 15 16 "github.com/hashicorp/go-version" 17 ) 18 19 // Constraint represents a single constraint for a version, such as ">= 20 // 1.0". 21 type Constraint struct { 22 f constraintFunc 23 check *version.Version 24 original string 25 } 26 27 // Constraints is a slice of constraints. We make a custom type so that 28 // we can add methods to it. 29 type Constraints []*Constraint 30 31 type constraintFunc func(v, c *version.Version) bool 32 33 var constraintOperators map[string]constraintFunc 34 35 var constraintRegexp *regexp.Regexp 36 37 func init() { 38 constraintOperators = map[string]constraintFunc{ 39 "": constraintEqual, 40 "=": constraintEqual, 41 "!=": constraintNotEqual, 42 ">": constraintGreaterThan, 43 "<": constraintLessThan, 44 ">=": constraintGreaterThanEqual, 45 "<=": constraintLessThanEqual, 46 } 47 48 ops := make([]string, 0, len(constraintOperators)) 49 for k := range constraintOperators { 50 ops = append(ops, regexp.QuoteMeta(k)) 51 } 52 53 constraintRegexp = regexp.MustCompile(fmt.Sprintf( 54 `^\s*(%s)\s*(%s)\s*$`, 55 strings.Join(ops, "|"), 56 version.SemverRegexpRaw)) 57 } 58 59 // NewConstraint will parse one or more constraints from the given 60 // constraint string. The string must be a comma-separated list of constraints. 61 func NewConstraint(v string) (Constraints, error) { 62 vs := strings.Split(v, ",") 63 result := make([]*Constraint, len(vs)) 64 for i, single := range vs { 65 c, err := parseSingle(single) 66 if err != nil { 67 return nil, err 68 } 69 70 result[i] = c 71 } 72 73 return Constraints(result), nil 74 } 75 76 // Check tests if a version satisfies all the constraints. 77 func (cs Constraints) Check(v *version.Version) bool { 78 for _, c := range cs { 79 if !c.Check(v) { 80 return false 81 } 82 } 83 84 return true 85 } 86 87 // String returns the string format of the constraints. 88 func (cs Constraints) String() string { 89 csStr := make([]string, len(cs)) 90 for i, c := range cs { 91 csStr[i] = c.String() 92 } 93 94 return strings.Join(csStr, ",") 95 } 96 97 // Check tests if a constraint is validated by the given version. 98 func (c *Constraint) Check(v *version.Version) bool { 99 return c.f(v, c.check) 100 } 101 102 func (c *Constraint) String() string { 103 return c.original 104 } 105 106 func parseSingle(v string) (*Constraint, error) { 107 matches := constraintRegexp.FindStringSubmatch(v) 108 if matches == nil { 109 return nil, fmt.Errorf("Malformed constraint: %s", v) 110 } 111 112 check, err := version.NewSemver(matches[2]) 113 if err != nil { 114 return nil, err 115 } 116 117 return &Constraint{ 118 f: constraintOperators[matches[1]], 119 check: check, 120 original: v, 121 }, nil 122 } 123 124 //------------------------------------------------------------------- 125 // Constraint functions 126 //------------------------------------------------------------------- 127 128 func constraintEqual(v, c *version.Version) bool { 129 return v.Equal(c) 130 } 131 132 func constraintNotEqual(v, c *version.Version) bool { 133 return !v.Equal(c) 134 } 135 136 func constraintGreaterThan(v, c *version.Version) bool { 137 return v.Compare(c) == 1 138 } 139 140 func constraintLessThan(v, c *version.Version) bool { 141 return v.Compare(c) == -1 142 } 143 144 func constraintGreaterThanEqual(v, c *version.Version) bool { 145 return v.Compare(c) >= 0 146 } 147 148 func constraintLessThanEqual(v, c *version.Version) bool { 149 return v.Compare(c) <= 0 150 }