blob: a6e6a97d001c40cf27b81e143e37522f0572cd8a [file] [log] [blame]
package semver
import (
"errors"
"fmt"
"strings"
)
func rewriteRange(i string) string {
m := constraintRangeRegex.FindAllStringSubmatch(i, -1)
if m == nil {
return i
}
o := i
for _, v := range m {
t := fmt.Sprintf(">= %s, <= %s", v[1], v[11])
o = strings.Replace(o, v[0], t, 1)
}
return o
}
func parseConstraint(c string) (Constraint, error) {
m := constraintRegex.FindStringSubmatch(c)
if m == nil {
return nil, fmt.Errorf("Malformed constraint: %s", c)
}
// Handle the full wildcard case first - easy!
if isX(m[3]) {
return any{}, nil
}
ver := m[2]
var wildPatch, wildMinor bool
if isX(strings.TrimPrefix(m[4], ".")) {
wildPatch = true
wildMinor = true
ver = fmt.Sprintf("%s.0.0%s", m[3], m[6])
} else if isX(strings.TrimPrefix(m[5], ".")) {
wildPatch = true
ver = fmt.Sprintf("%s%s.0%s", m[3], m[4], m[6])
}
v, err := NewVersion(ver)
if err != nil {
// The constraintRegex should catch any regex parsing errors. So,
// we should never get here.
return nil, errors.New("constraint Parser Error")
}
switch m[1] {
case "^":
// Caret always expands to a range
return expandCaret(v), nil
case "~":
// Tilde always expands to a range
return expandTilde(v, wildMinor), nil
case "!=":
// Not equals expands to a range if no element isX(); otherwise expands
// to a union of ranges
return expandNeq(v, wildMinor, wildPatch), nil
case "", "=":
if wildPatch || wildMinor {
// Equalling a wildcard has the same behavior as expanding tilde
return expandTilde(v, wildMinor), nil
}
return v, nil
case ">":
return expandGreater(v, wildMinor, wildPatch, false), nil
case ">=", "=>":
return expandGreater(v, wildMinor, wildPatch, true), nil
case "<":
return expandLess(v, wildMinor, wildPatch, false), nil
case "<=", "=<":
return expandLess(v, wildMinor, wildPatch, true), nil
default:
// Shouldn't be possible to get here, unless the regex is allowing
// predicate we don't know about...
return nil, fmt.Errorf("Unrecognized predicate %q", m[1])
}
}
func expandCaret(v *Version) Constraint {
maxv := &Version{
major: v.major + 1,
minor: 0,
patch: 0,
}
return rangeConstraint{
min: v,
max: maxv,
includeMin: true,
includeMax: false,
}
}
func expandTilde(v *Version, wildMinor bool) Constraint {
if wildMinor {
// When minor is wild on a tilde, behavior is same as caret
return expandCaret(v)
}
maxv := &Version{
major: v.major,
minor: v.minor + 1,
patch: 0,
}
return rangeConstraint{
min: v,
max: maxv,
includeMin: true,
includeMax: false,
}
}
// expandNeq expands a "not-equals" constraint.
//
// If the constraint has any wildcards, it will expand into a unionConstraint
// (which is how we represent a disjoint set). If there are no wildcards, it
// will expand to a rangeConstraint with no min or max, but having the one
// exception.
func expandNeq(v *Version, wildMinor, wildPatch bool) Constraint {
if !(wildMinor || wildPatch) {
return rangeConstraint{
excl: []*Version{v},
}
}
// Create the low range with no min, and the max as the floor admitted by
// the wildcard
lr := rangeConstraint{
max: v,
includeMax: false,
}
// The high range uses the derived version (bumped depending on where the
// wildcards were) as the min, and is inclusive
minv := &Version{
major: v.major,
minor: v.minor,
patch: v.patch,
}
if wildMinor {
minv.major++
} else {
minv.minor++
}
hr := rangeConstraint{
min: minv,
includeMin: true,
}
return Union(lr, hr)
}
func expandGreater(v *Version, wildMinor, wildPatch, eq bool) Constraint {
if (wildMinor || wildPatch) && !eq {
// wildcards negate the meaning of prerelease and other info
v = &Version{
major: v.major,
minor: v.minor,
patch: v.patch,
}
// Not equal but with wildcards is the weird case - we have to bump up
// the next version AND make it equal
if wildMinor {
v.major++
} else {
v.minor++
}
return rangeConstraint{
min: v,
includeMin: true,
}
}
return rangeConstraint{
min: v,
includeMin: eq,
}
}
func expandLess(v *Version, wildMinor, wildPatch, eq bool) Constraint {
if eq && (wildMinor || wildPatch) {
// wildcards negate the meaning of prerelease and other info
v = &Version{
major: v.major,
minor: v.minor,
patch: v.patch,
}
if wildMinor {
v.major++
} else if wildPatch {
v.minor++
}
return rangeConstraint{
max: v,
includeMax: false,
}
}
return rangeConstraint{
max: v,
includeMax: eq,
}
}
func isX(x string) bool {
l := strings.ToLower(x)
return l == "x" || l == "*"
}