TeraNativeRecoverySchedule.swift (3040B)
1 import Foundation 2 import TeraKitBindings 3 4 struct TeraNativeRecoverySchedule: Sendable, Equatable { 5 let author: String 6 let revision: UInt64 7 let after: String? 8 } 9 10 extension TeraRuntimeBackend { 11 func nativeRecoverySchedule() async throws -> TeraNativeRecoverySchedule { 12 throw TeraComposerAcknowledgment.unconfirmed 13 } 14 15 func advanceNativeRecoverySchedule(expected _: TeraNativeRecoverySchedule, after _: String?) async throws -> TeraNativeRecoverySchedule { 16 throw TeraComposerAcknowledgment.unconfirmed 17 } 18 } 19 20 extension TeraRuntimeClient { 21 func nativeRecoverySchedule() async throws -> TeraNativeRecoverySchedule { 22 try await addOperation("runtime.recovery.schedule") { try await $0.nativeRecoverySchedule() } 23 } 24 25 func advanceNativeRecoverySchedule(expected: TeraNativeRecoverySchedule, after: String?) async throws -> TeraNativeRecoverySchedule { 26 try await addOperation("runtime.recovery.schedule.advance") { try await $0.advanceNativeRecoverySchedule(expected: expected, after: after) } 27 } 28 } 29 30 extension TeraGeneratedRuntimeBackend { 31 func nativeRecoverySchedule() async throws -> TeraNativeRecoverySchedule { 32 do { 33 return try await runtime.nativeRecoverySchedule(schemaVersion: 1).appValue() 34 } catch { throw TeraGeneratedRuntimeFailure.from(error) } 35 } 36 37 func advanceNativeRecoverySchedule(expected: TeraNativeRecoverySchedule, after: String?) async throws -> TeraNativeRecoverySchedule { 38 do { 39 let value = try await runtime.advanceNativeRecoverySchedule(expected: .init(schemaVersion: 1, author: expected.author, 40 revision: expected.revision, after: expected.after), after: after).appValue() 41 guard value.author == expected.author, value.after == after, 42 value.revision == expected.revision + (expected.after == after ? 0 : 1) 43 else { throw TeraComposerAcknowledgment.unconfirmed } 44 return value 45 } catch { throw TeraGeneratedRuntimeFailure.from(error) } 46 } 47 } 48 49 private extension FfiNativeRecoverySchedule { 50 func appValue() throws -> TeraNativeRecoverySchedule { 51 let canonical = { (value: String) in value.utf8.count == 64 && value.utf8.allSatisfy { (48 ... 57).contains($0) || (97 ... 102).contains($0) } } 52 guard schemaVersion == 1, canonical(author), revision <= UInt64(Int64.max), 53 after.map(canonical) ?? true, revision > 0 || after == nil 54 else { throw TeraComposerAcknowledgment.unconfirmed } 55 return .init(author: author, revision: revision, after: after) 56 } 57 } 58 59 /// One pass owns this handle. Failed persistence never advances its local view. 60 actor TeraNativeRecoveryContinuation { 61 private var value: TeraNativeRecoverySchedule 62 private let client: TeraRuntimeClient 63 64 init(_ value: TeraNativeRecoverySchedule, client: TeraRuntimeClient) { 65 self.value = value 66 self.client = client 67 } 68 69 func advance(_ after: String?) async throws { 70 value = try await client.advanceNativeRecoverySchedule(expected: value, after: after) 71 } 72 }