range.go 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. package semver
  2. import (
  3. "fmt"
  4. "strings"
  5. "unicode"
  6. )
  7. type comparator func(Version, Version) bool
  8. var (
  9. compEQ comparator = func(v1 Version, v2 Version) bool {
  10. return v1.Compare(v2) == 0
  11. }
  12. compNE = func(v1 Version, v2 Version) bool {
  13. return v1.Compare(v2) != 0
  14. }
  15. compGT = func(v1 Version, v2 Version) bool {
  16. return v1.Compare(v2) == 1
  17. }
  18. compGE = func(v1 Version, v2 Version) bool {
  19. return v1.Compare(v2) >= 0
  20. }
  21. compLT = func(v1 Version, v2 Version) bool {
  22. return v1.Compare(v2) == -1
  23. }
  24. compLE = func(v1 Version, v2 Version) bool {
  25. return v1.Compare(v2) <= 0
  26. }
  27. )
  28. type versionRange struct {
  29. v Version
  30. c comparator
  31. }
  32. // rangeFunc creates a Range from the given versionRange.
  33. func (vr *versionRange) rangeFunc() Range {
  34. return Range(func(v Version) bool {
  35. return vr.c(v, vr.v)
  36. })
  37. }
  38. // Range represents a range of versions.
  39. // A Range can be used to check if a Version satisfies it:
  40. //
  41. // range, err := semver.ParseRange(">1.0.0 <2.0.0")
  42. // range(semver.MustParse("1.1.1") // returns true
  43. type Range func(Version) bool
  44. // OR combines the existing Range with another Range using logical OR.
  45. func (rf Range) OR(f Range) Range {
  46. return Range(func(v Version) bool {
  47. return rf(v) || f(v)
  48. })
  49. }
  50. // AND combines the existing Range with another Range using logical AND.
  51. func (rf Range) AND(f Range) Range {
  52. return Range(func(v Version) bool {
  53. return rf(v) && f(v)
  54. })
  55. }
  56. // ParseRange parses a range and returns a Range.
  57. // If the range could not be parsed an error is returned.
  58. //
  59. // Valid ranges are:
  60. // - "<1.0.0"
  61. // - "<=1.0.0"
  62. // - ">1.0.0"
  63. // - ">=1.0.0"
  64. // - "1.0.0", "=1.0.0", "==1.0.0"
  65. // - "!1.0.0", "!=1.0.0"
  66. //
  67. // A Range can consist of multiple ranges separated by space:
  68. // Ranges can be linked by logical AND:
  69. // - ">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"
  70. // - ">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
  71. //
  72. // Ranges can also be linked by logical OR:
  73. // - "<2.0.0 || >=3.0.0" would match "1.x.x" and "3.x.x" but not "2.x.x"
  74. //
  75. // AND has a higher precedence than OR. It's not possible to use brackets.
  76. //
  77. // Ranges can be combined by both AND and OR
  78. //
  79. // - `>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`
  80. func ParseRange(s string) (Range, error) {
  81. parts := splitAndTrim(s)
  82. orParts, err := splitORParts(parts)
  83. if err != nil {
  84. return nil, err
  85. }
  86. var orFn Range
  87. for _, p := range orParts {
  88. var andFn Range
  89. for _, ap := range p {
  90. opStr, vStr, err := splitComparatorVersion(ap)
  91. if err != nil {
  92. return nil, err
  93. }
  94. vr, err := buildVersionRange(opStr, vStr)
  95. if err != nil {
  96. return nil, fmt.Errorf("Could not parse Range %q: %s", ap, err)
  97. }
  98. rf := vr.rangeFunc()
  99. // Set function
  100. if andFn == nil {
  101. andFn = rf
  102. } else { // Combine with existing function
  103. andFn = andFn.AND(rf)
  104. }
  105. }
  106. if orFn == nil {
  107. orFn = andFn
  108. } else {
  109. orFn = orFn.OR(andFn)
  110. }
  111. }
  112. return orFn, nil
  113. }
  114. // splitORParts splits the already cleaned parts by '||'.
  115. // Checks for invalid positions of the operator and returns an
  116. // error if found.
  117. func splitORParts(parts []string) ([][]string, error) {
  118. var ORparts [][]string
  119. last := 0
  120. for i, p := range parts {
  121. if p == "||" {
  122. if i == 0 {
  123. return nil, fmt.Errorf("First element in range is '||'")
  124. }
  125. ORparts = append(ORparts, parts[last:i])
  126. last = i + 1
  127. }
  128. }
  129. if last == len(parts) {
  130. return nil, fmt.Errorf("Last element in range is '||'")
  131. }
  132. ORparts = append(ORparts, parts[last:])
  133. return ORparts, nil
  134. }
  135. // buildVersionRange takes a slice of 2: operator and version
  136. // and builds a versionRange, otherwise an error.
  137. func buildVersionRange(opStr, vStr string) (*versionRange, error) {
  138. c := parseComparator(opStr)
  139. if c == nil {
  140. return nil, fmt.Errorf("Could not parse comparator %q in %q", opStr, strings.Join([]string{opStr, vStr}, ""))
  141. }
  142. v, err := Parse(vStr)
  143. if err != nil {
  144. return nil, fmt.Errorf("Could not parse version %q in %q: %s", vStr, strings.Join([]string{opStr, vStr}, ""), err)
  145. }
  146. return &versionRange{
  147. v: v,
  148. c: c,
  149. }, nil
  150. }
  151. // splitAndTrim splits a range string by spaces and cleans leading and trailing spaces
  152. func splitAndTrim(s string) (result []string) {
  153. last := 0
  154. for i := 0; i < len(s); i++ {
  155. if s[i] == ' ' {
  156. if last < i-1 {
  157. result = append(result, s[last:i])
  158. }
  159. last = i + 1
  160. }
  161. }
  162. if last < len(s)-1 {
  163. result = append(result, s[last:])
  164. }
  165. // parts := strings.Split(s, " ")
  166. // for _, x := range parts {
  167. // if s := strings.TrimSpace(x); len(s) != 0 {
  168. // result = append(result, s)
  169. // }
  170. // }
  171. return
  172. }
  173. // splitComparatorVersion splits the comparator from the version.
  174. // Spaces between the comparator and the version are not allowed.
  175. // Input must be free of leading or trailing spaces.
  176. func splitComparatorVersion(s string) (string, string, error) {
  177. i := strings.IndexFunc(s, unicode.IsDigit)
  178. if i == -1 {
  179. return "", "", fmt.Errorf("Could not get version from string: %q", s)
  180. }
  181. return strings.TrimSpace(s[0:i]), s[i:], nil
  182. }
  183. func parseComparator(s string) comparator {
  184. switch s {
  185. case "==":
  186. fallthrough
  187. case "":
  188. fallthrough
  189. case "=":
  190. return compEQ
  191. case ">":
  192. return compGT
  193. case ">=":
  194. return compGE
  195. case "<":
  196. return compLT
  197. case "<=":
  198. return compLE
  199. case "!":
  200. fallthrough
  201. case "!=":
  202. return compNE
  203. }
  204. return nil
  205. }
  206. // MustParseRange is like ParseRange but panics if the range cannot be parsed.
  207. func MustParseRange(s string) Range {
  208. r, err := ParseRange(s)
  209. if err != nil {
  210. panic(`semver: ParseRange(` + s + `): ` + err.Error())
  211. }
  212. return r
  213. }