@@ -43,6 +43,11 @@ public struct SemanticVersion: Codable, Equatable, Hashable {
43
43
self . preRelease = preRelease
44
44
self . build = build
45
45
}
46
+
47
+ public enum PreReleaseIdentifier : Equatable {
48
+ case alphanumeric( String )
49
+ case numeric( Int )
50
+ }
46
51
}
47
52
48
53
@@ -74,26 +79,81 @@ extension SemanticVersion: Comparable {
74
79
if lhs. major != rhs. major { return lhs. major < rhs. major }
75
80
if lhs. minor != rhs. minor { return lhs. minor < rhs. minor }
76
81
if lhs. patch != rhs. patch { return lhs. patch < rhs. patch }
77
- if lhs. preRelease != rhs. preRelease {
78
- // Ensure stable versions sort after their betas ...
79
- if lhs. isStable { return false }
80
- if rhs. isStable { return true }
81
- // ... otherwise sort by preRelease
82
- return lhs. preRelease < rhs. preRelease
83
- }
84
- // ... and build
85
- return lhs. build < rhs. build
82
+
83
+ // A stable release takes precedence over a pre-release
84
+ if lhs. isStable != rhs. isStable { return rhs. isStable }
85
+
86
+ // Otherwise compare the pre-releases per section 11.4
87
+ // See: https://semver.org/#spec-item-11
88
+ return lhs. preReleaseIdentifiers < rhs. preReleaseIdentifiers
89
+
90
+ // Note that per section 10, buildmetadata MUST not be
91
+ // considered when determining precedence
92
+ // See: https://semver.org/#spec-item-10
86
93
}
87
94
}
88
95
89
96
90
97
extension SemanticVersion {
91
- public var isStable : Bool { return preRelease. isEmpty && build . isEmpty }
98
+ public var isStable : Bool { return preRelease. isEmpty }
92
99
public var isPreRelease : Bool { return !isStable }
93
100
public var isMajorRelease : Bool { return isStable && ( major > 0 && minor == 0 && patch == 0 ) }
94
101
public var isMinorRelease : Bool { return isStable && ( minor > 0 && patch == 0 ) }
95
102
public var isPatchRelease : Bool { return isStable && patch > 0 }
96
103
public var isInitialRelease : Bool { return self == . init( 0 , 0 , 0 ) }
104
+
105
+ public var preReleaseIdentifiers : [ PreReleaseIdentifier ] {
106
+ return preRelease
107
+ . split ( separator: " . " )
108
+ . map { PreReleaseIdentifier ( String ( $0) ) }
109
+ }
110
+ }
111
+
112
+ extension SemanticVersion . PreReleaseIdentifier {
113
+ init ( _ rawValue: String ) {
114
+ if let number = Int ( rawValue) {
115
+ self = . numeric( number)
116
+ } else {
117
+ self = . alphanumeric( rawValue)
118
+ }
119
+ }
120
+ }
121
+
122
+ extension SemanticVersion . PreReleaseIdentifier : Comparable {
123
+ public static func < ( lhs: Self , rhs: Self ) -> Bool {
124
+ // These rules are laid out in section 11.4 of the semver spec
125
+ // See: https://semver.org/#spec-item-11
126
+ switch ( lhs, rhs) {
127
+ case ( . numeric, . alphanumeric) :
128
+ // 11.4.3 - Numeric identifiers always have lower precedence than non-numeric identifiers
129
+ return true
130
+ case ( . alphanumeric, . numeric) :
131
+ // 11.4.3 - Numeric identifiers always have lower precedence than non-numeric identifiers
132
+ return false
133
+ case ( . numeric( let lhInt) , . numeric( let rhInt) ) :
134
+ // 11.4.1 - Identifiers consisting of only digits are compared numerically
135
+ return lhInt < rhInt
136
+ case ( . alphanumeric( let lhString) , . alphanumeric( let rhString) ) :
137
+ // 11.4.2 - Identifiers with letters or hyphens are compared lexically in ASCII sort order
138
+ return lhString < rhString
139
+ }
140
+ }
141
+ }
142
+
143
+
144
+ extension Array : Comparable where Element == SemanticVersion . PreReleaseIdentifier {
145
+ public static func < ( lhs: Self , rhs: Self ) -> Bool {
146
+ // Per section 11.4 of the semver spec, compare left to right until a
147
+ // difference is found.
148
+ // See: https://semver.org/#spec-item-11
149
+ for (lhIdentifier, rhIdentifier) in zip ( lhs, rhs) {
150
+ if lhIdentifier != rhIdentifier { return lhIdentifier < rhIdentifier }
151
+ }
152
+
153
+ // 11.4.4 - A larger set of identifiers will have a higher precendence
154
+ // than a smaller set, if all the preceding identifiers are equal.
155
+ return lhs. count < rhs. count
156
+ }
97
157
}
98
158
99
159
#if swift(>=5.5)
0 commit comments