range.go 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417
  1. package semver
  2. import (
  3. "fmt"
  4. "strconv"
  5. "strings"
  6. "unicode"
  7. )
  8. type wildcardType int
  9. const (
  10. noneWildcard wildcardType = iota
  11. majorWildcard wildcardType = 1
  12. minorWildcard wildcardType = 2
  13. patchWildcard wildcardType = 3
  14. )
  15. func wildcardTypefromInt(i int) wildcardType {
  16. switch i {
  17. case 1:
  18. return majorWildcard
  19. case 2:
  20. return minorWildcard
  21. case 3:
  22. return patchWildcard
  23. default:
  24. return noneWildcard
  25. }
  26. }
  27. type comparator func(Version, Version) bool
  28. var (
  29. compEQ comparator = func(v1 Version, v2 Version) bool {
  30. return v1.Compare(v2) == 0
  31. }
  32. compNE = func(v1 Version, v2 Version) bool {
  33. return v1.Compare(v2) != 0
  34. }
  35. compGT = func(v1 Version, v2 Version) bool {
  36. return v1.Compare(v2) == 1
  37. }
  38. compGE = func(v1 Version, v2 Version) bool {
  39. return v1.Compare(v2) >= 0
  40. }
  41. compLT = func(v1 Version, v2 Version) bool {
  42. return v1.Compare(v2) == -1
  43. }
  44. compLE = func(v1 Version, v2 Version) bool {
  45. return v1.Compare(v2) <= 0
  46. }
  47. )
  48. type versionRange struct {
  49. v Version
  50. c comparator
  51. }
  52. // rangeFunc creates a Range from the given versionRange.
  53. func (vr *versionRange) rangeFunc() Range {
  54. return Range(func(v Version) bool {
  55. return vr.c(v, vr.v)
  56. })
  57. }
  58. // Range represents a range of versions.
  59. // A Range can be used to check if a Version satisfies it:
  60. //
  61. // range, err := semver.ParseRange(">1.0.0 <2.0.0")
  62. // range(semver.MustParse("1.1.1") // returns true
  63. type Range func(Version) bool
  64. // OR combines the existing Range with another Range using logical OR.
  65. func (rf Range) OR(f Range) Range {
  66. return Range(func(v Version) bool {
  67. return rf(v) || f(v)
  68. })
  69. }
  70. // AND combines the existing Range with another Range using logical AND.
  71. func (rf Range) AND(f Range) Range {
  72. return Range(func(v Version) bool {
  73. return rf(v) && f(v)
  74. })
  75. }
  76. // ParseRange parses a range and returns a Range.
  77. // If the range could not be parsed an error is returned.
  78. //
  79. // Valid ranges are:
  80. // - "<1.0.0"
  81. // - "<=1.0.0"
  82. // - ">1.0.0"
  83. // - ">=1.0.0"
  84. // - "1.0.0", "=1.0.0", "==1.0.0"
  85. // - "!1.0.0", "!=1.0.0"
  86. //
  87. // A Range can consist of multiple ranges separated by space:
  88. // Ranges can be linked by logical AND:
  89. // - ">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"
  90. // - ">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
  91. //
  92. // Ranges can also be linked by logical OR:
  93. // - "<2.0.0 || >=3.0.0" would match "1.x.x" and "3.x.x" but not "2.x.x"
  94. //
  95. // AND has a higher precedence than OR. It's not possible to use brackets.
  96. //
  97. // Ranges can be combined by both AND and OR
  98. //
  99. // - `>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`
  100. func ParseRange(s string) (Range, error) {
  101. parts := splitAndTrim(s)
  102. orParts, err := splitORParts(parts)
  103. if err != nil {
  104. return nil, err
  105. }
  106. expandedParts, err := expandWildcardVersion(orParts)
  107. if err != nil {
  108. return nil, err
  109. }
  110. var orFn Range
  111. for _, p := range expandedParts {
  112. var andFn Range
  113. for _, ap := range p {
  114. opStr, vStr, err := splitComparatorVersion(ap)
  115. if err != nil {
  116. return nil, err
  117. }
  118. vr, err := buildVersionRange(opStr, vStr)
  119. if err != nil {
  120. return nil, fmt.Errorf("Could not parse Range %q: %s", ap, err)
  121. }
  122. rf := vr.rangeFunc()
  123. // Set function
  124. if andFn == nil {
  125. andFn = rf
  126. } else { // Combine with existing function
  127. andFn = andFn.AND(rf)
  128. }
  129. }
  130. if orFn == nil {
  131. orFn = andFn
  132. } else {
  133. orFn = orFn.OR(andFn)
  134. }
  135. }
  136. return orFn, nil
  137. }
  138. // splitORParts splits the already cleaned parts by '||'.
  139. // Checks for invalid positions of the operator and returns an
  140. // error if found.
  141. func splitORParts(parts []string) ([][]string, error) {
  142. var ORparts [][]string
  143. last := 0
  144. for i, p := range parts {
  145. if p == "||" {
  146. if i == 0 {
  147. return nil, fmt.Errorf("First element in range is '||'")
  148. }
  149. ORparts = append(ORparts, parts[last:i])
  150. last = i + 1
  151. }
  152. }
  153. if last == len(parts) {
  154. return nil, fmt.Errorf("Last element in range is '||'")
  155. }
  156. ORparts = append(ORparts, parts[last:])
  157. return ORparts, nil
  158. }
  159. // buildVersionRange takes a slice of 2: operator and version
  160. // and builds a versionRange, otherwise an error.
  161. func buildVersionRange(opStr, vStr string) (*versionRange, error) {
  162. c := parseComparator(opStr)
  163. if c == nil {
  164. return nil, fmt.Errorf("Could not parse comparator %q in %q", opStr, strings.Join([]string{opStr, vStr}, ""))
  165. }
  166. v, err := Parse(vStr)
  167. if err != nil {
  168. return nil, fmt.Errorf("Could not parse version %q in %q: %s", vStr, strings.Join([]string{opStr, vStr}, ""), err)
  169. }
  170. return &versionRange{
  171. v: v,
  172. c: c,
  173. }, nil
  174. }
  175. // inArray checks if a byte is contained in an array of bytes
  176. func inArray(s byte, list []byte) bool {
  177. for _, el := range list {
  178. if el == s {
  179. return true
  180. }
  181. }
  182. return false
  183. }
  184. // splitAndTrim splits a range string by spaces and cleans whitespaces
  185. func splitAndTrim(s string) (result []string) {
  186. last := 0
  187. var lastChar byte
  188. excludeFromSplit := []byte{'>', '<', '='}
  189. for i := 0; i < len(s); i++ {
  190. if s[i] == ' ' && !inArray(lastChar, excludeFromSplit) {
  191. if last < i-1 {
  192. result = append(result, s[last:i])
  193. }
  194. last = i + 1
  195. } else if s[i] != ' ' {
  196. lastChar = s[i]
  197. }
  198. }
  199. if last < len(s)-1 {
  200. result = append(result, s[last:])
  201. }
  202. for i, v := range result {
  203. result[i] = strings.Replace(v, " ", "", -1)
  204. }
  205. // parts := strings.Split(s, " ")
  206. // for _, x := range parts {
  207. // if s := strings.TrimSpace(x); len(s) != 0 {
  208. // result = append(result, s)
  209. // }
  210. // }
  211. return
  212. }
  213. // splitComparatorVersion splits the comparator from the version.
  214. // Input must be free of leading or trailing spaces.
  215. func splitComparatorVersion(s string) (string, string, error) {
  216. i := strings.IndexFunc(s, unicode.IsDigit)
  217. if i == -1 {
  218. return "", "", fmt.Errorf("Could not get version from string: %q", s)
  219. }
  220. return strings.TrimSpace(s[0:i]), s[i:], nil
  221. }
  222. // getWildcardType will return the type of wildcard that the
  223. // passed version contains
  224. func getWildcardType(vStr string) wildcardType {
  225. parts := strings.Split(vStr, ".")
  226. nparts := len(parts)
  227. wildcard := parts[nparts-1]
  228. possibleWildcardType := wildcardTypefromInt(nparts)
  229. if wildcard == "x" {
  230. return possibleWildcardType
  231. }
  232. return noneWildcard
  233. }
  234. // createVersionFromWildcard will convert a wildcard version
  235. // into a regular version, replacing 'x's with '0's, handling
  236. // special cases like '1.x.x' and '1.x'
  237. func createVersionFromWildcard(vStr string) string {
  238. // handle 1.x.x
  239. vStr2 := strings.Replace(vStr, ".x.x", ".x", 1)
  240. vStr2 = strings.Replace(vStr2, ".x", ".0", 1)
  241. parts := strings.Split(vStr2, ".")
  242. // handle 1.x
  243. if len(parts) == 2 {
  244. return vStr2 + ".0"
  245. }
  246. return vStr2
  247. }
  248. // incrementMajorVersion will increment the major version
  249. // of the passed version
  250. func incrementMajorVersion(vStr string) (string, error) {
  251. parts := strings.Split(vStr, ".")
  252. i, err := strconv.Atoi(parts[0])
  253. if err != nil {
  254. return "", err
  255. }
  256. parts[0] = strconv.Itoa(i + 1)
  257. return strings.Join(parts, "."), nil
  258. }
  259. // incrementMajorVersion will increment the minor version
  260. // of the passed version
  261. func incrementMinorVersion(vStr string) (string, error) {
  262. parts := strings.Split(vStr, ".")
  263. i, err := strconv.Atoi(parts[1])
  264. if err != nil {
  265. return "", err
  266. }
  267. parts[1] = strconv.Itoa(i + 1)
  268. return strings.Join(parts, "."), nil
  269. }
  270. // expandWildcardVersion will expand wildcards inside versions
  271. // following these rules:
  272. //
  273. // * when dealing with patch wildcards:
  274. // >= 1.2.x will become >= 1.2.0
  275. // <= 1.2.x will become < 1.3.0
  276. // > 1.2.x will become >= 1.3.0
  277. // < 1.2.x will become < 1.2.0
  278. // != 1.2.x will become < 1.2.0 >= 1.3.0
  279. //
  280. // * when dealing with minor wildcards:
  281. // >= 1.x will become >= 1.0.0
  282. // <= 1.x will become < 2.0.0
  283. // > 1.x will become >= 2.0.0
  284. // < 1.0 will become < 1.0.0
  285. // != 1.x will become < 1.0.0 >= 2.0.0
  286. //
  287. // * when dealing with wildcards without
  288. // version operator:
  289. // 1.2.x will become >= 1.2.0 < 1.3.0
  290. // 1.x will become >= 1.0.0 < 2.0.0
  291. func expandWildcardVersion(parts [][]string) ([][]string, error) {
  292. var expandedParts [][]string
  293. for _, p := range parts {
  294. var newParts []string
  295. for _, ap := range p {
  296. if strings.Index(ap, "x") != -1 {
  297. opStr, vStr, err := splitComparatorVersion(ap)
  298. if err != nil {
  299. return nil, err
  300. }
  301. versionWildcardType := getWildcardType(vStr)
  302. flatVersion := createVersionFromWildcard(vStr)
  303. var resultOperator string
  304. var shouldIncrementVersion bool
  305. switch opStr {
  306. case ">":
  307. resultOperator = ">="
  308. shouldIncrementVersion = true
  309. case ">=":
  310. resultOperator = ">="
  311. case "<":
  312. resultOperator = "<"
  313. case "<=":
  314. resultOperator = "<"
  315. shouldIncrementVersion = true
  316. case "", "=", "==":
  317. newParts = append(newParts, ">="+flatVersion)
  318. resultOperator = "<"
  319. shouldIncrementVersion = true
  320. case "!=", "!":
  321. newParts = append(newParts, "<"+flatVersion)
  322. resultOperator = ">="
  323. shouldIncrementVersion = true
  324. }
  325. var resultVersion string
  326. if shouldIncrementVersion {
  327. switch versionWildcardType {
  328. case patchWildcard:
  329. resultVersion, _ = incrementMinorVersion(flatVersion)
  330. case minorWildcard:
  331. resultVersion, _ = incrementMajorVersion(flatVersion)
  332. }
  333. } else {
  334. resultVersion = flatVersion
  335. }
  336. ap = resultOperator + resultVersion
  337. }
  338. newParts = append(newParts, ap)
  339. }
  340. expandedParts = append(expandedParts, newParts)
  341. }
  342. return expandedParts, nil
  343. }
  344. func parseComparator(s string) comparator {
  345. switch s {
  346. case "==":
  347. fallthrough
  348. case "":
  349. fallthrough
  350. case "=":
  351. return compEQ
  352. case ">":
  353. return compGT
  354. case ">=":
  355. return compGE
  356. case "<":
  357. return compLT
  358. case "<=":
  359. return compLE
  360. case "!":
  361. fallthrough
  362. case "!=":
  363. return compNE
  364. }
  365. return nil
  366. }
  367. // MustParseRange is like ParseRange but panics if the range cannot be parsed.
  368. func MustParseRange(s string) Range {
  369. r, err := ParseRange(s)
  370. if err != nil {
  371. panic(`semver: ParseRange(` + s + `): ` + err.Error())
  372. }
  373. return r
  374. }