Restructured the folder structure in the library and test targets.

This commit is contained in:
2025-01-18 03:50:18 +01:00
parent 1466bff250
commit 1094bbb6c8
15 changed files with 0 additions and 0 deletions
@@ -0,0 +1,9 @@
import Foundation
public protocol BundleServicing {
// MARK: Functions
func url(forResource name: String?, withExtension ext: String?, subdirectory subpath: String?) -> URL?
}
@@ -0,0 +1,28 @@
import Foundation
public protocol FileServicing {
// MARK: Computed
var currentFolder: URL { get async }
// MARK: Functions
func copyFile(from source: URL, to destination: URL) async throws (FileServiceError)
func createFolder(at location: URL) async throws (FileServiceError)
func deleteItem(at location: URL) async throws (FileServiceError)
func isItemExists(at location: URL) async throws (FileServiceError) -> Bool
}
// MARK: - Errors
public enum FileServiceError: Error, Equatable {
case folderNotCreated
case itemAlreadyExists
case itemEmptyData
case itemNotCopied
case itemNotDeleted
case itemNotExists
case itemNotFileURL
}
@@ -0,0 +1,79 @@
import Foundation
public struct FileService: FileServicing {
// MARK: Properties
private let fileManager: FileManager
// MARK: Initialisers
public init(fileManager: FileManager = .default) {
self.fileManager = fileManager
}
// MARK: Computed
public var currentFolder: URL {
get async {
.init(at: fileManager.currentDirectoryPath)
}
}
// MARK: Functions
public func copyFile(from source: URL, to destination: URL) async throws (FileServiceError) {
guard try await !isItemExists(at: destination) else {
throw FileServiceError.itemAlreadyExists
}
var itemData: Data?
do {
itemData = try Data(contentsOf: source)
} catch {
throw FileServiceError.itemEmptyData
}
do {
try itemData?.write(to: destination, options: .atomic)
} catch {
throw FileServiceError.itemNotCopied
}
}
public func createFolder(at location: URL) async throws (FileServiceError) {
guard try await !isItemExists(at: location) else {
throw FileServiceError.itemAlreadyExists
}
do {
try fileManager.createDirectory(at: location, withIntermediateDirectories: true)
} catch {
throw FileServiceError.folderNotCreated
}
}
public func deleteItem(at location: URL) async throws (FileServiceError) {
guard try await isItemExists(at: location) else {
throw FileServiceError.itemNotExists
}
do {
try fileManager.removeItem(at: location)
} catch {
throw FileServiceError.itemNotDeleted
}
}
public func isItemExists(at location: URL) async throws (FileServiceError) -> Bool {
guard location.isFileURL else {
throw FileServiceError.itemNotFileURL
}
let filePath = location.pathString
return fileManager.fileExists(atPath: filePath)
}
}
@@ -0,0 +1,39 @@
import Foundation
public struct CopyFilesTask {
// MARK: Properties
private let bundleService: BundleServicing
private let fileService: FileServicing
// MARK: Initialisers
public init(
bundleService: BundleServicing? = nil,
fileService: FileServicing
) {
self.bundleService = bundleService ?? Bundle.module
self.fileService = fileService
}
// MARK: Functions
public func callAsFunction(to rootFolder: URL) async throws (FileServiceError) {
for resource in ResourceFile.allCases {
guard let source = bundleService.url(
forResource: resource.rawValue,
withExtension: nil,
subdirectory: "Resources/Files"
) else {
assertionFailure("URL should have been initialized.")
return
}
let destination = rootFolder.appendingPath(resource.fileName)
try await fileService.copyFile(from: source, to: destination)
}
}
}
@@ -0,0 +1,40 @@
import Foundation
public struct CreateFoldersTask {
// MARK: Properties
private let fileService: FileServicing
// MARK: Initialisers
public init(fileService: FileServicing) {
self.fileService = fileService
}
// MARK: Functions
public func callAsFunction(at rootFolder: URL) async throws {
let folders = Self.foldersToCreate.map { rootFolder.appendingPath($0) }
for folder in folders {
try await fileService.createFolder(at: folder)
}
}
}
// MARK: - Helpers
extension CreateFoldersTask {
// MARK: Constants
static let foldersToCreate: [String] = [
"Sources/App",
"Sources/AppInfrastructure",
"Tests/App/Cases",
"Tests/App/Sources"
]
}
@@ -0,0 +1,44 @@
import Foundation
public struct CreateRootFolderTask {
// MARK: Properties
private let fileService: FileServicing
// MARK: Initialisers
public init(fileService: FileServicing) {
self.fileService = fileService
}
// MARK: Functions
public func callAsFunction(
name: String,
at location: URL? = nil
) async throws -> URL {
guard !name.isEmpty else {
throw CreateRootFolderError.nameIsEmpty
}
let rootFolder = if let location {
location
} else {
await fileService.currentFolder
}
let newFolder = rootFolder.appendingPath(name)
try await fileService.createFolder(at: newFolder)
return newFolder
}
}
// MARK: - Errors
public enum CreateRootFolderError: Error {
case nameIsEmpty
}