TeraRestoreRecoveryStore.swift (2299B)
1 import Foundation 2 3 /// Each action is explicit and bounded; no worker authorizes restored delivery. 4 @MainActor 5 final class TeraRestoreRecoveryStore: ObservableObject { 6 @Published private(set) var status: TeraRestoreStatus? 7 @Published private(set) var isWorking = false 8 @Published private(set) var message: String? 9 @Published private(set) var reviewedInventory: String? 10 private let client: TeraRuntimeClient 11 private var generation = TeraSessionGeneration.initial 12 13 init(client: TeraRuntimeClient) { 14 self.client = client 15 } 16 17 func invalidate() { 18 generation = generation.invalidated() 19 status = nil 20 message = nil 21 reviewedInventory = nil 22 } 23 24 func load() async { 25 await perform(.load) 26 } 27 28 func checkNext() async { 29 await perform(.check) 30 } 31 32 func review() async { 33 await perform(.review) 34 } 35 36 func resume() async { 37 await perform(.resume) 38 } 39 40 private enum Action { case load, check, review, resume } 41 42 private func perform(_ action: Action) async { 43 guard !isWorking, generation.isActive else { return } 44 isWorking = true 45 let selected = generation 46 defer { isWorking = false } 47 do { 48 let digest = try await execute(action) 49 let current = try await client.restoreStatus() 50 guard generation == selected, !Task.isCancelled else { return } 51 status = current 52 reviewedInventory = digest 53 message = nil 54 } catch { 55 guard generation == selected, !Task.isCancelled else { return } 56 reviewedInventory = nil 57 message = "The recovery check could not finish. Saved work remains on this device; check again before resuming." 58 } 59 } 60 61 private func execute(_ action: Action) async throws -> String? { 62 switch action { 63 case .load: return nil 64 case .check: 65 guard let target = status?.targets.first(where: { $0.observation == nil }) 66 ?? status?.targets.first(where: { $0.observation == .incomplete }) else { return nil } 67 try await client.reconcileRestoredTarget(target) 68 return nil 69 case .review: return try await client.reviewRestoredWork() 70 case .resume: 71 guard let reviewedInventory else { throw TeraComposerAcknowledgment.unconfirmed } 72 try await client.resumeRestoredWork(reviewedInventory: reviewedInventory) 73 return nil 74 } 75 } 76 }