1- // Copyright (c) HashiCorp, Inc.
1+ // Copyright IBM Corp. 2014, 2025
22// SPDX-License-Identifier: MPL-2.0
33
44package version
@@ -8,8 +8,26 @@ import (
88 "regexp"
99 "sort"
1010 "strings"
11+ "sync"
1112)
1213
14+ var (
15+ constraintRegexp * regexp.Regexp
16+ constraintRegexpOnce sync.Once
17+ )
18+
19+ func getConstraintRegexp () * regexp.Regexp {
20+ constraintRegexpOnce .Do (func () {
21+ // This heavy lifting only happens the first time this function is called
22+ constraintRegexp = regexp .MustCompile (fmt .Sprintf (
23+ `^\s*(%s)\s*(%s)\s*$` ,
24+ `<=|>=|!=|~>|<|>|=|` ,
25+ VersionRegexpRaw ,
26+ ))
27+ })
28+ return constraintRegexp
29+ }
30+
1331// Constraint represents a single constraint for a version, such as
1432// ">= 1.0".
1533type Constraint struct {
@@ -29,38 +47,11 @@ type Constraints []*Constraint
2947
3048type constraintFunc func (v , c * Version ) bool
3149
32- var constraintOperators map [string ]constraintOperation
33-
3450type constraintOperation struct {
3551 op operator
3652 f constraintFunc
3753}
3854
39- var constraintRegexp * regexp.Regexp
40-
41- func init () {
42- constraintOperators = map [string ]constraintOperation {
43- "" : {op : equal , f : constraintEqual },
44- "=" : {op : equal , f : constraintEqual },
45- "!=" : {op : notEqual , f : constraintNotEqual },
46- ">" : {op : greaterThan , f : constraintGreaterThan },
47- "<" : {op : lessThan , f : constraintLessThan },
48- ">=" : {op : greaterThanEqual , f : constraintGreaterThanEqual },
49- "<=" : {op : lessThanEqual , f : constraintLessThanEqual },
50- "~>" : {op : pessimistic , f : constraintPessimistic },
51- }
52-
53- ops := make ([]string , 0 , len (constraintOperators ))
54- for k := range constraintOperators {
55- ops = append (ops , regexp .QuoteMeta (k ))
56- }
57-
58- constraintRegexp = regexp .MustCompile (fmt .Sprintf (
59- `^\s*(%s)\s*(%s)\s*$` ,
60- strings .Join (ops , "|" ),
61- VersionRegexpRaw ))
62- }
63-
6455// NewConstraint will parse one or more constraints from the given
6556// constraint string. The string must be a comma-separated list of
6657// constraints.
@@ -107,7 +98,7 @@ func (cs Constraints) Check(v *Version) bool {
10798// to '>0.2' it is *NOT* treated as equal.
10899//
109100// Missing operator is treated as equal to '=', whitespaces
110- // are ignored and constraints are sorted before comaparison .
101+ // are ignored and constraints are sorted before comparison .
111102func (cs Constraints ) Equals (c Constraints ) bool {
112103 if len (cs ) != len (c ) {
113104 return false
@@ -176,17 +167,35 @@ func (c *Constraint) String() string {
176167}
177168
178169func parseSingle (v string ) (* Constraint , error ) {
179- matches := constraintRegexp .FindStringSubmatch (v )
170+ matches := getConstraintRegexp () .FindStringSubmatch (v )
180171 if matches == nil {
181- return nil , fmt .Errorf ("Malformed constraint: %s" , v )
172+ return nil , fmt .Errorf ("malformed constraint: %s" , v )
182173 }
183174
184175 check , err := NewVersion (matches [2 ])
185176 if err != nil {
186177 return nil , err
187178 }
188179
189- cop := constraintOperators [matches [1 ]]
180+ var cop constraintOperation
181+ switch matches [1 ] {
182+ case "=" :
183+ cop = constraintOperation {op : equal , f : constraintEqual }
184+ case "!=" :
185+ cop = constraintOperation {op : notEqual , f : constraintNotEqual }
186+ case ">" :
187+ cop = constraintOperation {op : greaterThan , f : constraintGreaterThan }
188+ case "<" :
189+ cop = constraintOperation {op : lessThan , f : constraintLessThan }
190+ case ">=" :
191+ cop = constraintOperation {op : greaterThanEqual , f : constraintGreaterThanEqual }
192+ case "<=" :
193+ cop = constraintOperation {op : lessThanEqual , f : constraintLessThanEqual }
194+ case "~>" :
195+ cop = constraintOperation {op : pessimistic , f : constraintPessimistic }
196+ default :
197+ cop = constraintOperation {op : equal , f : constraintEqual }
198+ }
190199
191200 return & Constraint {
192201 f : cop .f ,
0 commit comments