A clean, Markdown-based publishing platform made for writers. Write together, and build a community. https://writefreely.org
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

316 lines
6.7 KiB

  1. // Copyright 2018 The Go Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. // Package semver implements comparison of semantic version strings.
  5. // In this package, semantic version strings must begin with a leading "v",
  6. // as in "v1.0.0".
  7. //
  8. // The general form of a semantic version string accepted by this package is
  9. //
  10. // vMAJOR[.MINOR[.PATCH[-PRERELEASE][+BUILD]]]
  11. //
  12. // where square brackets indicate optional parts of the syntax;
  13. // MAJOR, MINOR, and PATCH are decimal integers without extra leading zeros;
  14. // PRERELEASE and BUILD are each a series of non-empty dot-separated identifiers
  15. // using only alphanumeric characters and hyphens; and
  16. // all-numeric PRERELEASE identifiers must not have leading zeros.
  17. //
  18. // This package follows Semantic Versioning 2.0.0 (see semver.org)
  19. // with two exceptions. First, it requires the "v" prefix. Second, it recognizes
  20. // vMAJOR and vMAJOR.MINOR (with no prerelease or build suffixes)
  21. // as shorthands for vMAJOR.0.0 and vMAJOR.MINOR.0.
  22. // Package writefreely
  23. // copied from
  24. // https://github.com/golang/tools/blob/master/internal/semver/semver.go
  25. // slight modifications made
  26. package writefreely
  27. // parsed returns the parsed form of a semantic version string.
  28. type parsed struct {
  29. major string
  30. minor string
  31. patch string
  32. short string
  33. prerelease string
  34. build string
  35. err string
  36. }
  37. // IsValid reports whether v is a valid semantic version string.
  38. func IsValid(v string) bool {
  39. _, ok := semParse(v)
  40. return ok
  41. }
  42. // CompareSemver returns an integer comparing two versions according to
  43. // according to semantic version precedence.
  44. // The result will be 0 if v == w, -1 if v < w, or +1 if v > w.
  45. //
  46. // An invalid semantic version string is considered less than a valid one.
  47. // All invalid semantic version strings compare equal to each other.
  48. func CompareSemver(v, w string) int {
  49. pv, ok1 := semParse(v)
  50. pw, ok2 := semParse(w)
  51. if !ok1 && !ok2 {
  52. return 0
  53. }
  54. if !ok1 {
  55. return -1
  56. }
  57. if !ok2 {
  58. return +1
  59. }
  60. if c := compareInt(pv.major, pw.major); c != 0 {
  61. return c
  62. }
  63. if c := compareInt(pv.minor, pw.minor); c != 0 {
  64. return c
  65. }
  66. if c := compareInt(pv.patch, pw.patch); c != 0 {
  67. return c
  68. }
  69. return comparePrerelease(pv.prerelease, pw.prerelease)
  70. }
  71. func semParse(v string) (p parsed, ok bool) {
  72. if v == "" || v[0] != 'v' {
  73. p.err = "missing v prefix"
  74. return
  75. }
  76. p.major, v, ok = parseInt(v[1:])
  77. if !ok {
  78. p.err = "bad major version"
  79. return
  80. }
  81. if v == "" {
  82. p.minor = "0"
  83. p.patch = "0"
  84. p.short = ".0.0"
  85. return
  86. }
  87. if v[0] != '.' {
  88. p.err = "bad minor prefix"
  89. ok = false
  90. return
  91. }
  92. p.minor, v, ok = parseInt(v[1:])
  93. if !ok {
  94. p.err = "bad minor version"
  95. return
  96. }
  97. if v == "" {
  98. p.patch = "0"
  99. p.short = ".0"
  100. return
  101. }
  102. if v[0] != '.' {
  103. p.err = "bad patch prefix"
  104. ok = false
  105. return
  106. }
  107. p.patch, v, ok = parseInt(v[1:])
  108. if !ok {
  109. p.err = "bad patch version"
  110. return
  111. }
  112. if len(v) > 0 && v[0] == '-' {
  113. p.prerelease, v, ok = parsePrerelease(v)
  114. if !ok {
  115. p.err = "bad prerelease"
  116. return
  117. }
  118. }
  119. if len(v) > 0 && v[0] == '+' {
  120. p.build, v, ok = parseBuild(v)
  121. if !ok {
  122. p.err = "bad build"
  123. return
  124. }
  125. }
  126. if v != "" {
  127. p.err = "junk on end"
  128. ok = false
  129. return
  130. }
  131. ok = true
  132. return
  133. }
  134. func parseInt(v string) (t, rest string, ok bool) {
  135. if v == "" {
  136. return
  137. }
  138. if v[0] < '0' || '9' < v[0] {
  139. return
  140. }
  141. i := 1
  142. for i < len(v) && '0' <= v[i] && v[i] <= '9' {
  143. i++
  144. }
  145. if v[0] == '0' && i != 1 {
  146. return
  147. }
  148. return v[:i], v[i:], true
  149. }
  150. func parsePrerelease(v string) (t, rest string, ok bool) {
  151. // "A pre-release version MAY be denoted by appending a hyphen and
  152. // a series of dot separated identifiers immediately following the patch version.
  153. // Identifiers MUST comprise only ASCII alphanumerics and hyphen [0-9A-Za-z-].
  154. // Identifiers MUST NOT be empty. Numeric identifiers MUST NOT include leading zeroes."
  155. if v == "" || v[0] != '-' {
  156. return
  157. }
  158. i := 1
  159. start := 1
  160. for i < len(v) && v[i] != '+' {
  161. if !isIdentChar(v[i]) && v[i] != '.' {
  162. return
  163. }
  164. if v[i] == '.' {
  165. if start == i || isBadNum(v[start:i]) {
  166. return
  167. }
  168. start = i + 1
  169. }
  170. i++
  171. }
  172. if start == i || isBadNum(v[start:i]) {
  173. return
  174. }
  175. return v[:i], v[i:], true
  176. }
  177. func parseBuild(v string) (t, rest string, ok bool) {
  178. if v == "" || v[0] != '+' {
  179. return
  180. }
  181. i := 1
  182. start := 1
  183. for i < len(v) {
  184. if !isIdentChar(v[i]) {
  185. return
  186. }
  187. if v[i] == '.' {
  188. if start == i {
  189. return
  190. }
  191. start = i + 1
  192. }
  193. i++
  194. }
  195. if start == i {
  196. return
  197. }
  198. return v[:i], v[i:], true
  199. }
  200. func isIdentChar(c byte) bool {
  201. return 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || '0' <= c && c <= '9' || c == '-'
  202. }
  203. func isBadNum(v string) bool {
  204. i := 0
  205. for i < len(v) && '0' <= v[i] && v[i] <= '9' {
  206. i++
  207. }
  208. return i == len(v) && i > 1 && v[0] == '0'
  209. }
  210. func isNum(v string) bool {
  211. i := 0
  212. for i < len(v) && '0' <= v[i] && v[i] <= '9' {
  213. i++
  214. }
  215. return i == len(v)
  216. }
  217. func compareInt(x, y string) int {
  218. if x == y {
  219. return 0
  220. }
  221. if len(x) < len(y) {
  222. return -1
  223. }
  224. if len(x) > len(y) {
  225. return +1
  226. }
  227. if x < y {
  228. return -1
  229. } else {
  230. return +1
  231. }
  232. }
  233. func comparePrerelease(x, y string) int {
  234. // "When major, minor, and patch are equal, a pre-release version has
  235. // lower precedence than a normal version.
  236. // Example: 1.0.0-alpha < 1.0.0.
  237. // Precedence for two pre-release versions with the same major, minor,
  238. // and patch version MUST be determined by comparing each dot separated
  239. // identifier from left to right until a difference is found as follows:
  240. // identifiers consisting of only digits are compared numerically and
  241. // identifiers with letters or hyphens are compared lexically in ASCII
  242. // sort order. Numeric identifiers always have lower precedence than
  243. // non-numeric identifiers. A larger set of pre-release fields has a
  244. // higher precedence than a smaller set, if all of the preceding
  245. // identifiers are equal.
  246. // Example: 1.0.0-alpha < 1.0.0-alpha.1 < 1.0.0-alpha.beta <
  247. // 1.0.0-beta < 1.0.0-beta.2 < 1.0.0-beta.11 < 1.0.0-rc.1 < 1.0.0."
  248. if x == y {
  249. return 0
  250. }
  251. if x == "" {
  252. return +1
  253. }
  254. if y == "" {
  255. return -1
  256. }
  257. for x != "" && y != "" {
  258. x = x[1:] // skip - or .
  259. y = y[1:] // skip - or .
  260. var dx, dy string
  261. dx, x = nextIdent(x)
  262. dy, y = nextIdent(y)
  263. if dx != dy {
  264. ix := isNum(dx)
  265. iy := isNum(dy)
  266. if ix != iy {
  267. if ix {
  268. return -1
  269. } else {
  270. return +1
  271. }
  272. }
  273. if ix {
  274. if len(dx) < len(dy) {
  275. return -1
  276. }
  277. if len(dx) > len(dy) {
  278. return +1
  279. }
  280. }
  281. if dx < dy {
  282. return -1
  283. } else {
  284. return +1
  285. }
  286. }
  287. }
  288. if x == "" {
  289. return -1
  290. } else {
  291. return +1
  292. }
  293. }
  294. func nextIdent(x string) (dx, rest string) {
  295. i := 0
  296. for i < len(x) && x[i] != '.' {
  297. i++
  298. }
  299. return x[:i], x[i:]
  300. }