-
-
Notifications
You must be signed in to change notification settings - Fork 430
Expand file tree
/
Copy pathMetacelloSemanticVersionNumber.class.st
More file actions
554 lines (479 loc) · 19.3 KB
/
Copy pathMetacelloSemanticVersionNumber.class.st
File metadata and controls
554 lines (479 loc) · 19.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
"
MetacelloSemanticVersionNumber conforms to version 2.0.0-rc.1 of [Semantic Versioning 2.0.0-rc.1](http://semver.org/)
The most important thing that you need to know is that:
**A normal version number MUST take the form X.Y.Z where X, Y, and Z are non-negative integers.**
**Semantic Versioning Specification** extracted from [Semantic versioning 2.0.0-rc.1](https://github.com/mojombo/semver/blob/3c7f2e8df747ea0ca15208fdfc90e3275240184f/semver.md):
Semantic Versioning Specification (SemVer)
------------------------------------------
The key words ""MUST"", ""MUST NOT"", ""REQUIRED"", ""SHALL"", ""SHALL NOT"", ""SHOULD"",
""SHOULD NOT"", ""RECOMMENDED"", ""MAY"", and ""OPTIONAL"" in this document are to be
interpreted as described in RFC 2119.
1. Software using Semantic Versioning MUST declare a public API. This API
could be declared in the code itself or exist strictly in documentation.
However it is done, it should be precise and comprehensive.
1. A normal version number MUST take the form X.Y.Z where X, Y, and Z are
non-negative integers. X is the major version, Y is the minor version, and Z
is the patch version. Each element MUST increase numerically by increments of
one. For instance: 1.9.0 -> 1.10.0 -> 1.11.0.
1. Once a versioned package has been released, the contents of that version
MUST NOT be modified. Any modifications must be released as a new version.
1. Major version zero (0.y.z) is for initial development. Anything may change
at any time. The public API should not be considered stable.
1. Version 1.0.0 defines the public API. The way in which the version number
is incremented after this release is dependent on this public API and how it
changes.
1. Patch version Z (x.y.Z | x > 0) MUST be incremented if only backwards
compatible bug fixes are introduced. A bug fix is defined as an internal
change that fixes incorrect behavior.
1. Minor version Y (x.Y.z | x > 0) MUST be incremented if new, backwards
compatible functionality is introduced to the public API. It MUST be
incremented if any public API functionality is marked as deprecated. It MAY be
incremented if substantial new functionality or improvements are introduced
within the private code. It MAY include patch level changes. Patch version
MUST be reset to 0 when minor version is incremented.
1. Major version X (X.y.z | X > 0) MUST be incremented if any backwards
incompatible changes are introduced to the public API. It MAY include minor
and patch level changes. Patch and minor version MUST be reset to 0 when major
version is incremented.
1. A pre-release version MAY be denoted by appending a hyphen and a series of
dot separated identifiers immediately following the patch version. Identifiers
MUST be comprised of only ASCII alphanumerics and dash [0-9A-Za-z-].
Pre-release versions satisfy but have a lower precedence than the associated
normal version. Examples: 1.0.0-alpha, 1.0.0-alpha.1, 1.0.0-0.3.7,
1.0.0-x.7.z.92.
1. A build version MAY be denoted by appending a plus sign and a series of dot
separated identifiers immediately following the patch version or pre-release
version. Identifiers MUST be comprised of only ASCII alphanumerics and dash
[0-9A-Za-z-]. Build versions satisfy and have a higher precedence than the
associated normal version. Examples: 1.0.0+build.1, 1.3.7+build.11.e0f985a.
1. Precedence MUST be calculated by separating the version into major, minor,
patch, pre-release, and build identifiers in that order. Major, minor, and
patch versions are always compared numerically. Pre-release and build version
precedence MUST be determined by comparing each dot separated identifier as
follows: identifiers consisting of only digits are compared numerically and
identifiers with letters or dashes are compared lexically in ASCII sort order.
Numeric identifiers always have lower precedence than non-numeric identifiers.
Example: 1.0.0-alpha < 1.0.0-alpha.1 < 1.0.0-beta.2 < 1.0.0-beta.11 <
1.0.0-rc.1 < 1.0.0-rc.1+build.1 < 1.0.0 < 1.0.0+0.3.7 < 1.3.7+build <
1.3.7+build.2.b8f12d7 < 1.3.7+build.11.e0f985a.
About
-----
The Semantic Versioning specification is authored by [Tom
Preston-Werner](http://tom.preston-werner.com), inventor of Gravatars and
cofounder of GitHub.
If you'd like to leave feedback, please [open an issue on
GitHub](https://github.com/mojombo/semver/issues).
License
-------
Creative Commons - CC BY 3.0
http://creativecommons.org/licenses/by/3.0/
"
Class {
#name : 'MetacelloSemanticVersionNumber',
#superclass : 'Magnitude',
#instVars : [
'normalVersion',
'preReleaseVersion',
'buildVersion'
],
#category : 'Metacello-Core-Model',
#package : 'Metacello-Core',
#tag : 'Model'
}
{ #category : 'private' }
MetacelloSemanticVersionNumber class >> extractNumericComponent: subString forPattern: forPattern [
"$. separated components are integers"
| number |
forPattern
ifTrue: [ ^ subString ].
number := [ subString asNumber ]
on: Error
do: [ :ex | ex return: subString ].
^ number asString = subString
ifTrue: [ number ]
ifFalse: [ subString ]
]
{ #category : 'instance creation' }
MetacelloSemanticVersionNumber class >> fromString: aString [
| svn |
svn := self fromString: aString forPattern: false.
self validateVersionNumber: svn against: aString.
^ svn
]
{ #category : 'instance creation' }
MetacelloSemanticVersionNumber class >> fromString: aString forPattern: forPattern [
| preRelease build versionString identifierCount normalEnd preReleaseEnd normalComponents preReleaseComponents buildComponents |
normalComponents := OrderedCollection new.
preReleaseComponents := OrderedCollection new.
buildComponents := OrderedCollection new.
preRelease := aString indexOf: $- startingAt: 1.
build := aString indexOf: $+ startingAt: 1.
(build > 0 and: [ preRelease > build ])
ifTrue: [ preRelease := 0 ].
normalEnd := preRelease = 0
ifTrue: [
build = 0
ifTrue: [ aString size ]
ifFalse: [ build - 1 ] ]
ifFalse: [ preRelease - 1 ].
versionString := aString copyFrom: 1 to: normalEnd.
identifierCount := 0.
(versionString findTokens: '.')
do: [ :subString |
| integer |
forPattern
ifTrue: [ integer := subString ]
ifFalse: [
integer := self integerFromString: subString.
integer < 0
ifTrue: [
self
error:
'invalid version number: normal version component must be integer '
, subString printString ] ].
normalComponents add: integer.
identifierCount := identifierCount + 1 ].
(forPattern not and: [ identifierCount ~= 3 ])
ifTrue: [ self error: 'invalid version number: normal version must have only 3 components' ].
preReleaseEnd := build = 0
ifTrue: [ aString size ]
ifFalse: [ build - 1 ].
preRelease > 0
ifTrue: [
versionString := aString copyFrom: preRelease + 1 to: preReleaseEnd.
(versionString findTokens: '.')
do: [ :subString |
(forPattern or: [ self isSemanticIdentifier: subString ])
ifFalse: [
self
error:
'invalid version number: preRelease version component must be one of [0-9A-Za-z-]' ].
preReleaseComponents
add:
(self extractNumericComponent: subString forPattern: forPattern) ] ].
build > 0
ifTrue: [
versionString := aString copyFrom: build + 1 to: aString size.
(versionString findTokens: '.')
do: [ :subString |
(forPattern or: [ self isSemanticIdentifier: subString ])
ifFalse: [
self
error:
'invalid version number: build version component must be one of [0-9A-Za-z-]' ].
buildComponents
add:
(self extractNumericComponent: subString forPattern: forPattern) ] ].
^ self new
normalVersion: normalComponents;
preReleaseVersion: preReleaseComponents;
buildVersion: buildComponents;
yourself
]
{ #category : 'private' }
MetacelloSemanticVersionNumber class >> integerFromString: aString [
aString detect: [ :char | char isDigit not ] ifNone: [
| integer |
integer := aString extractIntegerPart.
((aString at: 1) = $0 and: [ aString size > 1 ]) ifTrue: [
self error:
'invalid version number: normal version component must not have leading 0s'
, aString asString ].
^ integer ].
self error:
'invalid version number: normal version component must be integer '
, aString asString
]
{ #category : 'private' }
MetacelloSemanticVersionNumber class >> isSemanticIdentifier: aString [
"whether the receiver is composed entirely of alphanumerics"
aString
do: [ :c |
c isAlphaNumeric
ifFalse: [
c = $-
ifFalse: [ ^ false ] ] ].
^ true
]
{ #category : 'private' }
MetacelloSemanticVersionNumber class >> validateVersionNumber: svn against: aString [
svn printString = aString
ifFalse: [
self
error:
'The printString of a semantic version number should be equal to the source version string' ]
]
{ #category : 'comparing' }
MetacelloSemanticVersionNumber >> < aMetacelloVersionNumber [
aMetacelloVersionNumber species = self species
ifFalse: [ ^ false ].
^ self compareLessThan: aMetacelloVersionNumber
]
{ #category : 'comparing' }
MetacelloSemanticVersionNumber >> = aMetacelloVersionNumber [
aMetacelloVersionNumber species = self species
ifFalse: [ ^ false ].
^ self compareEqualTo: aMetacelloVersionNumber
]
{ #category : 'accessing' }
MetacelloSemanticVersionNumber >> approximateBase [
| base |
base := self copyFrom: 1 to: self size - 1.
base at: base size put: (base at: base size) + 1.
^base
]
{ #category : 'accessing' }
MetacelloSemanticVersionNumber >> buildVersion [
buildVersion ifNil: [ buildVersion := #() ].
^ buildVersion
]
{ #category : 'accessing' }
MetacelloSemanticVersionNumber >> buildVersion: anObject [
buildVersion := anObject
]
{ #category : 'private' }
MetacelloSemanticVersionNumber >> compareEqualTo: aMetacelloVersionNumber [
aMetacelloVersionNumber species = self species
ifFalse: [ ^ false ].
(self compareEqualTo: self normalVersion other: aMetacelloVersionNumber normalVersion)
ifFalse: [ ^ false ].
(self compareEqualTo: self preReleaseVersion other: aMetacelloVersionNumber preReleaseVersion)
ifFalse: [ ^ false ].
^ self compareEqualTo: self buildVersion other: aMetacelloVersionNumber buildVersion
]
{ #category : 'private' }
MetacelloSemanticVersionNumber >> compareEqualTo: myComponents other: otherComponents [
| mySize |
mySize := myComponents size.
mySize = otherComponents size
ifFalse: [ ^ false ].
1 to: mySize do: [ :i |
(myComponents at: i) = (otherComponents at: i)
ifFalse: [ ^ false ] ].
^ true
]
{ #category : 'private' }
MetacelloSemanticVersionNumber >> compareLessThan: aMetacelloVersionNumber [
| myComponents otherComponents defaultResult |
aMetacelloVersionNumber species = self species ifFalse: [ ^ false ].
myComponents := self normalVersion.
otherComponents := aMetacelloVersionNumber normalVersion.
defaultResult := true.
(self compareEqualTo: myComponents other: otherComponents)
ifTrue: [ defaultResult := false ]
ifFalse: [ (self compareLessThan: myComponents other: otherComponents version: #normal) ifFalse: [ ^ false ] ].
myComponents := self preReleaseVersion.
otherComponents := aMetacelloVersionNumber preReleaseVersion.
(self compareEqualTo: myComponents other: otherComponents)
ifTrue: [ myComponents size > 0 ifTrue: [ defaultResult := false ] ]
ifFalse: [ ^ self compareLessThan: myComponents other: otherComponents version: #preRelease ].
myComponents := self buildVersion.
otherComponents := aMetacelloVersionNumber buildVersion.
^ (self compareEqualTo: myComponents other: otherComponents)
ifTrue: [ defaultResult ]
ifFalse: [ self compareLessThan: myComponents other: otherComponents version: #build ]
]
{ #category : 'private' }
MetacelloSemanticVersionNumber >> compareLessThan: myComponents other: otherComponents version: version [
| mySize aSize commonSize count more |
mySize := myComponents size.
aSize := otherComponents size.
commonSize := mySize min: aSize.
count := 0.
more := true.
[ more and: [ count < commonSize ] ]
whileTrue: [ (myComponents at: count + 1) = (otherComponents at: count + 1)
ifTrue: [ count := count + 1 ]
ifFalse: [ more := false ] ].
count < commonSize ifTrue: [ ^ (myComponents at: count + 1) metacelloSemanticVersionComponentLessThan: (otherComponents at: count + 1) ].
^ mySize < aSize
ifTrue: [ mySize = 0
ifTrue: [ #preRelease == version ifTrue: [ ^ false ].
^ true ].
(myComponents at: commonSize) = (otherComponents at: commonSize) ifFalse: [ ^ true ].
true ]
ifFalse: [ mySize = aSize ifTrue: [ ^ false ].
aSize = 0
ifTrue: [ #build == version ifTrue: [ ^ false ].
^ true ].
(myComponents at: commonSize) = (otherComponents at: commonSize) ifFalse: [ ^ false ].
true ]
]
{ #category : 'copying' }
MetacelloSemanticVersionNumber >> copyFrom: start to: stop [
"Answer a copy of a subset of the receiver, starting from element at
index start until element at index stop."
| newSize new j |
newSize := stop - start + 1.
new := self species new: newSize.
j := 0.
start to: stop do: [:i |
new at: j + 1 put: (self at: i).
j := j + 1 ].
^new
]
{ #category : 'operations' }
MetacelloSemanticVersionNumber >> decrementMajorVersion [
self decrementNormalVersionAt: 1
]
{ #category : 'operations' }
MetacelloSemanticVersionNumber >> decrementMinorVersion [
self decrementNormalVersionAt: 2
]
{ #category : 'operations' }
MetacelloSemanticVersionNumber >> decrementMinorVersionNumber [
self decrementNormalVersionAt: 3
]
{ #category : 'private' }
MetacelloSemanticVersionNumber >> decrementNormalVersionAt: index [
| int col |
col := self normalVersion.
int := col at: index.
int > 0
ifTrue: [ col at: index put: int - 1 ]
]
{ #category : 'operations' }
MetacelloSemanticVersionNumber >> decrementPatchVersion [
self decrementNormalVersionAt: 3
]
{ #category : 'enumerating' }
MetacelloSemanticVersionNumber >> do: aBlock [
"Refer to the comment in Collection|do:."
1 to: self size do:
[:index | aBlock value: (self at: index)]
]
{ #category : 'enumerating' }
MetacelloSemanticVersionNumber >> do: elementBlock separatedBy: separatorBlock [
"Evaluate the elementBlock for all elements in the receiver,
and evaluate the separatorBlock between."
| beforeFirst |
beforeFirst := true.
self do:
[:each |
beforeFirst
ifTrue: [beforeFirst := false]
ifFalse: [separatorBlock value].
elementBlock value: each]
]
{ #category : 'comparing' }
MetacelloSemanticVersionNumber >> hash [
^ self versionComponents hash
]
{ #category : 'operations' }
MetacelloSemanticVersionNumber >> incrementMajorVersion [
self incrementNormalVersionAt: 1
]
{ #category : 'operations' }
MetacelloSemanticVersionNumber >> incrementMinorVersion [
self incrementNormalVersionAt: 2
]
{ #category : 'operations' }
MetacelloSemanticVersionNumber >> incrementMinorVersionNumber [
self incrementNormalVersionAt: 3
]
{ #category : 'private' }
MetacelloSemanticVersionNumber >> incrementNormalVersionAt: index [
| int col |
col := self normalVersion.
int := col at: index.
col at: index put: int + 1
]
{ #category : 'operations' }
MetacelloSemanticVersionNumber >> incrementPatchVersion [
self incrementNormalVersionAt: 3
]
{ #category : 'comparing' }
MetacelloSemanticVersionNumber >> match: aVersionPattern [
"Answer whether the version number of the receiver matches the given pattern string.
A Metacello version number is made up of version sequences delimited by the characters $. and $-.
The $. introduces a numeric version sequence and $- introduces an alphanumeric version sequence.
A version pattern is made up of version pattern match sequences. also delimited by the characters $.
and $-.. Each pattern match sequence is tested against the corresponding version sequence of the
receiver, using the 'standard' pattern matching rules. All sequences must answer true for a match.
The special pattern sequence '?' is a match for the corresponding version sequence and all subsequent
version sequences. '?' as the version pattern matches all versions. No more version pattern
sequences are permitted once the '?' sequence is used. If used, it is the last version pattern
sequence. "
| patternVersion mySize patternSize components |
patternVersion := (self class fromString: aVersionPattern forPattern: true) versionComponents.
components := self versionComponents.
mySize := components size.
patternSize := patternVersion size.
mySize = patternSize
ifFalse: [
mySize < patternSize
ifTrue: [ ^ false ].
(patternVersion at: patternSize) ~= '?'
ifTrue: [ ^ false ].
mySize := patternSize ].
1 to: mySize do: [ :i |
| pattern |
pattern := (patternVersion at: i) asString.
pattern = '?'
ifTrue: [
i = mySize
ifFalse: [ ^ self error: 'Invalid version match pattern: ' , aVersionPattern printString ] ]
ifFalse: [
(pattern match: (components at: i) asString)
ifFalse: [ ^ false ] ] ].
^ true
]
{ #category : 'accessing' }
MetacelloSemanticVersionNumber >> normalVersion [
normalVersion ifNil: [ normalVersion := #() ].
^ normalVersion
]
{ #category : 'accessing' }
MetacelloSemanticVersionNumber >> normalVersion: anObject [
normalVersion := anObject
]
{ #category : 'copying' }
MetacelloSemanticVersionNumber >> postCopy [
normalVersion := normalVersion copy.
preReleaseVersion := preReleaseVersion copy.
buildVersion := buildVersion copy
]
{ #category : 'accessing' }
MetacelloSemanticVersionNumber >> preReleaseVersion [
preReleaseVersion ifNil: [ preReleaseVersion := #() ].
^ preReleaseVersion
]
{ #category : 'accessing' }
MetacelloSemanticVersionNumber >> preReleaseVersion: anObject [
preReleaseVersion := anObject
]
{ #category : 'printing' }
MetacelloSemanticVersionNumber >> print: components prefix: prefixChar on: aStream [
| beforeFirst |
beforeFirst := true.
components
do: [ :component |
beforeFirst
ifTrue: [
beforeFirst := false.
prefixChar ifNotNil: [ aStream nextPut: prefixChar ] ]
ifFalse: [ aStream nextPut: $. ].
aStream nextPutAll: component asString ]
]
{ #category : 'printing' }
MetacelloSemanticVersionNumber >> printOn: aStream [
self print: self normalVersion prefix: nil on: aStream.
self print: self preReleaseVersion prefix: $- on: aStream.
self print: self buildVersion prefix: $+ on: aStream
]
{ #category : 'private' }
MetacelloSemanticVersionNumber >> versionComponents [
^ self normalVersion , self preReleaseVersion , self buildVersion
]
{ #category : 'accessing' }
MetacelloSemanticVersionNumber >> versionString [
| strm |
strm := WriteStream on: String new.
self printOn: strm.
^strm contents
]
{ #category : 'comparing' }
MetacelloSemanticVersionNumber >> ~> aMetacelloVersionNumber [
aMetacelloVersionNumber size == 1 ifTrue: [ ^false ].
^self >= aMetacelloVersionNumber and: [ self < aMetacelloVersionNumber approximateBase ]
]