velody/packages/apple/VelodyPersistence/Sources/VelodyPersistence/RemoteLibrarySyncCursorStore.swift

95 lines
2.7 KiB
Swift

import Foundation
import VelodyDomain
public protocol RemoteLibrarySyncCursorStore: Actor {
func loadCursor() async throws -> SyncCursor?
func saveCursor(_ cursor: SyncCursor) async throws
func clearCursor() async throws
}
private struct StoredRemoteLibrarySyncCursor: Codable {
var value: String
}
public actor FileRemoteLibrarySyncCursorStore: RemoteLibrarySyncCursorStore {
private let fileURL: URL
private let fileManager: FileManager
private let encoder = JSONEncoder()
private let decoder = JSONDecoder()
public init(
fileURL: URL? = nil,
fileManager: FileManager = .default
) throws {
self.fileManager = fileManager
if let fileURL {
self.fileURL = fileURL
} else {
self.fileURL = try Self.defaultFileURL(fileManager: fileManager)
}
}
public func loadCursor() async throws -> SyncCursor? {
guard fileManager.fileExists(atPath: fileURL.path) else {
return nil
}
let data = try Data(contentsOf: fileURL)
let storedCursor = try decoder.decode(StoredRemoteLibrarySyncCursor.self, from: data)
return SyncCursor(value: storedCursor.value)
}
public func saveCursor(_ cursor: SyncCursor) async throws {
try fileManager.createDirectory(
at: fileURL.deletingLastPathComponent(),
withIntermediateDirectories: true
)
let data = try encoder.encode(
StoredRemoteLibrarySyncCursor(value: cursor.value)
)
try data.write(to: fileURL, options: .atomic)
}
public func clearCursor() async throws {
guard fileManager.fileExists(atPath: fileURL.path) else {
return
}
try fileManager.removeItem(at: fileURL)
}
private static func defaultFileURL(fileManager: FileManager) throws -> URL {
guard let applicationSupportURL = fileManager.urls(
for: .applicationSupportDirectory,
in: .userDomainMask
).first else {
throw CocoaError(.fileNoSuchFile)
}
return applicationSupportURL
.appendingPathComponent("Velody", isDirectory: true)
.appendingPathComponent("remote-library-sync-cursor.json")
}
}
public actor InMemoryRemoteLibrarySyncCursorStore: RemoteLibrarySyncCursorStore {
private var cursor: SyncCursor?
public init(cursor: SyncCursor? = nil) {
self.cursor = cursor
}
public func loadCursor() async throws -> SyncCursor? {
cursor
}
public func saveCursor(_ cursor: SyncCursor) async throws {
self.cursor = cursor
}
public func clearCursor() async throws {
cursor = nil
}
}