terraform/vendor/github.com/apparentlymart/go-versions/versions/constraints/version.go

82 lines
2.9 KiB
Go
Raw Normal View History

package constraints
import (
"fmt"
"strings"
)
// ParseExactVersion parses a string that must contain the specification of a
// single, exact version, and then returns it as a VersionSpec.
//
// This is primarily here to allow versions.ParseVersion to re-use the
// constraint grammar, and isn't very useful for direct use from calling
// applications.
func ParseExactVersion(vs string) (VersionSpec, error) {
spec := VersionSpec{}
if strings.TrimSpace(vs) == "" {
return spec, fmt.Errorf("empty specification")
}
raw, remain := scanConstraint(vs)
switch strings.TrimSpace(raw.op) {
case ">", ">=", "<", "<=", "!", "!=", "~>", "^", "~":
// If it looks like the user was trying to write a constraint string
// then we'll help them out with a more specialized error.
return spec, fmt.Errorf("can't use constraint operator %q; an exact version is required", raw.op)
case "":
// Empty operator is okay as long as we don't also have separator spaces.
// (Caller can trim off spaces beforehand if they want to tolerate this.)
if raw.sep != "" {
return spec, fmt.Errorf("extraneous spaces at start of specification")
}
default:
return spec, fmt.Errorf("invalid sequence %q at start of specification", raw.op)
}
if remain != "" {
remain = strings.TrimSpace(remain)
switch {
case remain == "":
return spec, fmt.Errorf("extraneous spaces at end of specification")
case strings.HasPrefix(vs, "v"):
// User seems to be trying to use a "v" prefix, like "v1.0.0"
return spec, fmt.Errorf(`a "v" prefix should not be used`)
case strings.HasPrefix(remain, ",") || strings.HasPrefix(remain, "|"):
// User seems to be trying to list/combine multiple versions
return spec, fmt.Errorf("can't specify multiple versions; a single exact version is required")
case strings.HasPrefix(remain, "-"):
// User seems to be trying to use the npm-style range operator
return spec, fmt.Errorf("can't specify version range; a single exact version is required")
case strings.HasPrefix(strings.TrimSpace(vs), remain):
// Whole string is invalid, then.
return spec, fmt.Errorf("invalid specification; required format is three positive integers separated by periods")
default:
return spec, fmt.Errorf("invalid characters %q", remain)
}
}
if raw.numCt > 3 {
return spec, fmt.Errorf("too many numbered portions; only three are allowed (major, minor, patch)")
}
for i := raw.numCt; i < len(raw.nums); i++ {
raw.nums[i] = "0"
}
for i, s := range raw.nums {
switch {
case isWildcardNum(s):
// Can't use wildcards in an exact specification
return spec, fmt.Errorf("can't use wildcard for %s number; an exact version is required", rawNumNames[i])
}
}
// Since we eliminated all of the unconstrained cases above, either by normalizing
// or returning an error, we are guaranteed to get constrained numbers here.
spec = raw.VersionSpec()
return spec, nil
}