From 9a4146535182c660f0d91248cad98d8f7fceb9e6 Mon Sep 17 00:00:00 2001 From: "Alkenso (Vladimir Vashurkin)" Date: Thu, 19 Dec 2024 08:58:08 +0200 Subject: [PATCH] Introduce FileManager.uniqueFile to generate file with unique name --- .../Extensions - FileManager.swift | 35 +++++++++++++++++++ .../FileManagerTests.swift | 27 ++++++++++++++ 2 files changed, 62 insertions(+) rename Tests/SpellbookTests/{Extensions Tests => Filesystem & Bundle}/FileManagerTests.swift (64%) diff --git a/Sources/SpellbookFoundation/Filesystem & Bundle/Extensions - FileManager.swift b/Sources/SpellbookFoundation/Filesystem & Bundle/Extensions - FileManager.swift index 917930e..1162c1b 100644 --- a/Sources/SpellbookFoundation/Filesystem & Bundle/Extensions - FileManager.swift +++ b/Sources/SpellbookFoundation/Filesystem & Bundle/Extensions - FileManager.swift @@ -171,6 +171,41 @@ extension FileManager { } } +extension FileManager { + /// Generates file with name based on string generated by `nameGenerator` call + /// and checks that the file does not exist on the file system. + /// + /// - Note: The call does NOT create any files. + public func uniqueFile(in directory: URL, _ nameGenerator: (_ attempt: Int) -> String) -> URL { + for attempt in 0.. URL { + let pathExtension = name.pathExtension + return uniqueFile(in: directory) { attempt in + guard attempt > 0 else { return name } + var nextName = "\(name.deletingPathExtension)_\(attempt)" + if !pathExtension.isEmpty { + nextName += ".\(pathExtension)" + } + return nextName + } + } +} + extension stat { public var fileType: URLFileResourceType { URLFileResourceType(mode: st_mode) diff --git a/Tests/SpellbookTests/Extensions Tests/FileManagerTests.swift b/Tests/SpellbookTests/Filesystem & Bundle/FileManagerTests.swift similarity index 64% rename from Tests/SpellbookTests/Extensions Tests/FileManagerTests.swift rename to Tests/SpellbookTests/Filesystem & Bundle/FileManagerTests.swift index 361cfe6..4518e09 100644 --- a/Tests/SpellbookTests/Extensions Tests/FileManagerTests.swift +++ b/Tests/SpellbookTests/Filesystem & Bundle/FileManagerTests.swift @@ -59,4 +59,31 @@ class FileManagerExtensionsTests: XCTestCase { try FileManager.default.removeXattr(at: file, name: "xa1") XCTAssertEqual(try FileManager.default.listXattr(at: file), ["xa2"]) } + + func test_uniqueFile() { + let fm = FileManager.default + func createFile(_ name: String) -> Bool { + fm.createFile(atPath: tempDir.location.appendingPathComponent(name).path, contents: .random(20)) + } + + XCTAssertEqual(fm.uniqueFile("test.foo", in: tempDir.location).lastPathComponent, "test.foo") + // Ensure it is not created. + XCTAssertEqual(fm.uniqueFile("test.foo", in: tempDir.location).lastPathComponent, "test.foo") + + XCTAssertTrue(createFile("test.foo")) + XCTAssertEqual(fm.uniqueFile("test.foo", in: tempDir.location).lastPathComponent, "test_1.foo") + + XCTAssertTrue(createFile("test.foo.bar")) + XCTAssertEqual(fm.uniqueFile("test.foo.bar", in: tempDir.location).lastPathComponent, "test.foo_1.bar") + + XCTAssertEqual(fm.uniqueFile("test", in: tempDir.location).lastPathComponent, "test") + XCTAssertTrue(createFile("test")) + XCTAssertTrue(createFile("test_1")) + XCTAssertTrue(createFile("test_3")) + XCTAssertEqual(fm.uniqueFile("test", in: tempDir.location).lastPathComponent, "test_2") + + XCTAssertEqual(fm.uniqueFile("dir/", in: tempDir.location).lastPathComponent, "dir") + XCTAssertTrue(createFile("dir")) + XCTAssertEqual(fm.uniqueFile("dir/", in: tempDir.location).lastPathComponent, "dir_1") + } }