Improve storage, paths

This commit is contained in:
Christoph Hagen
2024-12-16 21:01:38 +01:00
parent b22b76fd32
commit 849585acc7
19 changed files with 393 additions and 268 deletions

View File

@@ -1,44 +1,5 @@
import Foundation
enum SecurityScopeBookmark: String {
case outputPath = "outputPathBookmark"
case contentPath = "contentPathBookmark"
}
enum StorageAccessError: Error {
case noBookmarkData
case bookmarkDataCorrupted(Error)
case folderAccessFailed(URL)
case stringConversionFailed
case fileNotFound(String)
}
extension StorageAccessError: CustomStringConvertible {
var description: String {
switch self {
case .noBookmarkData:
return "No bookmark data to access resources in folder"
case .bookmarkDataCorrupted(let error):
return "Failed to resolve bookmark: \(error)"
case .folderAccessFailed(let url):
return "Failed to access folder: \(url.path())"
case .stringConversionFailed:
return "Failed to convert string to data"
case .fileNotFound(let path):
return "File not found: \(path)"
}
}
}
/**
A class that handles the storage of the website data.
@@ -48,11 +9,10 @@ extension StorageAccessError: CustomStringConvertible {
- files: Contains additional files
- videos: Contains raw video files
- posts: Contains the markdown files for localized posts, file name is the post id
-
*/
final class Storage {
private(set) var baseFolder: URL
- Note: The base folder and output folder are stored as security-scoped bookmarks in user defaults.
*/
final class Storage: ObservableObject {
private let encoder = JSONEncoder()
@@ -60,20 +20,21 @@ final class Storage {
private let fm = FileManager.default
@Published
var contentFolderStatus: SecurityScopeStatus = .noBookmark
@Published
var outputFolderStatus: SecurityScopeStatus = .noBookmark
/**
Create the storage.
*/
init(baseFolder: URL) {
self.baseFolder = baseFolder
init() {
encoder.outputFormatting = [.prettyPrinted, .sortedKeys]
}
// MARK: Helper
private func subFolder(_ name: String) -> URL {
baseFolder.appending(path: name, directoryHint: .isDirectory)
}
private func files(in folder: URL) throws -> [URL] {
do {
return try fm.contentsOfDirectory(at: folder, includingPropertiesForKeys: [.isDirectoryKey])
@@ -96,8 +57,7 @@ final class Storage {
// MARK: Folders
func update(baseFolder: URL) throws {
self.baseFolder = baseFolder
func updateBaseFolder() throws {
try createFolderStructure()
}
@@ -447,6 +407,19 @@ final class Storage {
}
}
func create(folder relativePath: String, in scopr: SecurityScopeBookmark) -> Bool {
return write(in: .outputPath) { folder in
let url = folder.appendingPathComponent(relativePath, isDirectory: true)
do {
try url.ensureFolderExistence()
return true
} catch {
print("Failed to create folder \(url.path()): \(error)")
return false
}
}
}
func write(in scope: SecurityScopeBookmark, operation: (URL) -> Bool) -> Bool {
do {
return try operate(in: scope, operation: operation)
@@ -486,6 +459,7 @@ final class Storage {
if isStale {
print("Bookmark is stale, consider saving a new bookmark.")
#warning("Show warning about stale bookmark")
}
// Start accessing the security-scoped resource
@@ -496,6 +470,45 @@ final class Storage {
return try operation(folderUrl)
}
@discardableResult
func check(contentPath: String) -> SecurityScopeStatus {
contentFolderStatus = Storage.ensure(securityScope: .contentPath, matches: contentPath)
return contentFolderStatus
}
@discardableResult
func check(outputPath: String) -> SecurityScopeStatus {
outputFolderStatus = Storage.ensure(securityScope: .outputPath, matches: outputPath)
return outputFolderStatus
}
private static func ensure(securityScope: SecurityScopeBookmark, matches path: String) -> SecurityScopeStatus {
guard path != "" else {
return .noPath
}
guard let bookmarkData = UserDefaults.standard.data(forKey: securityScope.rawValue) else {
return .noBookmark
}
do {
var isStale = false
let url = try URL(
resolvingBookmarkData: bookmarkData,
options: .withSecurityScope,
relativeTo: nil,
bookmarkDataIsStale: &isStale)
guard !isStale else {
return .stale
}
guard url.path() == path else {
return .urlMismatch
}
return .nominal
} catch {
return .bookmarkCorrupted
}
}
// MARK: Writing files
/**