Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 38 additions & 29 deletions constraint.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,26 @@ import (
"regexp"
"sort"
"strings"
"sync"
)

var (
constraintRegexp *regexp.Regexp
constraintRegexpOnce sync.Once
)

func getConstraintRegexp() *regexp.Regexp {
constraintRegexpOnce.Do(func() {
// This heavy lifting only happens the first time this function is called
constraintRegexp = regexp.MustCompile(fmt.Sprintf(
`^\s*(%s)\s*(%s)\s*$`,
`<=|>=|!=|~>|<|>|=|`,
VersionRegexpRaw,
))
})
return constraintRegexp
}

// Constraint represents a single constraint for a version, such as
// ">= 1.0".
type Constraint struct {
Expand All @@ -29,38 +47,11 @@ type Constraints []*Constraint

type constraintFunc func(v, c *Version) bool

var constraintOperators map[string]constraintOperation

type constraintOperation struct {
op operator
f constraintFunc
}

var constraintRegexp *regexp.Regexp

func init() {
constraintOperators = map[string]constraintOperation{
"": {op: equal, f: constraintEqual},
"=": {op: equal, f: constraintEqual},
"!=": {op: notEqual, f: constraintNotEqual},
">": {op: greaterThan, f: constraintGreaterThan},
"<": {op: lessThan, f: constraintLessThan},
">=": {op: greaterThanEqual, f: constraintGreaterThanEqual},
"<=": {op: lessThanEqual, f: constraintLessThanEqual},
"~>": {op: pessimistic, f: 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.
Expand Down Expand Up @@ -176,7 +167,7 @@ func (c *Constraint) String() string {
}

func parseSingle(v string) (*Constraint, error) {
matches := constraintRegexp.FindStringSubmatch(v)
matches := getConstraintRegexp().FindStringSubmatch(v)
if matches == nil {
return nil, fmt.Errorf("malformed constraint: %s", v)
}
Expand All @@ -186,7 +177,25 @@ func parseSingle(v string) (*Constraint, error) {
return nil, err
}

cop := constraintOperators[matches[1]]
var cop constraintOperation
switch matches[1] {
case "=":
cop = constraintOperation{op: equal, f: constraintEqual}
case "!=":
cop = constraintOperation{op: notEqual, f: constraintNotEqual}
case ">":
cop = constraintOperation{op: greaterThan, f: constraintGreaterThan}
case "<":
cop = constraintOperation{op: lessThan, f: constraintLessThan}
case ">=":
cop = constraintOperation{op: greaterThanEqual, f: constraintGreaterThanEqual}
case "<=":
cop = constraintOperation{op: lessThanEqual, f: constraintLessThanEqual}
case "~>":
cop = constraintOperation{op: pessimistic, f: constraintPessimistic}
default:
cop = constraintOperation{op: equal, f: constraintEqual}
}

return &Constraint{
f: cop.f,
Expand Down
30 changes: 21 additions & 9 deletions version.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,31 @@ import (
"regexp"
"strconv"
"strings"
"sync"
)

// The compiled regular expression used to test the validity of a version.
var (
versionRegexp *regexp.Regexp
semverRegexp *regexp.Regexp
versionRegexp *regexp.Regexp
versionRegexpOnce sync.Once
semverRegexp *regexp.Regexp
semverRegexpOnce sync.Once
)

func getVersionRegexp() *regexp.Regexp {
versionRegexpOnce.Do(func() {
versionRegexp = regexp.MustCompile("^" + VersionRegexpRaw + "$")
})
return versionRegexp
}

func getSemverRegexp() *regexp.Regexp {
semverRegexpOnce.Do(func() {
semverRegexp = regexp.MustCompile("^" + SemverRegexpRaw + "$")
})
return semverRegexp
}

// The raw regular expression string used for testing the validity
// of a version.
const (
Expand All @@ -41,22 +58,17 @@ type Version struct {
original string
}

func init() {
versionRegexp = regexp.MustCompile("^" + VersionRegexpRaw + "$")
semverRegexp = regexp.MustCompile("^" + SemverRegexpRaw + "$")
}

// NewVersion parses the given version and returns a new
// Version.
func NewVersion(v string) (*Version, error) {
return newVersion(v, versionRegexp)
return newVersion(v, getVersionRegexp())
}

// NewSemver parses the given version and returns a new
// Version that adheres strictly to SemVer specs
// https://semver.org/
func NewSemver(v string) (*Version, error) {
return newVersion(v, semverRegexp)
return newVersion(v, getSemverRegexp())
}

func newVersion(v string, pattern *regexp.Regexp) (*Version, error) {
Expand Down