version.go 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  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 = `([0-9]+(\.[0-9]+){0,2})` +
  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 []int
  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([]int, len(segmentsStr), 3)
  37. si := 0
  38. for i, str := range segmentsStr {
  39. val, err := strconv.ParseInt(str, 10, 32)
  40. if err != nil {
  41. return nil, fmt.Errorf(
  42. "Error parsing version: %s", err)
  43. }
  44. segments[i] = int(val)
  45. si += 1
  46. }
  47. for i := len(segments); i < 3; i++ {
  48. segments = append(segments, 0)
  49. }
  50. return &Version{
  51. metadata: matches[7],
  52. pre: matches[4],
  53. segments: segments,
  54. si: si,
  55. }, nil
  56. }
  57. // Must is a helper that wraps a call to a function returning (*Version, error)
  58. // and panics if error is non-nil.
  59. func Must(v *Version, err error) *Version {
  60. if err != nil {
  61. panic(err)
  62. }
  63. return v
  64. }
  65. // Compare compares this version to another version. This
  66. // returns -1, 0, or 1 if this version is smaller, equal,
  67. // or larger than the other version, respectively.
  68. //
  69. // If you want boolean results, use the LessThan, Equal,
  70. // or GreaterThan methods.
  71. func (v *Version) Compare(other *Version) int {
  72. // A quick, efficient equality check
  73. if v.String() == other.String() {
  74. return 0
  75. }
  76. segmentsSelf := v.Segments()
  77. segmentsOther := other.Segments()
  78. // If the segments are the same, we must compare on prerelease info
  79. if reflect.DeepEqual(segmentsSelf, segmentsOther) {
  80. preSelf := v.Prerelease()
  81. preOther := other.Prerelease()
  82. if preSelf == "" && preOther == "" {
  83. return 0
  84. }
  85. if preSelf == "" {
  86. return 1
  87. }
  88. if preOther == "" {
  89. return -1
  90. }
  91. return comparePrereleases(preSelf, preOther)
  92. }
  93. // Compare the segments
  94. for i := 0; i < len(segmentsSelf); i++ {
  95. lhs := segmentsSelf[i]
  96. rhs := segmentsOther[i]
  97. if lhs == rhs {
  98. continue
  99. } else if lhs < rhs {
  100. return -1
  101. } else {
  102. return 1
  103. }
  104. }
  105. panic("should not be reached")
  106. }
  107. func comparePart(preSelf string, preOther string) int {
  108. if preSelf == preOther {
  109. return 0
  110. }
  111. // if a part is empty, we use the other to decide
  112. if preSelf == "" {
  113. _, notIsNumeric := strconv.ParseInt(preOther, 10, 64)
  114. if notIsNumeric == nil {
  115. return -1
  116. }
  117. return 1
  118. }
  119. if preOther == "" {
  120. _, notIsNumeric := strconv.ParseInt(preSelf, 10, 64)
  121. if notIsNumeric == nil {
  122. return 1
  123. }
  124. return -1
  125. }
  126. if preSelf > preOther {
  127. return 1
  128. }
  129. return -1
  130. }
  131. func comparePrereleases(v string, other string) int {
  132. // the same pre release!
  133. if v == other {
  134. return 0
  135. }
  136. // split both pre releases for analyse their parts
  137. selfPreReleaseMeta := strings.Split(v, ".")
  138. otherPreReleaseMeta := strings.Split(other, ".")
  139. selfPreReleaseLen := len(selfPreReleaseMeta)
  140. otherPreReleaseLen := len(otherPreReleaseMeta)
  141. biggestLen := otherPreReleaseLen
  142. if selfPreReleaseLen > otherPreReleaseLen {
  143. biggestLen = selfPreReleaseLen
  144. }
  145. // loop for parts to find the first difference
  146. for i := 0; i < biggestLen; i = i + 1 {
  147. partSelfPre := ""
  148. if i < selfPreReleaseLen {
  149. partSelfPre = selfPreReleaseMeta[i]
  150. }
  151. partOtherPre := ""
  152. if i < otherPreReleaseLen {
  153. partOtherPre = otherPreReleaseMeta[i]
  154. }
  155. compare := comparePart(partSelfPre, partOtherPre)
  156. // if parts are equals, continue the loop
  157. if compare != 0 {
  158. return compare
  159. }
  160. }
  161. return 0
  162. }
  163. // Equal tests if two versions are equal.
  164. func (v *Version) Equal(o *Version) bool {
  165. return v.Compare(o) == 0
  166. }
  167. // GreaterThan tests if this version is greater than another version.
  168. func (v *Version) GreaterThan(o *Version) bool {
  169. return v.Compare(o) > 0
  170. }
  171. // LessThan tests if this version is less than another version.
  172. func (v *Version) LessThan(o *Version) bool {
  173. return v.Compare(o) < 0
  174. }
  175. // Metadata returns any metadata that was part of the version
  176. // string.
  177. //
  178. // Metadata is anything that comes after the "+" in the version.
  179. // For example, with "1.2.3+beta", the metadata is "beta".
  180. func (v *Version) Metadata() string {
  181. return v.metadata
  182. }
  183. // Prerelease returns any prerelease data that is part of the version,
  184. // or blank if there is no prerelease data.
  185. //
  186. // Prerelease information is anything that comes after the "-" in the
  187. // version (but before any metadata). For example, with "1.2.3-beta",
  188. // the prerelease information is "beta".
  189. func (v *Version) Prerelease() string {
  190. return v.pre
  191. }
  192. // Segments returns the numeric segments of the version as a slice.
  193. //
  194. // This excludes any metadata or pre-release information. For example,
  195. // for a version "1.2.3-beta", segments will return a slice of
  196. // 1, 2, 3.
  197. func (v *Version) Segments() []int {
  198. return v.segments
  199. }
  200. // String returns the full version string included pre-release
  201. // and metadata information.
  202. func (v *Version) String() string {
  203. var buf bytes.Buffer
  204. fmt.Fprintf(&buf, "%d.%d.%d", v.segments[0], v.segments[1], v.segments[2])
  205. if v.pre != "" {
  206. fmt.Fprintf(&buf, "-%s", v.pre)
  207. }
  208. if v.metadata != "" {
  209. fmt.Fprintf(&buf, "+%s", v.metadata)
  210. }
  211. return buf.String()
  212. }