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  }