TeraPublicationRetry.swift (3509B)
1 import Foundation 2 import TeraKitBindings 3 4 enum TeraPublicationActionReason: Sendable, Equatable { 5 case coordinateChanged 6 case deadlineExceeded, attemptLimit, authenticationRequired, quotaExceeded 7 case invalidPayload, deliveryRefused 8 9 var explanation: String { 10 switch self { 11 case .coordinateChanged: "Another request owns this address, the saved timestamp does not win against its known revision, or the clock is behind the captured time. Review the current revision and device clock before publishing. Your captured form and signed evidence are retained." 12 case .deadlineExceeded: "The saved delivery window does not allow another attempt. The captured form and any signed publication or relay evidence are retained. Review them before choosing another submission." 13 case .attemptLimit: "The delivery attempt limit was reached. The saved publication and relay evidence are retained for review." 14 case .authenticationRequired: "A saved relay requires authentication. Review its access requirements before authorizing further publication. This request is retained." 15 case .quotaExceeded: "A saved relay refused this publication because its quota was exhausted. Review that relay's limits. This request and its recorded effects are retained." 16 case .invalidPayload: "A saved relay reported a malformed publication. Review the captured form and relay evidence. The original signed publication is retained." 17 case .deliveryRefused: "A saved relay refused the publication. Review its requirements and the captured form. Recorded remote effects are retained." 18 } 19 } 20 } 21 22 /// Translation and presentation only; Rust decides whether new work may start. 23 enum TeraPublicationRetry: Sendable, Equatable { 24 case ready, complete, stopped 25 case deferredUntil(UInt64), inFlightUntil(UInt64) 26 case needsAction(TeraPublicationActionReason) 27 28 var mayStart: Bool { 29 self == .ready 30 } 31 32 var explanation: String? { 33 switch self { 34 case .ready, .complete, .stopped: nil 35 case let .needsAction(reason): reason.explanation 36 case let .deferredUntil(at): "The original publication is saved for retry. Check its status after \(date(at))." 37 case let .inFlightUntil(at): "An original delivery attempt is still claimed. Check its status after \(date(at)); recorded effects remain available." 38 } 39 } 40 41 static func decode(_ value: FfiPublicationRetryDecision) throws -> Self { 42 switch value { 43 case .ready: .ready 44 case .complete: .complete 45 case .stopped: .stopped 46 case let .deferredUntil(at): try .deferredUntil(timestamp(at)) 47 case let .inFlightUntil(at): try .inFlightUntil(timestamp(at)) 48 case let .needsAction(reason): .needsAction(decode(reason)) 49 } 50 } 51 52 private static func decode(_ reason: FfiPublicationActionReason) -> TeraPublicationActionReason { 53 switch reason { 54 case .coordinateChanged: .coordinateChanged 55 case .deadlineExceeded: .deadlineExceeded 56 case .attemptLimit: .attemptLimit 57 case .authenticationRequired: .authenticationRequired 58 case .quotaExceeded: .quotaExceeded 59 case .invalidPayload: .invalidPayload 60 case .deliveryRefused: .deliveryRefused 61 } 62 } 63 64 private static func timestamp(_ value: UInt64) throws -> UInt64 { 65 guard value > 0, value <= UInt64(Int64.max) else { throw TeraGeneratedSubmission.mismatch() } 66 return value 67 } 68 69 private func date(_ value: UInt64) -> String { 70 Date(timeIntervalSince1970: Double(value) / 1000).formatted(date: .abbreviated, time: .standard) 71 } 72 }