// // CachingDiskStorage.swift // Tusker // // Created by Shadowfacts on 1/17/21. // Copyright © 2021 Shadowfacts. All rights reserved. // import Foundation import Cache /// This class wraps a `DiskStorage` and maintains an in-memory cache of whether objects /// exist on disk to avoid unnecessary disk I/O when calling synchronous methods like /// `existsObject(forKey:)`. class CachingDiskStorage: StorageAware { private let storage: DiskStorage private var files = [String: FileState]() init(config: DiskConfig, transformer: Transformer) throws { storage = try DiskStorage(config: config, transformer: transformer) } private func state(for key: String) -> FileState { return files[key] ?? .unknown } func entry(forKey key: String) throws -> Entry { if state(for: key) == .doesNotExist { throw StorageError.notFound } return try storage.entry(forKey: key) } func existsObject(forKey key: String) throws -> Bool { switch state(for: key) { case .unknown: return try storage.existsObject(forKey: key) case .exists: return true case .doesNotExist: return false } } func removeObject(forKey key: String) throws { try storage.removeObject(forKey: key) files[key] = .doesNotExist } func setObject(_ object: T, forKey key: String, expiry: Expiry? = nil) throws { try storage.setObject(object, forKey: key, expiry: expiry) files[key] = .exists } func removeAll() throws { try storage.removeAll() files.removeAll() } func removeExpiredObjects() throws { try storage.removeExpiredObjects() } } extension CachingDiskStorage { private enum FileState { case unknown, exists, doesNotExist } }