Save automatically, improve mocks

This commit is contained in:
Christoph Hagen
2025-02-05 12:24:33 +01:00
parent d41c54d174
commit 5abe6e1a9f
55 changed files with 701 additions and 381 deletions

View File

@ -1,10 +1,7 @@
import Foundation
import AppKit
protocol SecurityBookmarkErrorDelegate: AnyObject {
func securityBookmark(error: String)
}
typealias StorageErrorCallback = (StorageError) -> Void
struct SecurityBookmark {
@ -25,7 +22,7 @@ struct SecurityBookmark {
private let fm = FileManager.default
weak var delegate: SecurityBookmarkErrorDelegate?
var errorNotification: StorageErrorCallback?
init(url: URL, isStale: Bool) {
self.url = url
@ -34,6 +31,14 @@ struct SecurityBookmark {
self.encoder.outputFormatting = [.prettyPrinted, .sortedKeys]
}
private func reportError(_ error: String) {
guard let errorNotification else {
print(error)
return
}
errorNotification(.init(message: error))
}
// MARK: Write
func openFinderWindow(relativePath: String) {
@ -65,7 +70,7 @@ struct SecurityBookmark {
do {
data = try encoder.encode(value)
} catch {
delegate?.securityBookmark(error: "Failed to encode \(value): \(error)")
reportError("Failed to encode \(value): \(error)")
return false
}
return write(data, to: relativePath)
@ -79,7 +84,7 @@ struct SecurityBookmark {
createParentFolder: Bool = true,
ifFileExists overwrite: OverwriteBehaviour = .writeIfChanged) -> Bool {
guard let data = content.data(using: .utf8) else {
delegate?.securityBookmark(error: "Failed to encode content to write to \(relativePath)")
reportError("Failed to encode content to write to \(relativePath)")
return false
}
return write(data, to: relativePath, createParentFolder: createParentFolder, ifFileExists: overwrite)
@ -95,7 +100,7 @@ struct SecurityBookmark {
if exists(file) {
switch overwrite {
case .fail:
delegate?.securityBookmark(error: "Failed to write \(relativePath): File exists")
reportError("Failed to write \(relativePath): File exists")
return false
case .skip: return true
case .write: break
@ -110,7 +115,7 @@ struct SecurityBookmark {
try createParentIfNeeded(of: file)
try data.write(to: file)
} catch {
delegate?.securityBookmark(error: "Failed to write \(relativePath): \(error)")
reportError("Failed to write \(relativePath): \(error)")
return false
}
return true
@ -136,7 +141,7 @@ struct SecurityBookmark {
return nil
}
guard let result = String(data: data, encoding: .utf8) else {
delegate?.securityBookmark(error: "Failed to read \(relativePath): invalid UTF-8")
reportError("Failed to read \(relativePath): invalid UTF-8")
return nil
}
return result
@ -150,7 +155,7 @@ struct SecurityBookmark {
do {
return try Data(contentsOf: file)
} catch {
delegate?.securityBookmark(error: "Failed to read \(relativePath) \(error)")
reportError("Failed to read \(relativePath) \(error)")
return nil
}
}
@ -163,7 +168,7 @@ struct SecurityBookmark {
do {
return try decoder.decode(T.self, from: data)
} catch {
delegate?.securityBookmark(error: "Failed to decode \(relativePath): \(error)")
reportError("Failed to decode \(relativePath): \(error)")
return nil
}
}
@ -178,7 +183,7 @@ struct SecurityBookmark {
with(relativePath: relativeSource) { source in
if !exists(source) {
if !failIfMissing { return true }
delegate?.securityBookmark(error: "Failed to move \(relativeSource): File does not exist")
reportError("Failed to move \(relativeSource): File does not exist")
return false
}
@ -186,7 +191,7 @@ struct SecurityBookmark {
if exists(destination) {
switch overwrite {
case .fail:
delegate?.securityBookmark(error: "Failed to move to \(relativeDestination): File already exists")
reportError("Failed to move to \(relativeDestination): File already exists")
return false
case .skip: return true
case .write: break
@ -205,7 +210,7 @@ struct SecurityBookmark {
try fm.moveItem(at: source, to: destination)
return true
} catch {
delegate?.securityBookmark(error: "Failed to move \(source.path()) to \(destination.path()): \(error)")
reportError("Failed to move \(source.path()) to \(destination.path()): \(error)")
return false
}
}
@ -220,7 +225,7 @@ struct SecurityBookmark {
if destination.exists {
switch overwrite {
case .fail:
delegate?.securityBookmark(error: "Failed to copy to \(relativePath): File already exists")
reportError("Failed to copy to \(relativePath): File already exists")
return false
case .skip: return true
case .write: break
@ -237,7 +242,7 @@ struct SecurityBookmark {
try fm.copyItem(at: externalFile, to: destination)
return true
} catch {
delegate?.securityBookmark(error: "Failed to copy \(externalFile.path()) to \(relativePath): \(error)")
reportError("Failed to copy \(externalFile.path()) to \(relativePath): \(error)")
return false
}
}
@ -252,7 +257,7 @@ struct SecurityBookmark {
try fm.removeItem(at: file)
return true
} catch {
delegate?.securityBookmark(error: "Failed to delete \(relativePath): \(error)")
reportError("Failed to delete \(relativePath): \(error)")
return false
}
}
@ -326,13 +331,13 @@ struct SecurityBookmark {
do {
data = try Data(contentsOf: url)
} catch {
delegate?.securityBookmark(error: "Failed to read \(url.path()): \(error)")
reportError("Failed to read \(url.path()): \(error)")
return
}
do {
items[id] = try decoder.decode(T.self, from: data)
} catch {
delegate?.securityBookmark(error: "Failed to decode \(url.path()): \(error)")
reportError("Failed to decode \(url.path()): \(error)")
return
}
}
@ -352,7 +357,7 @@ struct SecurityBookmark {
func with<T>(relativePath: String, perform operation: (URL) async -> T?) async -> T? {
let path = url.appending(path: relativePath.withLeadingSlashRemoved)
guard url.startAccessingSecurityScopedResource() else {
delegate?.securityBookmark(error: "Failed to start security scope")
reportError("Failed to start security scope")
return nil
}
defer { url.stopAccessingSecurityScopedResource() }
@ -364,7 +369,7 @@ struct SecurityBookmark {
*/
func perform(_ operation: (URL) -> Bool) -> Bool {
guard url.startAccessingSecurityScopedResource() else {
delegate?.securityBookmark(error: "Failed to start security scope")
reportError("Failed to start security scope")
return false
}
defer { url.stopAccessingSecurityScopedResource() }
@ -376,7 +381,7 @@ struct SecurityBookmark {
*/
func perform<T>(_ operation: (URL) -> T?) -> T? {
guard url.startAccessingSecurityScopedResource() else {
delegate?.securityBookmark(error: "Failed to start security scope")
reportError("Failed to start security scope")
return nil
}
defer { url.stopAccessingSecurityScopedResource() }
@ -390,7 +395,7 @@ struct SecurityBookmark {
try createIfNeeded(folder)
return true
} catch {
delegate?.securityBookmark(error: "Failed to create folder \(folder.path())")
reportError("Failed to create folder \(folder.path())")
return false
}
}
@ -406,7 +411,7 @@ struct SecurityBookmark {
do {
try fm.removeItem(at: file)
} catch {
delegate?.securityBookmark(error: "Failed to delete \(file.path()): \(error)")
reportError("Failed to delete \(file.path()): \(error)")
return false
}
return true
@ -432,7 +437,7 @@ struct SecurityBookmark {
do {
return try files(in: folder).filter { !$0.lastPathComponent.hasPrefix(".") }
} catch {
delegate?.securityBookmark(error: "Failed to read list of files in \(folder.path())")
reportError("Failed to read list of files in \(folder.path())")
return nil
}
}