Sapling/Sources/SaplingStorage/WorkspaceMetadataStore.swift

73 lines
2.5 KiB
Swift
Raw Normal View History

2026-05-29 15:19:33 +02:00
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")
}
}