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.

version.go 9.9KB

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