Skip to content

Commit d1ef00b

Browse files
committed
Drop NSRegularExpression
1 parent 40245e0 commit d1ef00b

File tree

5 files changed

+137
-201
lines changed

5 files changed

+137
-201
lines changed

Package.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import PackageDescription
1818

1919
let package = Package(
2020
name: "SemanticVersion",
21+
platforms: [.macOS("13.0")],
2122
products: [
2223
.library(
2324
name: "SemanticVersion",

Sources/SemanticVersion/NSRegularExpression+ext.swift

Lines changed: 0 additions & 48 deletions
This file was deleted.
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
// Copyright Dave Verwer, Sven A. Schmidt, and other contributors.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
16+
extension SemanticVersion: LosslessStringConvertible {
17+
18+
/// Initialize a version from a string. Returns `nil` if the string is not a semantic version.
19+
/// - Parameter string: Version string.
20+
public init?(_ string: String) {
21+
guard let match = string.wholeMatch(of: semVerRegex) else { return nil }
22+
guard
23+
let major = Int(match.major),
24+
let minor = Int(match.minor),
25+
let patch = Int(match.patch)
26+
else { return nil }
27+
self = .init(major, minor, patch,
28+
match.prerelease.map(String.init) ?? "",
29+
match.buildmetadata.map(String.init) ?? "")
30+
}
31+
32+
public var description: String {
33+
let pre = preRelease.isEmpty ? "" : "-" + preRelease
34+
let bld = build.isEmpty ? "" : "+" + build
35+
return "\(major).\(minor).\(patch)\(pre)\(bld)"
36+
}
37+
}
38+
39+
40+
// Source: https://regex101.com/r/Ly7O1x/3/
41+
// Linked from https://semver.org
42+
let semVerRegex = #/
43+
^
44+
v? # SPI extension: allow leading 'v'
45+
(?<major>0|[1-9]\d*)
46+
\.
47+
(?<minor>0|[1-9]\d*)
48+
\.
49+
(?<patch>0|[1-9]\d*)
50+
(?:-
51+
(?<prerelease>
52+
(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)
53+
(?:\.
54+
(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)
55+
)
56+
*)
57+
)?
58+
(?:\+
59+
(?<buildmetadata>[0-9a-zA-Z-]+
60+
(?:\.[0-9a-zA-Z-]+)
61+
*)
62+
)?
63+
$
64+
/#

Sources/SemanticVersion/SemanticVersion.swift

Lines changed: 1 addition & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,6 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15-
import Foundation
16-
1715

1816
/// `SemanticVersion` is a struct representing a software or project version according to ["Semantic Versioning"](https://semver.org).
1917
///
@@ -50,28 +48,6 @@ public struct SemanticVersion: Equatable, Hashable {
5048
}
5149
}
5250

53-
extension SemanticVersion: LosslessStringConvertible {
54-
55-
/// Initialize a version from a string. Returns `nil` if the string is not a semantic version.
56-
/// - Parameter string: Version string.
57-
public init?(_ string: String) {
58-
let groups = semVerRegex.matchGroups(string)
59-
guard
60-
groups.count == semVerRegex.numberOfCaptureGroups,
61-
let major = Int(groups[0]),
62-
let minor = Int(groups[1]),
63-
let patch = Int(groups[2])
64-
else { return nil }
65-
self = .init(major, minor, patch, groups[3], groups[4])
66-
}
67-
68-
public var description: String {
69-
let pre = preRelease.isEmpty ? "" : "-" + preRelease
70-
let bld = build.isEmpty ? "" : "+" + build
71-
return "\(major).\(minor).\(patch)\(pre)\(bld)"
72-
}
73-
}
74-
7551

7652
extension SemanticVersion: Comparable {
7753
public static func < (lhs: SemanticVersion, rhs: SemanticVersion) -> Bool {
@@ -140,7 +116,7 @@ extension SemanticVersion.PreReleaseIdentifier: Comparable {
140116
}
141117

142118

143-
extension Array: Comparable where Element == SemanticVersion.PreReleaseIdentifier {
119+
extension Array: @retroactive Comparable where Element == SemanticVersion.PreReleaseIdentifier {
144120
public static func < (lhs: Self, rhs: Self) -> Bool {
145121
// Per section 11.4 of the semver spec, compare left to right until a
146122
// difference is found.
@@ -158,60 +134,3 @@ extension Array: Comparable where Element == SemanticVersion.PreReleaseIdentifie
158134
#if swift(>=5.5)
159135
extension SemanticVersion: Sendable {}
160136
#endif
161-
162-
163-
// Source: https://regex101.com/r/Ly7O1x/3/
164-
// Linked from https://semver.org
165-
#if swift(>=5)
166-
167-
let semVerRegex = NSRegularExpression(#"""
168-
^
169-
v? # SPI extension: allow leading 'v'
170-
(?<major>0|[1-9]\d*)
171-
\.
172-
(?<minor>0|[1-9]\d*)
173-
\.
174-
(?<patch>0|[1-9]\d*)
175-
(?:-
176-
(?<prerelease>
177-
(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)
178-
(?:\.
179-
(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)
180-
)
181-
*)
182-
)?
183-
(?:\+
184-
(?<buildmetadata>[0-9a-zA-Z-]+
185-
(?:\.[0-9a-zA-Z-]+)
186-
*)
187-
)?
188-
$
189-
"""#, options: [.allowCommentsAndWhitespace])
190-
191-
#else
192-
193-
let semVerRegex = NSRegularExpression("""
194-
^
195-
v? # SPI extension: allow leading 'v'
196-
(?<major>0|[1-9]\\d*)
197-
\\.
198-
(?<minor>0|[1-9]\\d*)
199-
\\.
200-
(?<patch>0|[1-9]\\d*)
201-
(?:-
202-
(?<prerelease>
203-
(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)
204-
(?:\\.
205-
(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)
206-
)
207-
*)
208-
)?
209-
(?:\\+
210-
(?<buildmetadata>[0-9a-zA-Z-]+
211-
(?:\\.[0-9a-zA-Z-]+)
212-
*)
213-
)?
214-
$
215-
""", options: [.allowCommentsAndWhitespace])
216-
217-
#endif

Tests/SemanticVersionTests/SemanticVersionTests.swift

Lines changed: 71 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -18,83 +18,83 @@ import XCTest
1818
final class SemanticVersionTests: XCTestCase {
1919

2020
func test_semVerRegex_valid() throws {
21-
XCTAssert(semVerRegex.matches("0.0.4"))
22-
XCTAssert(semVerRegex.matches("1.2.3"))
23-
XCTAssert(semVerRegex.matches("10.20.30"))
24-
XCTAssert(semVerRegex.matches("1.1.2-prerelease+meta"))
25-
XCTAssert(semVerRegex.matches("1.1.2+meta"))
26-
XCTAssert(semVerRegex.matches("1.1.2+meta-valid"))
27-
XCTAssert(semVerRegex.matches("1.0.0-alpha"))
28-
XCTAssert(semVerRegex.matches("1.0.0-beta"))
29-
XCTAssert(semVerRegex.matches("1.0.0-alpha.beta"))
30-
XCTAssert(semVerRegex.matches("1.0.0-alpha.beta.1"))
31-
XCTAssert(semVerRegex.matches("1.0.0-alpha.1"))
32-
XCTAssert(semVerRegex.matches("1.0.0-alpha0.valid"))
33-
XCTAssert(semVerRegex.matches("1.0.0-alpha.0valid"))
34-
XCTAssert(semVerRegex.matches("1.0.0-alpha-a.b-c-somethinglong+build.1-aef.1-its-okay"))
35-
XCTAssert(semVerRegex.matches("1.0.0-rc.1+build.1"))
36-
XCTAssert(semVerRegex.matches("2.0.0-rc.1+build.123"))
37-
XCTAssert(semVerRegex.matches("1.2.3-beta"))
38-
XCTAssert(semVerRegex.matches("10.2.3-DEV-SNAPSHOT"))
39-
XCTAssert(semVerRegex.matches("1.2.3-SNAPSHOT-123"))
40-
XCTAssert(semVerRegex.matches("1.0.0"))
41-
XCTAssert(semVerRegex.matches("2.0.0"))
42-
XCTAssert(semVerRegex.matches("1.1.7"))
43-
XCTAssert(semVerRegex.matches("2.0.0+build.1848"))
44-
XCTAssert(semVerRegex.matches("2.0.1-alpha.1227"))
45-
XCTAssert(semVerRegex.matches("1.0.0-alpha+beta"))
46-
XCTAssert(semVerRegex.matches("1.2.3----RC-SNAPSHOT.12.9.1--.12+788"))
47-
XCTAssert(semVerRegex.matches("1.2.3----R-S.12.9.1--.12+meta"))
48-
XCTAssert(semVerRegex.matches("1.2.3----RC-SNAPSHOT.12.9.1--.12"))
49-
XCTAssert(semVerRegex.matches("1.0.0+0.build.1-rc.10000aaa-kk-0.1"))
50-
XCTAssert(semVerRegex.matches("99999999999999999999999.999999999999999999.99999999999999999"))
51-
XCTAssert(semVerRegex.matches("1.0.0-0A.is.legal"))
21+
XCTAssertNotNil("0.0.4".wholeMatch(of: semVerRegex))
22+
XCTAssertNotNil("1.2.3".wholeMatch(of: semVerRegex))
23+
XCTAssertNotNil("10.20.30".wholeMatch(of: semVerRegex))
24+
XCTAssertNotNil("1.1.2-prerelease+meta".wholeMatch(of: semVerRegex))
25+
XCTAssertNotNil("1.1.2+meta".wholeMatch(of: semVerRegex))
26+
XCTAssertNotNil("1.1.2+meta-valid".wholeMatch(of: semVerRegex))
27+
XCTAssertNotNil("1.0.0-alpha".wholeMatch(of: semVerRegex))
28+
XCTAssertNotNil("1.0.0-beta".wholeMatch(of: semVerRegex))
29+
XCTAssertNotNil("1.0.0-alpha.beta".wholeMatch(of: semVerRegex))
30+
XCTAssertNotNil("1.0.0-alpha.beta.1".wholeMatch(of: semVerRegex))
31+
XCTAssertNotNil("1.0.0-alpha.1".wholeMatch(of: semVerRegex))
32+
XCTAssertNotNil("1.0.0-alpha0.valid".wholeMatch(of: semVerRegex))
33+
XCTAssertNotNil("1.0.0-alpha.0valid".wholeMatch(of: semVerRegex))
34+
XCTAssertNotNil("1.0.0-alpha-a.b-c-somethinglong+build.1-aef.1-its-okay".wholeMatch(of: semVerRegex))
35+
XCTAssertNotNil("1.0.0-rc.1+build.1".wholeMatch(of: semVerRegex))
36+
XCTAssertNotNil("2.0.0-rc.1+build.123".wholeMatch(of: semVerRegex))
37+
XCTAssertNotNil("1.2.3-beta".wholeMatch(of: semVerRegex))
38+
XCTAssertNotNil("10.2.3-DEV-SNAPSHOT".wholeMatch(of: semVerRegex))
39+
XCTAssertNotNil("1.2.3-SNAPSHOT-123".wholeMatch(of: semVerRegex))
40+
XCTAssertNotNil("1.0.0".wholeMatch(of: semVerRegex))
41+
XCTAssertNotNil("2.0.0".wholeMatch(of: semVerRegex))
42+
XCTAssertNotNil("1.1.7".wholeMatch(of: semVerRegex))
43+
XCTAssertNotNil("2.0.0+build.1848".wholeMatch(of: semVerRegex))
44+
XCTAssertNotNil("2.0.1-alpha.1227".wholeMatch(of: semVerRegex))
45+
XCTAssertNotNil("1.0.0-alpha+beta".wholeMatch(of: semVerRegex))
46+
XCTAssertNotNil("1.2.3----RC-SNAPSHOT.12.9.1--.12+788".wholeMatch(of: semVerRegex))
47+
XCTAssertNotNil("1.2.3----R-S.12.9.1--.12+meta".wholeMatch(of: semVerRegex))
48+
XCTAssertNotNil("1.2.3----RC-SNAPSHOT.12.9.1--.12".wholeMatch(of: semVerRegex))
49+
XCTAssertNotNil("1.0.0+0.build.1-rc.10000aaa-kk-0.1".wholeMatch(of: semVerRegex))
50+
XCTAssertNotNil("99999999999999999999999.999999999999999999.99999999999999999".wholeMatch(of: semVerRegex))
51+
XCTAssertNotNil("1.0.0-0A.is.legal".wholeMatch(of: semVerRegex))
5252
}
5353

5454
func test_allow_leading_v() throws {
55-
XCTAssert(semVerRegex.matches("v0.0.4"))
55+
XCTAssertNotNil("v0.0.4".wholeMatch(of: semVerRegex))
5656
}
5757

5858
func test_semVerRegex_invalid() throws {
59-
XCTAssertFalse(semVerRegex.matches("1"))
60-
XCTAssertFalse(semVerRegex.matches("1.2"))
61-
XCTAssertFalse(semVerRegex.matches("1.2.3-0123"))
62-
XCTAssertFalse(semVerRegex.matches("1.2.3-0123.0123"))
63-
XCTAssertFalse(semVerRegex.matches("1.1.2+.123"))
64-
XCTAssertFalse(semVerRegex.matches("+invalid"))
65-
XCTAssertFalse(semVerRegex.matches("-invalid"))
66-
XCTAssertFalse(semVerRegex.matches("-invalid+invalid"))
67-
XCTAssertFalse(semVerRegex.matches("-invalid.01"))
68-
XCTAssertFalse(semVerRegex.matches("alpha"))
69-
XCTAssertFalse(semVerRegex.matches("alpha.beta"))
70-
XCTAssertFalse(semVerRegex.matches("alpha.beta.1"))
71-
XCTAssertFalse(semVerRegex.matches("alpha.1"))
72-
XCTAssertFalse(semVerRegex.matches("alpha+beta"))
73-
XCTAssertFalse(semVerRegex.matches("alpha_beta"))
74-
XCTAssertFalse(semVerRegex.matches("alpha."))
75-
XCTAssertFalse(semVerRegex.matches("alpha.."))
76-
XCTAssertFalse(semVerRegex.matches("beta"))
77-
XCTAssertFalse(semVerRegex.matches("1.0.0-alpha_beta"))
78-
XCTAssertFalse(semVerRegex.matches("-alpha."))
79-
XCTAssertFalse(semVerRegex.matches("1.0.0-alpha.."))
80-
XCTAssertFalse(semVerRegex.matches("1.0.0-alpha..1"))
81-
XCTAssertFalse(semVerRegex.matches("1.0.0-alpha...1"))
82-
XCTAssertFalse(semVerRegex.matches("1.0.0-alpha....1"))
83-
XCTAssertFalse(semVerRegex.matches("1.0.0-alpha.....1"))
84-
XCTAssertFalse(semVerRegex.matches("1.0.0-alpha......1"))
85-
XCTAssertFalse(semVerRegex.matches("1.0.0-alpha.......1"))
86-
XCTAssertFalse(semVerRegex.matches("01.1.1"))
87-
XCTAssertFalse(semVerRegex.matches("1.01.1"))
88-
XCTAssertFalse(semVerRegex.matches("1.1.01"))
89-
XCTAssertFalse(semVerRegex.matches("1.2"))
90-
XCTAssertFalse(semVerRegex.matches("1.2.3.DEV"))
91-
XCTAssertFalse(semVerRegex.matches("1.2-SNAPSHOT"))
92-
XCTAssertFalse(semVerRegex.matches("1.2.31.2.3----RC-SNAPSHOT.12.09.1--..12+788"))
93-
XCTAssertFalse(semVerRegex.matches("1.2-RC-SNAPSHOT"))
94-
XCTAssertFalse(semVerRegex.matches("-1.0.3-gamma+b7718"))
95-
XCTAssertFalse(semVerRegex.matches("+justmeta"))
96-
XCTAssertFalse(semVerRegex.matches("9.8.7+meta+meta"))
97-
XCTAssertFalse(semVerRegex.matches("9.8.7-whatever+meta+meta"))
59+
XCTAssertNil("1".wholeMatch(of: semVerRegex))
60+
XCTAssertNil("1.2".wholeMatch(of: semVerRegex))
61+
XCTAssertNil("1.2.3-0123".wholeMatch(of: semVerRegex))
62+
XCTAssertNil("1.2.3-0123.0123".wholeMatch(of: semVerRegex))
63+
XCTAssertNil("1.1.2+.123".wholeMatch(of: semVerRegex))
64+
XCTAssertNil("+invalid".wholeMatch(of: semVerRegex))
65+
XCTAssertNil("-invalid".wholeMatch(of: semVerRegex))
66+
XCTAssertNil("-invalid+invalid".wholeMatch(of: semVerRegex))
67+
XCTAssertNil("-invalid.01".wholeMatch(of: semVerRegex))
68+
XCTAssertNil("alpha".wholeMatch(of: semVerRegex))
69+
XCTAssertNil("alpha.beta".wholeMatch(of: semVerRegex))
70+
XCTAssertNil("alpha.beta.1".wholeMatch(of: semVerRegex))
71+
XCTAssertNil("alpha.1".wholeMatch(of: semVerRegex))
72+
XCTAssertNil("alpha+beta".wholeMatch(of: semVerRegex))
73+
XCTAssertNil("alpha_beta".wholeMatch(of: semVerRegex))
74+
XCTAssertNil("alpha.".wholeMatch(of: semVerRegex))
75+
XCTAssertNil("alpha..".wholeMatch(of: semVerRegex))
76+
XCTAssertNil("beta".wholeMatch(of: semVerRegex))
77+
XCTAssertNil("1.0.0-alpha_beta".wholeMatch(of: semVerRegex))
78+
XCTAssertNil("-alpha.".wholeMatch(of: semVerRegex))
79+
XCTAssertNil("1.0.0-alpha..".wholeMatch(of: semVerRegex))
80+
XCTAssertNil("1.0.0-alpha..1".wholeMatch(of: semVerRegex))
81+
XCTAssertNil("1.0.0-alpha...1".wholeMatch(of: semVerRegex))
82+
XCTAssertNil("1.0.0-alpha....1".wholeMatch(of: semVerRegex))
83+
XCTAssertNil("1.0.0-alpha.....1".wholeMatch(of: semVerRegex))
84+
XCTAssertNil("1.0.0-alpha......1".wholeMatch(of: semVerRegex))
85+
XCTAssertNil("1.0.0-alpha.......1".wholeMatch(of: semVerRegex))
86+
XCTAssertNil("01.1.1".wholeMatch(of: semVerRegex))
87+
XCTAssertNil("1.01.1".wholeMatch(of: semVerRegex))
88+
XCTAssertNil("1.1.01".wholeMatch(of: semVerRegex))
89+
XCTAssertNil("1.2".wholeMatch(of: semVerRegex))
90+
XCTAssertNil("1.2.3.DEV".wholeMatch(of: semVerRegex))
91+
XCTAssertNil("1.2-SNAPSHOT".wholeMatch(of: semVerRegex))
92+
XCTAssertNil("1.2.31.2.3----RC-SNAPSHOT.12.09.1--..12+788".wholeMatch(of: semVerRegex))
93+
XCTAssertNil("1.2-RC-SNAPSHOT".wholeMatch(of: semVerRegex))
94+
XCTAssertNil("-1.0.3-gamma+b7718".wholeMatch(of: semVerRegex))
95+
XCTAssertNil("+justmeta".wholeMatch(of: semVerRegex))
96+
XCTAssertNil("9.8.7+meta+meta".wholeMatch(of: semVerRegex))
97+
XCTAssertNil("9.8.7-whatever+meta+meta".wholeMatch(of: semVerRegex))
9898
}
9999

100100
func test_init() throws {

0 commit comments

Comments
 (0)