From 027727b7e3d9e69d5f8534dd1119f3aa7d0fa012 Mon Sep 17 00:00:00 2001 From: fortmarek Date: Mon, 11 Nov 2024 10:19:06 +0100 Subject: [PATCH] fix: glob with extension options --- Sources/FileSystem/FileSystem.swift | 18 ++++++++++++++ Tests/FileSystemTests/FileSystemTests.swift | 26 +++++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/Sources/FileSystem/FileSystem.swift b/Sources/FileSystem/FileSystem.swift index f734552..49092a8 100644 --- a/Sources/FileSystem/FileSystem.swift +++ b/Sources/FileSystem/FileSystem.swift @@ -590,6 +590,23 @@ public struct FileSystem: FileSysteming, Sendable { } } + private func expandBraces(in regexString: String) throws -> [String] { + let pattern = #"\{[^}]+\}"# + let regex = try Regex(pattern) + + guard let match = regexString.firstMatch(of: regex) else { + return [regexString] + } + + return regexString[match.range] + .dropFirst() + .dropLast() + .split(separator: ",") + .map { option in + regexString.replacingCharacters(in: match.range, with: option) + } + } + public func glob(directory: Path.AbsolutePath, include: [String]) throws -> AnyThrowingAsyncSequenceable { let logMessage = "Looking up files and directories from \(directory.pathString) that match the glob patterns \(include.joined(separator: ", "))." @@ -597,6 +614,7 @@ public struct FileSystem: FileSysteming, Sendable { return Glob.search( directory: URL(string: directory.pathString)!, include: try include + .flatMap { try expandBraces(in: $0) } .map { try Pattern($0) }, exclude: [ try Pattern("**/.DS_Store"), diff --git a/Tests/FileSystemTests/FileSystemTests.swift b/Tests/FileSystemTests/FileSystemTests.swift index 0f50767..a818573 100644 --- a/Tests/FileSystemTests/FileSystemTests.swift +++ b/Tests/FileSystemTests/FileSystemTests.swift @@ -887,6 +887,32 @@ final class FileSystemTests: XCTestCase, @unchecked Sendable { } } + func test_glob_with_extension_group() async throws { + try await subject.runInTemporaryDirectory(prefix: "FileSystem") { temporaryDirectory in + // Given + let swiftSourceFile = temporaryDirectory.appending(component: "file.swift") + let cppSourceFile = temporaryDirectory.appending(component: "file.cpp") + let jsSourceFile = temporaryDirectory.appending(component: "file.js") + try await subject.touch(swiftSourceFile) + try await subject.touch(cppSourceFile) + try await subject.touch(jsSourceFile) + + // When + let got = try await subject.glob(directory: temporaryDirectory, include: ["*.{swift,cpp}"]) + .collect() + .sorted() + + // Then + XCTAssertEqual( + got, + [ + cppSourceFile, + swiftSourceFile, + ] + ) + } + } + func test_remove_file() async throws { try await subject.runInTemporaryDirectory(prefix: "FileSystem") { temporaryDirectory in // Given