version.go 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384
  1. package version
  2. import (
  3. "bytes"
  4. "fmt"
  5. "reflect"
  6. "regexp"
  7. "strconv"
  8. "strings"
  9. )
  10. // The compiled regular expression used to test the validity of a version.
  11. var (
  12. versionRegexp *regexp.Regexp
  13. semverRegexp *regexp.Regexp
  14. )
  15. // The raw regular expression string used for testing the validity
  16. // of a version.
  17. const (
  18. VersionRegexpRaw string = `v?([0-9]+(\.[0-9]+)*?)` +
  19. `(-([0-9]+[0-9A-Za-z\-~]*(\.[0-9A-Za-z\-~]+)*)|(-?([A-Za-z\-~]+[0-9A-Za-z\-~]*(\.[0-9A-Za-z\-~]+)*)))?` +
  20. `(\+([0-9A-Za-z\-~]+(\.[0-9A-Za-z\-~]+)*))?` +
  21. `?`
  22. // SemverRegexpRaw requires a separator between version and prerelease
  23. SemverRegexpRaw string = `v?([0-9]+(\.[0-9]+)*?)` +
  24. `(-([0-9]+[0-9A-Za-z\-~]*(\.[0-9A-Za-z\-~]+)*)|(-([A-Za-z\-~]+[0-9A-Za-z\-~]*(\.[0-9A-Za-z\-~]+)*)))?` +
  25. `(\+([0-9A-Za-z\-~]+(\.[0-9A-Za-z\-~]+)*))?` +
  26. `?`
  27. )
  28. // Version represents a single version.
  29. type Version struct {
  30. metadata string
  31. pre string
  32. segments []int64
  33. si int
  34. original string
  35. }
  36. func init() {
  37. versionRegexp = regexp.MustCompile("^" + VersionRegexpRaw + "$")
  38. semverRegexp = regexp.MustCompile("^" + SemverRegexpRaw + "$")
  39. }
  40. // NewVersion parses the given version and returns a new
  41. // Version.
  42. func NewVersion(v string) (*Version, error) {
  43. return newVersion(v, versionRegexp)
  44. }
  45. // NewSemver parses the given version and returns a new
  46. // Version that adheres strictly to SemVer specs
  47. // https://semver.org/
  48. func NewSemver(v string) (*Version, error) {
  49. return newVersion(v, semverRegexp)
  50. }
  51. func newVersion(v string, pattern *regexp.Regexp) (*Version, error) {
  52. matches := pattern.FindStringSubmatch(v)
  53. if matches == nil {
  54. return nil, fmt.Errorf("Malformed version: %s", v)
  55. }
  56. segmentsStr := strings.Split(matches[1], ".")
  57. segments := make([]int64, len(segmentsStr))
  58. si := 0
  59. for i, str := range segmentsStr {
  60. val, err := strconv.ParseInt(str, 10, 64)
  61. if err != nil {
  62. return nil, fmt.Errorf(
  63. "Error parsing version: %s", err)
  64. }
  65. segments[i] = int64(val)
  66. si++
  67. }
  68. // Even though we could support more than three segments, if we
  69. // got less than three, pad it with 0s. This is to cover the basic
  70. // default usecase of semver, which is MAJOR.MINOR.PATCH at the minimum
  71. for i := len(segments); i < 3; i++ {
  72. segments = append(segments, 0)
  73. }
  74. pre := matches[7]
  75. if pre == "" {
  76. pre = matches[4]
  77. }
  78. return &Version{
  79. metadata: matches[10],
  80. pre: pre,
  81. segments: segments,
  82. si: si,
  83. original: v,
  84. }, nil
  85. }
  86. // Must is a helper that wraps a call to a function returning (*Version, error)
  87. // and panics if error is non-nil.
  88. func Must(v *Version, err error) *Version {
  89. if err != nil {
  90. panic(err)
  91. }
  92. return v
  93. }
  94. // Compare compares this version to another version. This
  95. // returns -1, 0, or 1 if this version is smaller, equal,
  96. // or larger than the other version, respectively.
  97. //
  98. // If you want boolean results, use the LessThan, Equal,
  99. // GreaterThan, GreaterThanOrEqual or LessThanOrEqual methods.
  100. func (v *Version) Compare(other *Version) int {
  101. // A quick, efficient equality check
  102. if v.String() == other.String() {
  103. return 0
  104. }
  105. segmentsSelf := v.Segments64()
  106. segmentsOther := other.Segments64()
  107. // If the segments are the same, we must compare on prerelease info
  108. if reflect.DeepEqual(segmentsSelf, segmentsOther) {
  109. preSelf := v.Prerelease()
  110. preOther := other.Prerelease()
  111. if preSelf == "" && preOther == "" {
  112. return 0
  113. }
  114. if preSelf == "" {
  115. return 1
  116. }
  117. if preOther == "" {
  118. return -1
  119. }
  120. return comparePrereleases(preSelf, preOther)
  121. }
  122. // Get the highest specificity (hS), or if they're equal, just use segmentSelf length
  123. lenSelf := len(segmentsSelf)
  124. lenOther := len(segmentsOther)
  125. hS := lenSelf
  126. if lenSelf < lenOther {
  127. hS = lenOther
  128. }
  129. // Compare the segments
  130. // Because a constraint could have more/less specificity than the version it's
  131. // checking, we need to account for a lopsided or jagged comparison
  132. for i := 0; i < hS; i++ {
  133. if i > lenSelf-1 {
  134. // This means Self had the lower specificity
  135. // Check to see if the remaining segments in Other are all zeros
  136. if !allZero(segmentsOther[i:]) {
  137. // if not, it means that Other has to be greater than Self
  138. return -1
  139. }
  140. break
  141. } else if i > lenOther-1 {
  142. // this means Other had the lower specificity
  143. // Check to see if the remaining segments in Self are all zeros -
  144. if !allZero(segmentsSelf[i:]) {
  145. //if not, it means that Self has to be greater than Other
  146. return 1
  147. }
  148. break
  149. }
  150. lhs := segmentsSelf[i]
  151. rhs := segmentsOther[i]
  152. if lhs == rhs {
  153. continue
  154. } else if lhs < rhs {
  155. return -1
  156. }
  157. // Otherwis, rhs was > lhs, they're not equal
  158. return 1
  159. }
  160. // if we got this far, they're equal
  161. return 0
  162. }
  163. func allZero(segs []int64) bool {
  164. for _, s := range segs {
  165. if s != 0 {
  166. return false
  167. }
  168. }
  169. return true
  170. }
  171. func comparePart(preSelf string, preOther string) int {
  172. if preSelf == preOther {
  173. return 0
  174. }
  175. var selfInt int64
  176. selfNumeric := true
  177. selfInt, err := strconv.ParseInt(preSelf, 10, 64)
  178. if err != nil {
  179. selfNumeric = false
  180. }
  181. var otherInt int64
  182. otherNumeric := true
  183. otherInt, err = strconv.ParseInt(preOther, 10, 64)
  184. if err != nil {
  185. otherNumeric = false
  186. }
  187. // if a part is empty, we use the other to decide
  188. if preSelf == "" {
  189. if otherNumeric {
  190. return -1
  191. }
  192. return 1
  193. }
  194. if preOther == "" {
  195. if selfNumeric {
  196. return 1
  197. }
  198. return -1
  199. }
  200. if selfNumeric && !otherNumeric {
  201. return -1
  202. } else if !selfNumeric && otherNumeric {
  203. return 1
  204. } else if !selfNumeric && !otherNumeric && preSelf > preOther {
  205. return 1
  206. } else if selfInt > otherInt {
  207. return 1
  208. }
  209. return -1
  210. }
  211. func comparePrereleases(v string, other string) int {
  212. // the same pre release!
  213. if v == other {
  214. return 0
  215. }
  216. // split both pre releases for analyse their parts
  217. selfPreReleaseMeta := strings.Split(v, ".")
  218. otherPreReleaseMeta := strings.Split(other, ".")
  219. selfPreReleaseLen := len(selfPreReleaseMeta)
  220. otherPreReleaseLen := len(otherPreReleaseMeta)
  221. biggestLen := otherPreReleaseLen
  222. if selfPreReleaseLen > otherPreReleaseLen {
  223. biggestLen = selfPreReleaseLen
  224. }
  225. // loop for parts to find the first difference
  226. for i := 0; i < biggestLen; i = i + 1 {
  227. partSelfPre := ""
  228. if i < selfPreReleaseLen {
  229. partSelfPre = selfPreReleaseMeta[i]
  230. }
  231. partOtherPre := ""
  232. if i < otherPreReleaseLen {
  233. partOtherPre = otherPreReleaseMeta[i]
  234. }
  235. compare := comparePart(partSelfPre, partOtherPre)
  236. // if parts are equals, continue the loop
  237. if compare != 0 {
  238. return compare
  239. }
  240. }
  241. return 0
  242. }
  243. // Equal tests if two versions are equal.
  244. func (v *Version) Equal(o *Version) bool {
  245. if v == nil || o == nil {
  246. return v == o
  247. }
  248. return v.Compare(o) == 0
  249. }
  250. // GreaterThan tests if this version is greater than another version.
  251. func (v *Version) GreaterThan(o *Version) bool {
  252. return v.Compare(o) > 0
  253. }
  254. // GreaterThanOrEqual tests if this version is greater than or equal to another version.
  255. func (v *Version) GreaterThanOrEqual(o *Version) bool {
  256. return v.Compare(o) >= 0
  257. }
  258. // LessThan tests if this version is less than another version.
  259. func (v *Version) LessThan(o *Version) bool {
  260. return v.Compare(o) < 0
  261. }
  262. // LessThanOrEqual tests if this version is less than or equal to another version.
  263. func (v *Version) LessThanOrEqual(o *Version) bool {
  264. return v.Compare(o) <= 0
  265. }
  266. // Metadata returns any metadata that was part of the version
  267. // string.
  268. //
  269. // Metadata is anything that comes after the "+" in the version.
  270. // For example, with "1.2.3+beta", the metadata is "beta".
  271. func (v *Version) Metadata() string {
  272. return v.metadata
  273. }
  274. // Prerelease returns any prerelease data that is part of the version,
  275. // or blank if there is no prerelease data.
  276. //
  277. // Prerelease information is anything that comes after the "-" in the
  278. // version (but before any metadata). For example, with "1.2.3-beta",
  279. // the prerelease information is "beta".
  280. func (v *Version) Prerelease() string {
  281. return v.pre
  282. }
  283. // Segments returns the numeric segments of the version as a slice of ints.
  284. //
  285. // This excludes any metadata or pre-release information. For example,
  286. // for a version "1.2.3-beta", segments will return a slice of
  287. // 1, 2, 3.
  288. func (v *Version) Segments() []int {
  289. segmentSlice := make([]int, len(v.segments))
  290. for i, v := range v.segments {
  291. segmentSlice[i] = int(v)
  292. }
  293. return segmentSlice
  294. }
  295. // Segments64 returns the numeric segments of the version as a slice of int64s.
  296. //
  297. // This excludes any metadata or pre-release information. For example,
  298. // for a version "1.2.3-beta", segments will return a slice of
  299. // 1, 2, 3.
  300. func (v *Version) Segments64() []int64 {
  301. result := make([]int64, len(v.segments))
  302. copy(result, v.segments)
  303. return result
  304. }
  305. // String returns the full version string included pre-release
  306. // and metadata information.
  307. //
  308. // This value is rebuilt according to the parsed segments and other
  309. // information. Therefore, ambiguities in the version string such as
  310. // prefixed zeroes (1.04.0 => 1.4.0), `v` prefix (v1.0.0 => 1.0.0), and
  311. // missing parts (1.0 => 1.0.0) will be made into a canonicalized form
  312. // as shown in the parenthesized examples.
  313. func (v *Version) String() string {
  314. var buf bytes.Buffer
  315. fmtParts := make([]string, len(v.segments))
  316. for i, s := range v.segments {
  317. // We can ignore err here since we've pre-parsed the values in segments
  318. str := strconv.FormatInt(s, 10)
  319. fmtParts[i] = str
  320. }
  321. fmt.Fprintf(&buf, strings.Join(fmtParts, "."))
  322. if v.pre != "" {
  323. fmt.Fprintf(&buf, "-%s", v.pre)
  324. }
  325. if v.metadata != "" {
  326. fmt.Fprintf(&buf, "+%s", v.metadata)
  327. }
  328. return buf.String()
  329. }
  330. // Original returns the original parsed version as-is, including any
  331. // potential whitespace, `v` prefix, etc.
  332. func (v *Version) Original() string {
  333. return v.original
  334. }