version.go 7.7 KB

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