73 lines
2.5 KiB
Swift
73 lines
2.5 KiB
Swift
|
|
import Foundation
|
||
|
|
import SaplingCore
|
||
|
|
|
||
|
|
public struct WorkspaceMetadata: Hashable, Codable, Sendable {
|
||
|
|
public var workspaceID: UUID
|
||
|
|
public var lastOpenedAt: Date
|
||
|
|
public var selectedDocumentURL: URL?
|
||
|
|
public var expandedItemIDs: Set<UUID>
|
||
|
|
|
||
|
|
public init(
|
||
|
|
workspaceID: UUID,
|
||
|
|
lastOpenedAt: Date = Date(),
|
||
|
|
selectedDocumentURL: URL? = nil,
|
||
|
|
expandedItemIDs: Set<UUID> = []
|
||
|
|
) {
|
||
|
|
self.workspaceID = workspaceID
|
||
|
|
self.lastOpenedAt = lastOpenedAt
|
||
|
|
self.selectedDocumentURL = selectedDocumentURL
|
||
|
|
self.expandedItemIDs = expandedItemIDs
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
public protocol WorkspaceMetadataStore: Sendable {
|
||
|
|
func loadMetadata(for workspaceID: UUID) throws -> WorkspaceMetadata?
|
||
|
|
func saveMetadata(_ metadata: WorkspaceMetadata) throws
|
||
|
|
}
|
||
|
|
|
||
|
|
public final class InMemoryWorkspaceMetadataStore: WorkspaceMetadataStore, @unchecked Sendable {
|
||
|
|
private var metadataByWorkspaceID: [UUID: WorkspaceMetadata]
|
||
|
|
|
||
|
|
public init(metadataByWorkspaceID: [UUID: WorkspaceMetadata] = [:]) {
|
||
|
|
self.metadataByWorkspaceID = metadataByWorkspaceID
|
||
|
|
}
|
||
|
|
|
||
|
|
public func loadMetadata(for workspaceID: UUID) throws -> WorkspaceMetadata? {
|
||
|
|
metadataByWorkspaceID[workspaceID]
|
||
|
|
}
|
||
|
|
|
||
|
|
public func saveMetadata(_ metadata: WorkspaceMetadata) throws {
|
||
|
|
metadataByWorkspaceID[metadata.workspaceID] = metadata
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
public final class JSONWorkspaceMetadataStore: WorkspaceMetadataStore {
|
||
|
|
private let metadataDirectory: URL
|
||
|
|
private let encoder: JSONEncoder
|
||
|
|
private let decoder: JSONDecoder
|
||
|
|
|
||
|
|
public init(metadataDirectory: URL) {
|
||
|
|
self.metadataDirectory = metadataDirectory
|
||
|
|
self.encoder = JSONEncoder()
|
||
|
|
self.decoder = JSONDecoder()
|
||
|
|
encoder.outputFormatting = [.prettyPrinted, .sortedKeys]
|
||
|
|
}
|
||
|
|
|
||
|
|
public func loadMetadata(for workspaceID: UUID) throws -> WorkspaceMetadata? {
|
||
|
|
let url = fileURL(for: workspaceID)
|
||
|
|
guard FileManager.default.fileExists(atPath: url.path) else { return nil }
|
||
|
|
let data = try Data(contentsOf: url)
|
||
|
|
return try decoder.decode(WorkspaceMetadata.self, from: data)
|
||
|
|
}
|
||
|
|
|
||
|
|
public func saveMetadata(_ metadata: WorkspaceMetadata) throws {
|
||
|
|
try FileManager.default.createDirectory(at: metadataDirectory, withIntermediateDirectories: true)
|
||
|
|
let data = try encoder.encode(metadata)
|
||
|
|
try data.write(to: fileURL(for: metadata.workspaceID), options: [.atomic])
|
||
|
|
}
|
||
|
|
|
||
|
|
private func fileURL(for workspaceID: UUID) -> URL {
|
||
|
|
metadataDirectory.appendingPathComponent("\(workspaceID.uuidString).json")
|
||
|
|
}
|
||
|
|
}
|