import Foundation public struct SaplingConfiguration: Hashable, Codable, Sendable { public var lastOpenedWorkspaceURL: URL? public var recentWorkspaceURLs: [URL] public var defaultBranchName: String public var autosavesDrafts: Bool public var showsHiddenFiles: Bool public var preferredEditorFontSize: Double public init( lastOpenedWorkspaceURL: URL? = nil, recentWorkspaceURLs: [URL] = [], defaultBranchName: String = "main", autosavesDrafts: Bool = true, showsHiddenFiles: Bool = false, preferredEditorFontSize: Double = 15 ) { self.lastOpenedWorkspaceURL = lastOpenedWorkspaceURL self.recentWorkspaceURLs = recentWorkspaceURLs self.defaultBranchName = defaultBranchName self.autosavesDrafts = autosavesDrafts self.showsHiddenFiles = showsHiddenFiles self.preferredEditorFontSize = preferredEditorFontSize } } public protocol ConfigurationStore: Sendable { func loadConfiguration() throws -> SaplingConfiguration func saveConfiguration(_ configuration: SaplingConfiguration) throws } public final class InMemoryConfigurationStore: ConfigurationStore, @unchecked Sendable { private var configuration: SaplingConfiguration public init(configuration: SaplingConfiguration = SaplingConfiguration()) { self.configuration = configuration } public func loadConfiguration() throws -> SaplingConfiguration { configuration } public func saveConfiguration(_ configuration: SaplingConfiguration) throws { self.configuration = configuration } } public final class JSONConfigurationStore: ConfigurationStore, @unchecked Sendable { private let fileURL: URL private let encoder: JSONEncoder private let decoder: JSONDecoder public init(fileURL: URL) { self.fileURL = fileURL self.encoder = JSONEncoder() self.decoder = JSONDecoder() encoder.outputFormatting = [.prettyPrinted, .sortedKeys] } public func loadConfiguration() throws -> SaplingConfiguration { guard FileManager.default.fileExists(atPath: fileURL.path) else { return SaplingConfiguration() } let data = try Data(contentsOf: fileURL) return try decoder.decode(SaplingConfiguration.self, from: data) } public func saveConfiguration(_ configuration: SaplingConfiguration) throws { try FileManager.default.createDirectory( at: fileURL.deletingLastPathComponent(), withIntermediateDirectories: true ) let data = try encoder.encode(configuration) try data.write(to: fileURL, options: [.atomic]) } }