field_ios

In-the-field app for Radroots on iOS
git clone https://radroots.dev/git/field_ios.git
Log | Files | Refs | README | LICENSE

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 }