TeraRecoveryInventory.swift (3210B)
1 import Foundation 2 3 enum TeraRecoveryOwner: Sendable, Equatable { 4 case legacy 5 case submission(TeraSubmissionRequest) 6 case repair 7 } 8 9 struct TeraRecoveryEntry: Sendable, Equatable { 10 let key: String 11 let revision: UInt64 12 let owner: TeraRecoveryOwner 13 } 14 15 struct TeraRecoveryPage: Sendable, Equatable { 16 let author: String 17 let entries: [TeraRecoveryEntry] 18 let scanned: UInt16 19 let nextCursor: String? 20 } 21 22 struct TeraNativeRecoveryProgress: Sendable, Equatable { 23 let visited: Int 24 let remaining: Int 25 let needsAttention: Bool 26 var issues: [TeraNativeRecoveryIssue] = [] 27 var pause: TeraNativeRecoveryPause? 28 } 29 30 extension TeraRuntimeClient { 31 func recoveryPage(limit: UInt16 = 32, cursor: String? = nil) async throws -> TeraRecoveryPage { 32 try await addOperation("runtime.recovery.page") { try await $0.recoveryPage(limit: limit, cursor: cursor) } 33 } 34 35 /// Keep exact identity selection and full status lookup in one admitted runtime 36 /// operation. A displayed page supplies no ownership or completeness evidence. 37 func recoveryUploadOwner(key: String) async throws -> TeraNativeUploadRecoveryOwner? { 38 try await addOperation("runtime.recovery.parent") { backend in 39 guard let entry = try await backend.recoveryParent(key: key) else { return nil } 40 guard entry.key == key else { throw TeraComposerAcknowledgment.unconfirmed } 41 switch entry.owner { 42 case .legacy: 43 let value = try await backend.draftStatus(id: key) 44 guard value.id == key, value.revision >= entry.revision else { throw TeraComposerAcknowledgment.unconfirmed } 45 return TeraNativeUploadRecoveryOwner(draft: value) 46 case let .submission(request): 47 let value = try await backend.submissionStatus(request: request) 48 guard value.intentID == key, value.request == request, value.revision >= entry.revision else { throw TeraComposerAcknowledgment.unconfirmed } 49 return TeraNativeUploadRecoveryOwner(submission: value) 50 case .repair: throw TeraNativeRecoveryFault.invalidParent 51 } 52 } 53 } 54 } 55 56 extension TeraRuntimeBackend { 57 func recoveryPage(limit _: UInt16, cursor _: String?) async throws -> TeraRecoveryPage { 58 throw TeraComposerAcknowledgment.unconfirmed 59 } 60 61 func recoveryParent(key _: String) async throws -> TeraRecoveryEntry? { 62 throw TeraComposerAcknowledgment.unconfirmed 63 } 64 } 65 66 extension TeraNativeUploadRecoveryOwner { 67 init(draft: TeraDraftStatus) { 68 self.init(id: draft.id, revision: draft.revision, media: draft.form?.media ?? [], 69 verifiedURLs: Set(draft.media.filter { $0.stage == .verified }.map(\.url)), 70 uploadURLs: Self.urls(draft.media)) 71 } 72 73 init(submission: TeraSubmissionStatus) { 74 self.init(id: submission.intentID, revision: submission.revision, media: submission.preparedMedia, 75 verifiedURLs: Set(submission.media.filter { $0.progress.stage == .verified }.map(\.progress.url)), 76 uploadURLs: Self.urls(submission.media.map(\.progress))) 77 } 78 79 private static func urls(_ media: [TeraDraftMediaStatus]) -> [String: String] { 80 media.reduce(into: [:]) { urls, item in 81 if let url = item.uploadURL { 82 urls[item.url] = url 83 } 84 } 85 } 86 }