@@ -18,6 +18,7 @@ import (
18
18
"fmt"
19
19
"regexp"
20
20
"strconv"
21
+ "strings"
21
22
)
22
23
23
24
// See: https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string
@@ -85,6 +86,137 @@ func (v Version) IsZero() bool {
85
86
return v .Major == 0 && v .Minor == 0 && v .Patch == 0 && v .PreRelease == "" && v .BuildMeta == ""
86
87
}
87
88
89
+ //===========================================================================
90
+ // Comparison
91
+ //===========================================================================
92
+
93
+ // Compare returns the precedence of version v compared to version o. See Compare
94
+ // for more details about how precedence is determined.
95
+ func (v Version ) Compare (o Version ) int {
96
+ return Compare (v , o )
97
+ }
98
+
99
+ // Compare returns the precedence of version a compared to version b. If a has a higher
100
+ // precedence than b (a > b), the result is 1. If a has a lower precedence than b (a < b),
101
+ // the result is -1. If they have the same precedence (a == b), the result is 0.
102
+ // Note that BuildMeta is not factored into precedence so it is possible to have two
103
+ // semantically equivalent versions with different string values.
104
+ //
105
+ // Precedence is determined by the first difference when comparing each of these
106
+ // identifiers from left to right as follows: Major, minor, and patch versions are
107
+ // always compared numerically.
108
+ //
109
+ // When major, minor, and patch are equal, a pre-release version has lower precedence
110
+ // than a normal version.
111
+ //
112
+ // Precedence for two pre-release versions with the same major, minor, and patch version
113
+ // MUST be determined by comparing each dot separated identifier from left to right
114
+ // until a difference is found as follows:
115
+ //
116
+ // 1. Identifiers consisting of only digits are compared numerically.
117
+ // 2. Identifiers with letters or hyphens are compared lexically in ASCII sort order.
118
+ // 3. Numeric identifiers always have lower precedence than non-numeric identifiers.
119
+ // 4. A larger set of pre-release fields has a higher precedence than a smaller set,
120
+ // if all of the preceding identifiers are equal.
121
+ func Compare (a , b Version ) int {
122
+ if a .Major != b .Major {
123
+ if a .Major > b .Major {
124
+ return 1
125
+ }
126
+ return - 1
127
+ }
128
+
129
+ if a .Minor != b .Minor {
130
+ if a .Minor > b .Minor {
131
+ return 1
132
+ }
133
+ return - 1
134
+ }
135
+
136
+ if a .Patch != b .Patch {
137
+ if a .Patch > b .Patch {
138
+ return 1
139
+ }
140
+ return - 1
141
+ }
142
+
143
+ if a .PreRelease != b .PreRelease {
144
+ // a doesn't have a prerelease so it has a higher precedence
145
+ if a .PreRelease == "" {
146
+ return 1
147
+ }
148
+
149
+ // b doesn't have a prerelease so it has a higher precedence
150
+ if b .PreRelease == "" {
151
+ return - 1
152
+ }
153
+
154
+ ar := strings .Split (a .PreRelease , "." )
155
+ br := strings .Split (b .PreRelease , "." )
156
+
157
+ if len (ar ) >= len (br ) {
158
+ for i , bv := range br {
159
+ av := ar [i ]
160
+ if precedence := compareIdentifier (av , bv ); precedence != 0 {
161
+ return precedence
162
+ }
163
+ }
164
+
165
+ // a has more pre-release fields than b and all are equal so a has a higher precedence
166
+ // if len(ar) == len(br) then the first difference would have been found in the loop
167
+ return 1
168
+ } else {
169
+ for i , av := range ar {
170
+ bv := br [i ]
171
+ if precedence := compareIdentifier (av , bv ); precedence != 0 {
172
+ return precedence
173
+ }
174
+ }
175
+
176
+ // b has more pre-release fields than a and all are equal so b has a higher precedence
177
+ return - 1
178
+ }
179
+ }
180
+
181
+ // At this point we know the versions are semantically equivalent.
182
+ return 0
183
+ }
184
+
185
+ func compareIdentifier (a , b string ) int {
186
+ if a == b {
187
+ return 0
188
+ }
189
+
190
+ // Determine if a and b are numeric or non-numeric.
191
+ na , aIsNumeric := isNumeric (a )
192
+ nb , bIsNumeric := isNumeric (b )
193
+
194
+ switch {
195
+ // If both are numeric, compare numerically.
196
+ case aIsNumeric && bIsNumeric :
197
+ if na > nb {
198
+ return 1
199
+ }
200
+ return - 1
201
+ // If one is numeric and the other is not, the numeric one has lower precedence.
202
+ case aIsNumeric :
203
+ return - 1
204
+ case bIsNumeric :
205
+ return 1
206
+ default :
207
+ // Compare lexicographically in ASCII sort order.
208
+ return strings .Compare (a , b )
209
+ }
210
+ }
211
+
212
+ func isNumeric (s string ) (v uint16 , ok bool ) {
213
+ n , err := strconv .ParseUint (s , 10 , 16 )
214
+ if err != nil {
215
+ return 0 , false
216
+ }
217
+ return uint16 (n ), true
218
+ }
219
+
88
220
//===========================================================================
89
221
// Serialization and Deserialization
90
222
//===========================================================================
0 commit comments