]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blobdiff - vendor/github.com/blang/semver/range.go
vendor: github.com/hashicorp/terraform/...@v0.10.0
[github/fretlink/terraform-provider-statuscake.git] / vendor / github.com / blang / semver / range.go
diff --git a/vendor/github.com/blang/semver/range.go b/vendor/github.com/blang/semver/range.go
new file mode 100644 (file)
index 0000000..fca406d
--- /dev/null
@@ -0,0 +1,416 @@
+package semver
+
+import (
+       "fmt"
+       "strconv"
+       "strings"
+       "unicode"
+)
+
+type wildcardType int
+
+const (
+       noneWildcard  wildcardType = iota
+       majorWildcard wildcardType = 1
+       minorWildcard wildcardType = 2
+       patchWildcard wildcardType = 3
+)
+
+func wildcardTypefromInt(i int) wildcardType {
+       switch i {
+       case 1:
+               return majorWildcard
+       case 2:
+               return minorWildcard
+       case 3:
+               return patchWildcard
+       default:
+               return noneWildcard
+       }
+}
+
+type comparator func(Version, Version) bool
+
+var (
+       compEQ comparator = func(v1 Version, v2 Version) bool {
+               return v1.Compare(v2) == 0
+       }
+       compNE = func(v1 Version, v2 Version) bool {
+               return v1.Compare(v2) != 0
+       }
+       compGT = func(v1 Version, v2 Version) bool {
+               return v1.Compare(v2) == 1
+       }
+       compGE = func(v1 Version, v2 Version) bool {
+               return v1.Compare(v2) >= 0
+       }
+       compLT = func(v1 Version, v2 Version) bool {
+               return v1.Compare(v2) == -1
+       }
+       compLE = func(v1 Version, v2 Version) bool {
+               return v1.Compare(v2) <= 0
+       }
+)
+
+type versionRange struct {
+       v Version
+       c comparator
+}
+
+// rangeFunc creates a Range from the given versionRange.
+func (vr *versionRange) rangeFunc() Range {
+       return Range(func(v Version) bool {
+               return vr.c(v, vr.v)
+       })
+}
+
+// Range represents a range of versions.
+// A Range can be used to check if a Version satisfies it:
+//
+//     range, err := semver.ParseRange(">1.0.0 <2.0.0")
+//     range(semver.MustParse("1.1.1") // returns true
+type Range func(Version) bool
+
+// OR combines the existing Range with another Range using logical OR.
+func (rf Range) OR(f Range) Range {
+       return Range(func(v Version) bool {
+               return rf(v) || f(v)
+       })
+}
+
+// AND combines the existing Range with another Range using logical AND.
+func (rf Range) AND(f Range) Range {
+       return Range(func(v Version) bool {
+               return rf(v) && f(v)
+       })
+}
+
+// ParseRange parses a range and returns a Range.
+// If the range could not be parsed an error is returned.
+//
+// Valid ranges are:
+//   - "<1.0.0"
+//   - "<=1.0.0"
+//   - ">1.0.0"
+//   - ">=1.0.0"
+//   - "1.0.0", "=1.0.0", "==1.0.0"
+//   - "!1.0.0", "!=1.0.0"
+//
+// A Range can consist of multiple ranges separated by space:
+// Ranges can be linked by logical AND:
+//   - ">1.0.0 <2.0.0" would match between both ranges, so "1.1.1" and "1.8.7" but not "1.0.0" or "2.0.0"
+//   - ">1.0.0 <3.0.0 !2.0.3-beta.2" would match every version between 1.0.0 and 3.0.0 except 2.0.3-beta.2
+//
+// Ranges can also be linked by logical OR:
+//   - "<2.0.0 || >=3.0.0" would match "1.x.x" and "3.x.x" but not "2.x.x"
+//
+// AND has a higher precedence than OR. It's not possible to use brackets.
+//
+// Ranges can be combined by both AND and OR
+//
+//  - `>1.0.0 <2.0.0 || >3.0.0 !4.2.1` would match `1.2.3`, `1.9.9`, `3.1.1`, but not `4.2.1`, `2.1.1`
+func ParseRange(s string) (Range, error) {
+       parts := splitAndTrim(s)
+       orParts, err := splitORParts(parts)
+       if err != nil {
+               return nil, err
+       }
+       expandedParts, err := expandWildcardVersion(orParts)
+       if err != nil {
+               return nil, err
+       }
+       var orFn Range
+       for _, p := range expandedParts {
+               var andFn Range
+               for _, ap := range p {
+                       opStr, vStr, err := splitComparatorVersion(ap)
+                       if err != nil {
+                               return nil, err
+                       }
+                       vr, err := buildVersionRange(opStr, vStr)
+                       if err != nil {
+                               return nil, fmt.Errorf("Could not parse Range %q: %s", ap, err)
+                       }
+                       rf := vr.rangeFunc()
+
+                       // Set function
+                       if andFn == nil {
+                               andFn = rf
+                       } else { // Combine with existing function
+                               andFn = andFn.AND(rf)
+                       }
+               }
+               if orFn == nil {
+                       orFn = andFn
+               } else {
+                       orFn = orFn.OR(andFn)
+               }
+
+       }
+       return orFn, nil
+}
+
+// splitORParts splits the already cleaned parts by '||'.
+// Checks for invalid positions of the operator and returns an
+// error if found.
+func splitORParts(parts []string) ([][]string, error) {
+       var ORparts [][]string
+       last := 0
+       for i, p := range parts {
+               if p == "||" {
+                       if i == 0 {
+                               return nil, fmt.Errorf("First element in range is '||'")
+                       }
+                       ORparts = append(ORparts, parts[last:i])
+                       last = i + 1
+               }
+       }
+       if last == len(parts) {
+               return nil, fmt.Errorf("Last element in range is '||'")
+       }
+       ORparts = append(ORparts, parts[last:])
+       return ORparts, nil
+}
+
+// buildVersionRange takes a slice of 2: operator and version
+// and builds a versionRange, otherwise an error.
+func buildVersionRange(opStr, vStr string) (*versionRange, error) {
+       c := parseComparator(opStr)
+       if c == nil {
+               return nil, fmt.Errorf("Could not parse comparator %q in %q", opStr, strings.Join([]string{opStr, vStr}, ""))
+       }
+       v, err := Parse(vStr)
+       if err != nil {
+               return nil, fmt.Errorf("Could not parse version %q in %q: %s", vStr, strings.Join([]string{opStr, vStr}, ""), err)
+       }
+
+       return &versionRange{
+               v: v,
+               c: c,
+       }, nil
+
+}
+
+// inArray checks if a byte is contained in an array of bytes
+func inArray(s byte, list []byte) bool {
+       for _, el := range list {
+               if el == s {
+                       return true
+               }
+       }
+       return false
+}
+
+// splitAndTrim splits a range string by spaces and cleans whitespaces
+func splitAndTrim(s string) (result []string) {
+       last := 0
+       var lastChar byte
+       excludeFromSplit := []byte{'>', '<', '='}
+       for i := 0; i < len(s); i++ {
+               if s[i] == ' ' && !inArray(lastChar, excludeFromSplit) {
+                       if last < i-1 {
+                               result = append(result, s[last:i])
+                       }
+                       last = i + 1
+               } else if s[i] != ' ' {
+                       lastChar = s[i]
+               }
+       }
+       if last < len(s)-1 {
+               result = append(result, s[last:])
+       }
+
+       for i, v := range result {
+               result[i] = strings.Replace(v, " ", "", -1)
+       }
+
+       // parts := strings.Split(s, " ")
+       // for _, x := range parts {
+       //      if s := strings.TrimSpace(x); len(s) != 0 {
+       //              result = append(result, s)
+       //      }
+       // }
+       return
+}
+
+// splitComparatorVersion splits the comparator from the version.
+// Input must be free of leading or trailing spaces.
+func splitComparatorVersion(s string) (string, string, error) {
+       i := strings.IndexFunc(s, unicode.IsDigit)
+       if i == -1 {
+               return "", "", fmt.Errorf("Could not get version from string: %q", s)
+       }
+       return strings.TrimSpace(s[0:i]), s[i:], nil
+}
+
+// getWildcardType will return the type of wildcard that the
+// passed version contains
+func getWildcardType(vStr string) wildcardType {
+       parts := strings.Split(vStr, ".")
+       nparts := len(parts)
+       wildcard := parts[nparts-1]
+
+       possibleWildcardType := wildcardTypefromInt(nparts)
+       if wildcard == "x" {
+               return possibleWildcardType
+       }
+
+       return noneWildcard
+}
+
+// createVersionFromWildcard will convert a wildcard version
+// into a regular version, replacing 'x's with '0's, handling
+// special cases like '1.x.x' and '1.x'
+func createVersionFromWildcard(vStr string) string {
+       // handle 1.x.x
+       vStr2 := strings.Replace(vStr, ".x.x", ".x", 1)
+       vStr2 = strings.Replace(vStr2, ".x", ".0", 1)
+       parts := strings.Split(vStr2, ".")
+
+       // handle 1.x
+       if len(parts) == 2 {
+               return vStr2 + ".0"
+       }
+
+       return vStr2
+}
+
+// incrementMajorVersion will increment the major version
+// of the passed version
+func incrementMajorVersion(vStr string) (string, error) {
+       parts := strings.Split(vStr, ".")
+       i, err := strconv.Atoi(parts[0])
+       if err != nil {
+               return "", err
+       }
+       parts[0] = strconv.Itoa(i + 1)
+
+       return strings.Join(parts, "."), nil
+}
+
+// incrementMajorVersion will increment the minor version
+// of the passed version
+func incrementMinorVersion(vStr string) (string, error) {
+       parts := strings.Split(vStr, ".")
+       i, err := strconv.Atoi(parts[1])
+       if err != nil {
+               return "", err
+       }
+       parts[1] = strconv.Itoa(i + 1)
+
+       return strings.Join(parts, "."), nil
+}
+
+// expandWildcardVersion will expand wildcards inside versions
+// following these rules:
+//
+// * when dealing with patch wildcards:
+// >= 1.2.x    will become    >= 1.2.0
+// <= 1.2.x    will become    <  1.3.0
+// >  1.2.x    will become    >= 1.3.0
+// <  1.2.x    will become    <  1.2.0
+// != 1.2.x    will become    <  1.2.0 >= 1.3.0
+//
+// * when dealing with minor wildcards:
+// >= 1.x      will become    >= 1.0.0
+// <= 1.x      will become    <  2.0.0
+// >  1.x      will become    >= 2.0.0
+// <  1.0      will become    <  1.0.0
+// != 1.x      will become    <  1.0.0 >= 2.0.0
+//
+// * when dealing with wildcards without
+// version operator:
+// 1.2.x       will become    >= 1.2.0 < 1.3.0
+// 1.x         will become    >= 1.0.0 < 2.0.0
+func expandWildcardVersion(parts [][]string) ([][]string, error) {
+       var expandedParts [][]string
+       for _, p := range parts {
+               var newParts []string
+               for _, ap := range p {
+                       if strings.Index(ap, "x") != -1 {
+                               opStr, vStr, err := splitComparatorVersion(ap)
+                               if err != nil {
+                                       return nil, err
+                               }
+
+                               versionWildcardType := getWildcardType(vStr)
+                               flatVersion := createVersionFromWildcard(vStr)
+
+                               var resultOperator string
+                               var shouldIncrementVersion bool
+                               switch opStr {
+                               case ">":
+                                       resultOperator = ">="
+                                       shouldIncrementVersion = true
+                               case ">=":
+                                       resultOperator = ">="
+                               case "<":
+                                       resultOperator = "<"
+                               case "<=":
+                                       resultOperator = "<"
+                                       shouldIncrementVersion = true
+                               case "", "=", "==":
+                                       newParts = append(newParts, ">="+flatVersion)
+                                       resultOperator = "<"
+                                       shouldIncrementVersion = true
+                               case "!=", "!":
+                                       newParts = append(newParts, "<"+flatVersion)
+                                       resultOperator = ">="
+                                       shouldIncrementVersion = true
+                               }
+
+                               var resultVersion string
+                               if shouldIncrementVersion {
+                                       switch versionWildcardType {
+                                       case patchWildcard:
+                                               resultVersion, _ = incrementMinorVersion(flatVersion)
+                                       case minorWildcard:
+                                               resultVersion, _ = incrementMajorVersion(flatVersion)
+                                       }
+                               } else {
+                                       resultVersion = flatVersion
+                               }
+
+                               ap = resultOperator + resultVersion
+                       }
+                       newParts = append(newParts, ap)
+               }
+               expandedParts = append(expandedParts, newParts)
+       }
+
+       return expandedParts, nil
+}
+
+func parseComparator(s string) comparator {
+       switch s {
+       case "==":
+               fallthrough
+       case "":
+               fallthrough
+       case "=":
+               return compEQ
+       case ">":
+               return compGT
+       case ">=":
+               return compGE
+       case "<":
+               return compLT
+       case "<=":
+               return compLE
+       case "!":
+               fallthrough
+       case "!=":
+               return compNE
+       }
+
+       return nil
+}
+
+// MustParseRange is like ParseRange but panics if the range cannot be parsed.
+func MustParseRange(s string) Range {
+       r, err := ParseRange(s)
+       if err != nil {
+               panic(`semver: ParseRange(` + s + `): ` + err.Error())
+       }
+       return r
+}