Skip to content

Commit 6d4b9f7

Browse files
committed
Migrate code to use Swift Regex
1 parent 150b709 commit 6d4b9f7

File tree

10 files changed

+84
-92
lines changed

10 files changed

+84
-92
lines changed

Sources/Build/BuildOperation.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ import Foundation
2424
import class TSCBasic.DiagnosticsEngine
2525
import protocol TSCBasic.OutputByteStream
2626
import class Basics.AsyncProcess
27-
import struct TSCBasic.RegEx
2827

2928
import enum TSCUtility.Diagnostics
3029

@@ -834,7 +833,7 @@ public final class BuildOperation: PackageStructureDelegate, SPMBuildCore.BuildS
834833
guard let _ = self._buildPlan?.targets.first(where: { $0.module.name == target }) else { return nil }
835834

836835
// Check for cases involving modules that cannot be found.
837-
if let importedModule = try? RegEx(pattern: "no such module '(.+)'").matchGroups(in: message).first?.first {
836+
if let importedModule = message.firstMatch(of: #/no such module '(?<module>.+)'/#)?.module {
838837
// A target is importing a module that can't be found. We take a look at the build plan and see if can offer any advice.
839838

840839
// Look for a target with the same module name as the one that's being imported.

Sources/Build/LLBuildProgressTracker.swift

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ import SPMBuildCore
2020
import SPMLLBuild
2121

2222
import protocol TSCBasic.OutputByteStream
23-
import struct TSCBasic.RegEx
2423
import class TSCBasic.ThreadSafeOutputByteStream
2524

2625
import class TSCUtility.IndexStoreAPI
@@ -448,11 +447,13 @@ final class LLBuildProgressTracker: LLBuildBuildSystemDelegate, SwiftCompilerOut
448447
// next we want to try and scoop out any errors from the output (if reasonable size, otherwise this
449448
// will be very slow), so they can later be passed to the advice provider in case of failure.
450449
if output.utf8.count < 1024 * 10 {
451-
let regex = try! RegEx(pattern: #".*(error:[^\n]*)\n.*"#, options: .dotMatchesLineSeparators)
452-
for match in regex.matchGroups(in: output) {
453-
self.errorMessagesByTarget[parser.targetName] = (
454-
self.errorMessagesByTarget[parser.targetName] ?? []
455-
) + [match[0]]
450+
let regex = #/.*(?<error>error:[^\n]*)\n.*/#.dotMatchesNewlines()
451+
for match in output.matches(of: regex) {
452+
self
453+
.errorMessagesByTarget[parser.targetName] = (
454+
self
455+
.errorMessagesByTarget[parser.targetName] ?? []
456+
) + [String(match.error)]
456457
}
457458
}
458459
}

Sources/PackageCollections/Providers/GitHubPackageMetadataProvider.swift

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -234,23 +234,11 @@ struct GitHubPackageMetadataProvider: PackageMetadataProvider, Closable {
234234

235235
// FIXME: use URL instead of string
236236
internal static func apiURL(_ url: String) -> URL? {
237-
do {
238-
let regex = try NSRegularExpression(pattern: #"([^/@]+)[:/]([^:/]+)/([^/.]+)(\.git)?$"#, options: .caseInsensitive)
239-
if let match = regex.firstMatch(in: url, options: [], range: NSRange(location: 0, length: url.count)) {
240-
if let hostRange = Range(match.range(at: 1), in: url),
241-
let ownerRange = Range(match.range(at: 2), in: url),
242-
let repoRange = Range(match.range(at: 3), in: url) {
243-
let host = String(url[hostRange])
244-
let owner = String(url[ownerRange])
245-
let repo = String(url[repoRange])
246-
247-
return URL(string: "https://\(Self.apiHostPrefix)\(host)/repos/\(owner)/\(repo)")
248-
}
249-
}
250-
return nil
251-
} catch {
237+
let regex = #/(?<host>[^/@]+)[:/](?<owner>[^:/]+)/(?<repo>[^/.]+)(\.git)?$/#.ignoresCase()
238+
guard let match = url.firstMatch(of: regex) else {
252239
return nil
253240
}
241+
return URL(string: "https://\(Self.apiHostPrefix)\(match.host)/repos/\(match.owner)/\(match.repo)")
254242
}
255243

256244
private func makeRequestOptions(validResponseCodes: [Int]) -> LegacyHTTPClientRequest.Options {

Sources/PackageLoading/ManifestJSONParser.swift

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ import struct Basics.InternalError
2020
import struct Basics.RelativePath
2121

2222
import enum TSCBasic.PathValidationError
23-
import struct TSCBasic.RegEx
2423
import struct TSCBasic.StringError
2524

2625
import struct TSCUtility.Version
@@ -254,7 +253,7 @@ enum ManifestJSONParser {
254253
}
255254

256255
/// Looks for Xcode-style build setting macros "$()".
257-
fileprivate static let invalidValueRegex = try! RegEx(pattern: #"(\$\(.*?\))"#)
256+
fileprivate static let invalidValueRegex = #/(\$\(.*?\))/#
258257
}
259258

260259
extension SystemPackageProviderDescription {
@@ -488,7 +487,7 @@ extension TargetBuildSettingDescription.Kind {
488487
static func from(_ name: String, values: [String]) throws -> Self {
489488
// Diagnose invalid values.
490489
for item in values {
491-
let groups = ManifestJSONParser.invalidValueRegex.matchGroups(in: item).flatMap{ $0 }
490+
let groups = item.matches(of: ManifestJSONParser.invalidValueRegex).map { $0.1 }
492491
if !groups.isEmpty {
493492
let error = "the build setting '\(name)' contains invalid component(s): \(groups.joined(separator: " "))"
494493
throw ManifestParseError.runtimeManifestErrors([error])

Sources/PackageLoading/Target+PkgConfig.swift

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ import Basics
1414
import PackageModel
1515

1616
import class Basics.AsyncProcess
17-
import struct TSCBasic.RegEx
1817

1918
import enum TSCUtility.Platform
2019

@@ -318,14 +317,14 @@ public func removeDefaultFlags(cFlags: [String], libs: [String]) throws -> ([Str
318317
///
319318
/// See https://github.com/swiftlang/swift-package-manager/issues/6439 for details.
320319
public func patchSDKPaths(in flags: [String], to sdkRootPath: AbsolutePath) throws -> [String] {
321-
let sdkRegex = try! RegEx(pattern: #"^.*\.sdk(\/.*|$)"#)
320+
let sdkRegex = #/^.*\.sdk(\/.*|$)/#
322321

323322
return try ["-I", "-L"].reduce(flags) { (flags, flag) in
324323
try patch(flag: flag, in: flags) { value in
325-
guard let groups = sdkRegex.matchGroups(in: value).first else {
324+
guard let match = value.firstMatch(of: sdkRegex) else {
326325
return value
327326
}
328-
return sdkRootPath.pathString + groups[0]
327+
return sdkRootPath.pathString + match.1
329328
}
330329
}
331330
}

Sources/PackageLoading/ToolsVersionParser.swift

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ import Foundation
1515
import PackageModel
1616

1717
import struct TSCBasic.ByteString
18-
import struct TSCBasic.RegEx
1918

2019
import struct TSCUtility.Version
2120

@@ -621,19 +620,27 @@ extension ManifestLoader {
621620
do { contents = try fileSystem.getDirectoryContents(packagePath) } catch {
622621
throw ToolsVersionParser.Error.inaccessiblePackage(path: packagePath, reason: String(describing: error))
623622
}
624-
let regex = try! RegEx(pattern: #"^Package@swift-(\d+)(?:\.(\d+))?(?:\.(\d+))?.swift$"#)
625-
623+
let regex = #/^Package@swift-(?<major>\d+)(?:\.(?<minor>\d+))?(?:\.(?<patch>\d+))?.swift$/#
626624
// Collect all version-specific manifests at the given package path.
627625
let versionSpecificManifests = Dictionary(contents.compactMap{ file -> (ToolsVersion, String)? in
628-
let parsedVersion = regex.matchGroups(in: file)
629-
guard parsedVersion.count == 1, parsedVersion[0].count == 3 else {
626+
let parsedVersionMatches = file.matches(of: regex)
627+
guard parsedVersionMatches.count == 1,
628+
let parsedVersion = parsedVersionMatches.first else {
630629
return nil
631630
}
632-
633-
let major = Int(parsedVersion[0][0])!
634-
let minor = parsedVersion[0][1].isEmpty ? 0 : Int(parsedVersion[0][1])!
635-
let patch = parsedVersion[0][2].isEmpty ? 0 : Int(parsedVersion[0][2])!
636-
631+
let major = Int(parsedVersion.major) ?? 0
632+
let minor: Int
633+
if let minorString = parsedVersion.minor {
634+
minor = Int(minorString) ?? 0
635+
} else {
636+
minor = 0
637+
}
638+
let patch: Int
639+
if let patchString = parsedVersion.patch {
640+
patch = Int(patchString) ?? 0
641+
} else {
642+
patch = 0
643+
}
637644
return (ToolsVersion(version: Version(major, minor, patch)), file)
638645
}, uniquingKeysWith: { $1 })
639646

Sources/PackageModel/SwiftLanguageVersion.swift

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,6 @@
1212

1313
import Foundation
1414

15-
import struct TSCBasic.RegEx
16-
1715
import struct TSCUtility.Version
1816

1917
/// Represents a Swift language version.
@@ -63,21 +61,31 @@ public struct SwiftLanguageVersion: Hashable, Sendable {
6361
}
6462

6563
/// Regex for parsing the Swift language version.
66-
private static let regex = try! RegEx(pattern: #"^(\d+)(?:\.(\d+))?(?:\.(\d+))?$"#)
64+
private static let regex = #/^(?<major>\d+)(?:\.(?<minor>\d+))?(?:\.(?<patch>\d+))?$/#
6765

6866
/// Create an instance of Swift language version from the given string.
6967
///
7068
// The Swift language version is not officially fixed but we require it to
7169
// be a valid SemVer-like string.
7270
public init?(string: String) {
73-
let parsedVersion = SwiftLanguageVersion.regex.matchGroups(in: string)
74-
guard parsedVersion.count == 1, parsedVersion[0].count == 3 else {
71+
let parsedVersions = string.matches(of: SwiftLanguageVersion.regex)
72+
guard parsedVersions.count == 1 else {
7573
return nil
7674
}
77-
let major = Int(parsedVersion[0][0])!
78-
let minor = parsedVersion[0][1].isEmpty ? 0 : Int(parsedVersion[0][1])!
79-
let patch = parsedVersion[0][2].isEmpty ? 0 : Int(parsedVersion[0][2])!
80-
75+
let parsedVersion = parsedVersions[0]
76+
let major = Int(parsedVersion.major) ?? 0
77+
let minor: Int
78+
if let minorString = parsedVersion.minor {
79+
minor = Int(minorString) ?? 0
80+
} else {
81+
minor = 0
82+
}
83+
let patch: Int
84+
if let patchString = parsedVersion.patch {
85+
patch = Int(patchString) ?? 0
86+
} else {
87+
patch = 0
88+
}
8189
self.rawValue = string
8290
self._version = Version(major, minor, patch)
8391
}

Sources/PackageRegistryCommand/PackageRegistryCommand+Publish.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -437,9 +437,9 @@ enum PackageArchiveSigner {
437437
}
438438
manifests.append(Manifest.filename)
439439

440-
let regex = try RegEx(pattern: #"^Package@swift-(\d+)(?:\.(\d+))?(?:\.(\d+))?.swift$"#)
440+
let regex = #/^Package@swift-(\d+)(?:\.(\d+))?(?:\.(\d+))?.swift$/#
441441
let versionSpecificManifests: [String] = packageContents.filter { file in
442-
let matchGroups = regex.matchGroups(in: file)
442+
let matchGroups = file.matches(of: regex)
443443
return !matchGroups.isEmpty
444444
}
445445
manifests.append(contentsOf: versionSpecificManifests)

Sources/SourceControl/GitRepository.swift

Lines changed: 30 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ import enum TSCBasic.FileMode
2424
import struct TSCBasic.FileSystemError
2525
import class Basics.AsyncProcess
2626
import struct Basics.AsyncProcessResult
27-
import struct TSCBasic.RegEx
2827

2928
import protocol TSCUtility.DiagnosticLocationProviding
3029
import enum TSCUtility.Git
@@ -1243,7 +1242,7 @@ public enum GitProgressParser: FetchProgress {
12431242
case resolvingDeltas(progress: Double, currentObjects: Int, totalObjects: Int)
12441243

12451244
/// The pattern used to match git output. Capture groups are labeled from ?<i0> to ?<i19>.
1246-
static let pattern = #"""
1245+
private static let regex = #/
12471246
(?xi)
12481247
(?:
12491248
remote: \h+ (?<i0>Enumerating \h objects): \h+ (?<i1>[0-9]+)
@@ -1261,66 +1260,60 @@ public enum GitProgressParser: FetchProgress {
12611260
(?<i14>Receiving \h objects): \h+ (?<i15>[0-9]+)% \h+ \((?<i16>[0-9]+)\/(?<i17>[0-9]+)\)
12621261
(?:, \h+ (?<i18>[0-9]+.?[0-9]+ \h [A-Z]iB) \h+ \| \h+ (?<i19>[0-9]+.?[0-9]+ \h [A-Z]iB\/s))?
12631262
)
1264-
"""#
1265-
static let regex = try? RegEx(pattern: pattern)
1263+
/#
12661264

12671265
init?(from string: String) {
1268-
guard let matches = GitProgressParser.regex?.matchGroups(in: string).first,
1269-
matches.count == 20 else { return nil }
1270-
1271-
if matches[0] == "Enumerating objects" {
1272-
guard let currentObjects = Int(matches[1]) else { return nil }
1273-
1266+
guard let match = try? Self.regex.firstMatch(in: string)
1267+
else { return nil }
1268+
let captures = match.output
1269+
if captures.i0 == "Enumerating objects" {
1270+
guard let currentObjects = captures.i1.flatMap({ Int($0) })
1271+
else { return nil }
12741272
self = .enumeratingObjects(currentObjects: currentObjects)
1275-
} else if matches[2] == "Counting objects" {
1276-
guard let progress = Double(matches[3]),
1277-
let currentObjects = Int(matches[4]),
1278-
let totalObjects = Int(matches[5]) else { return nil }
1279-
1273+
} else if captures.i2 == "Counting objects" {
1274+
guard let progress = captures.i3.flatMap({ Double($0) }),
1275+
let currentObjects = captures.i4.flatMap({ Int($0) }),
1276+
let totalObjects = captures.i5.flatMap({ Int($0) })
1277+
else { return nil }
12801278
self = .countingObjects(
12811279
progress: progress / 100,
12821280
currentObjects: currentObjects,
12831281
totalObjects: totalObjects
12841282
)
1285-
1286-
} else if matches[6] == "Compressing objects" {
1287-
guard let progress = Double(matches[7]),
1288-
let currentObjects = Int(matches[8]),
1289-
let totalObjects = Int(matches[9]) else { return nil }
1290-
1283+
} else if captures.i6 == "Compressing objects" {
1284+
guard let progress = captures.i7.flatMap({ Double($0) }),
1285+
let currentObjects = captures.i8.flatMap({ Int($0) }),
1286+
let totalObjects = captures.i9.flatMap({ Int($0) })
1287+
else { return nil }
12911288
self = .compressingObjects(
12921289
progress: progress / 100,
12931290
currentObjects: currentObjects,
12941291
totalObjects: totalObjects
12951292
)
1296-
1297-
} else if matches[10] == "Resolving deltas" {
1298-
guard let progress = Double(matches[11]),
1299-
let currentObjects = Int(matches[12]),
1300-
let totalObjects = Int(matches[13]) else { return nil }
1301-
1293+
} else if captures.i10 == "Resolving deltas" {
1294+
guard let progress = captures.i11.flatMap({ Double($0) }),
1295+
let currentObjects = captures.i12.flatMap({ Int($0) }),
1296+
let totalObjects = captures.i13.flatMap({ Int($0) })
1297+
else { return nil }
13021298
self = .resolvingDeltas(
13031299
progress: progress / 100,
13041300
currentObjects: currentObjects,
13051301
totalObjects: totalObjects
13061302
)
1307-
1308-
} else if matches[14] == "Receiving objects" {
1309-
guard let progress = Double(matches[15]),
1310-
let currentObjects = Int(matches[16]),
1311-
let totalObjects = Int(matches[17]) else { return nil }
1312-
1313-
let downloadProgress = matches[18]
1314-
let downloadSpeed = matches[19]
1315-
1303+
} else if captures.i14 == "Receiving objects" {
1304+
guard let progress = captures.i15.flatMap({ Double($0) }),
1305+
let currentObjects = captures.i16.flatMap({ Int($0) }),
1306+
let totalObjects = captures.i17.flatMap({ Int($0) })
1307+
else { return nil }
1308+
let downloadProgress = captures.i18.map(String.init)
1309+
let downloadSpeed = captures.i19.map(String.init)
13161310
self = .receivingObjects(
13171311
progress: progress / 100,
13181312
currentObjects: currentObjects,
13191313
totalObjects: totalObjects,
13201314
downloadProgress: downloadProgress,
13211315
downloadSpeed: downloadSpeed
13221316
)
1323-
13241317
} else {
13251318
return nil
13261319
}

Sources/Workspace/PackageContainer/SourceControlPackageContainer.swift

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,6 @@ import PackageLoading
2020
import PackageModel
2121
import SourceControl
2222

23-
import struct TSCBasic.RegEx
24-
2523
import enum TSCUtility.Git
2624
import struct TSCUtility.Version
2725

@@ -280,8 +278,8 @@ internal final class SourceControlPackageContainer: PackageContainer, CustomStri
280278
)
281279
} else {
282280
// Revision does not exist, so we customize the error.
283-
let sha1RegEx = try! RegEx(pattern: #"\A[:xdigit:]{40}\Z"#)
284-
let isBranchRev = sha1RegEx.matchGroups(in: revision).compactMap { $0 }.isEmpty
281+
let sha1RegEx = #/\A[:xdigit:]{40}\Z/#
282+
let isBranchRev = revision.matches(of: sha1RegEx).isEmpty
285283
let errorMessage = "could not find " + (isBranchRev ? "a branch named ‘\(revision)" : "the commit \(revision)")
286284
let mainBranchExists = (try? repository.resolveRevision(identifier: "main")) != nil
287285
let suggestion = (revision == "master" && mainBranchExists) ? "did you mean ‘main’?" : nil

0 commit comments

Comments
 (0)