2022-08-16 10:39:05 +02:00
|
|
|
import Foundation
|
|
|
|
|
|
|
|
enum FileSystem {
|
|
|
|
|
|
|
|
fileprivate static var fm: FileManager { .default }
|
|
|
|
|
|
|
|
static func folders(in folder: URL) throws -> [URL] {
|
|
|
|
try fm.contentsOfDirectory(at: folder, includingPropertiesForKeys: [.isDirectoryKey])
|
|
|
|
.filter { $0.isDirectory }
|
|
|
|
}
|
|
|
|
|
2022-08-16 12:26:45 +02:00
|
|
|
/**
|
|
|
|
Copy a file to the destination, creating the containing folder if needed
|
|
|
|
*/
|
2022-08-16 10:39:05 +02:00
|
|
|
static func copy(_ source: URL, to destination: URL) throws {
|
|
|
|
try destination.ensureParentFolderExistence()
|
|
|
|
try source.copy(to: destination)
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
extension URL {
|
|
|
|
|
|
|
|
func ensureParentFolderExistence() throws {
|
|
|
|
try deletingLastPathComponent().ensureFolderExistence()
|
|
|
|
}
|
|
|
|
|
|
|
|
func ensureFolderExistence() throws {
|
|
|
|
if !exists {
|
|
|
|
print("Creating directory \(path)")
|
|
|
|
try wrap(.failedToWriteFile(path)) {
|
|
|
|
try FileManager.default.createDirectory(at: self, withIntermediateDirectories: true)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var isDirectory: Bool {
|
|
|
|
(try? resourceValues(forKeys: [.isDirectoryKey]))?.isDirectory == true
|
|
|
|
}
|
|
|
|
|
|
|
|
var exists: Bool {
|
|
|
|
FileSystem.fm.fileExists(atPath: path)
|
|
|
|
}
|
|
|
|
|
|
|
|
func delete() throws {
|
|
|
|
try FileSystem.fm.removeItem(at: self)
|
|
|
|
}
|
|
|
|
|
|
|
|
func copy(to url: URL) throws {
|
|
|
|
try wrap(.failedToWriteFile(url.path)) {
|
|
|
|
if url.exists {
|
|
|
|
try url.delete()
|
|
|
|
}
|
|
|
|
try url.ensureParentFolderExistence()
|
|
|
|
try FileSystem.fm.copyItem(at: self, to: url)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
extension Data {
|
|
|
|
|
|
|
|
func createFolderAndWrite(to url: URL) throws {
|
|
|
|
try url.ensureParentFolderExistence()
|
|
|
|
// if url.exists {
|
|
|
|
// print("Overwriting \(url.path)")
|
|
|
|
// }
|
|
|
|
try wrap(.failedToWriteFile(url.path)) {
|
|
|
|
try write(to: url)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
extension String {
|
|
|
|
|
|
|
|
func createFolderAndWrite(to url: URL) throws {
|
|
|
|
try data(using: .utf8)!.createFolderAndWrite(to: url)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
extension Encodable {
|
|
|
|
|
|
|
|
func writeJSON(to url: URL) throws {
|
|
|
|
let encoder = JSONEncoder()
|
|
|
|
encoder.outputFormatting = .prettyPrinted
|
|
|
|
let content = try wrap(.failedToEncodeJSON(url.path)) {
|
|
|
|
try encoder.encode(self)
|
|
|
|
}
|
|
|
|
try content.createFolderAndWrite(to: url)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
extension Decodable {
|
|
|
|
|
|
|
|
init(decodeFrom url: URL) throws {
|
|
|
|
let data = try wrap(.failedToOpenFile(url.path)) {
|
|
|
|
try Data(contentsOf: url)
|
|
|
|
}
|
|
|
|
self = try wrap({ .failedToDecodeJSON(file: url.path, error: $0.localizedDescription)}) {
|
|
|
|
try JSONDecoder().decode(Self.self, from: data)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|