TeraGeneratedComposer.swift (7532B)
1 import Foundation 2 import TeraKitBindings 3 4 enum TeraGeneratedComposer { 5 static func reserveID() throws -> String { 6 let record = try composerReserveId() 7 try version(record.schemaVersion) 8 return record.id 9 } 10 11 static func save(runtime: TeraRuntime, request: TeraComposerSaveRequest) async throws -> TeraComposerSaveReceipt { 12 let record = try await runtime.composerSave(request: request.generatedValue) 13 try version(record.schemaVersion) 14 let draft = try record.draft.composerAppValue 15 guard draft.scope == request.scope, draft.id == request.id, 16 draft.editSequence == request.editSequence, draft.form == request.form, 17 draft.revision == (request.expectedRevision.map { $0 &+ 1 } ?? 1) 18 else { throw mismatch() } 19 return TeraComposerSaveReceipt(draft: draft, replayed: record.replayed) 20 } 21 22 static func load(runtime: TeraRuntime, scope: TeraComposerScope, id: String) async throws -> TeraComposerDraft { 23 let draft = try await runtime.composerLoad(scope: scope.generatedValue, composerId: id).composerAppValue 24 guard draft.scope == scope, draft.id == id else { throw mismatch() } 25 return draft 26 } 27 28 static func list(runtime: TeraRuntime, scope: TeraComposerScope, limit: UInt16, cursor: String?) async throws -> TeraComposerPage { 29 let record = try await runtime.composerList(schemaVersion: 1, scope: scope.generatedValue, limit: limit, cursor: cursor) 30 try version(record.schemaVersion) 31 let actualScope = try record.scope.composerAppValue 32 guard actualScope == scope else { throw mismatch() } 33 return try TeraComposerPage(scope: actualScope, entries: record.entries.map { entry in 34 switch entry { 35 case let .draft(summary): 36 try version(summary.schemaVersion) 37 return .draft(TeraComposerSummary( 38 id: summary.id, revision: summary.revision, editSequence: summary.editSequence, 39 commandType: summary.commandType.composerAppValue, 40 createdAtUnixMilliseconds: summary.createdAtUnixMs, 41 updatedAtUnixMilliseconds: summary.updatedAtUnixMs 42 )) 43 case let .repair(draftKey, revision, reason): 44 return .repair(draftKey: draftKey, revision: revision, 45 reason: reason == .unsupportedSchema ? .unsupportedSchema : .corruptRecord) 46 } 47 }, nextCursor: record.nextCursor) 48 } 49 50 static func version(_ version: UInt16) throws { 51 guard version == 1 else { 52 throw TeraRuntimeFailure.local(operation: "runtime.composer", code: "composer_schema_unsupported", 53 safeMessage: "This local composer format requires a compatible app.") 54 } 55 } 56 57 private static func mismatch() -> TeraRuntimeFailure { 58 .local(operation: "runtime.composer", code: "composer_receipt_mismatch", 59 safeMessage: "The local composer response could not be reconciled.") 60 } 61 } 62 63 extension TeraComposerScope { 64 var generatedValue: FfiComposerScopeRecord { 65 FfiComposerScopeRecord(schemaVersion: 1, authorPublicKey: authorPublicKey, localNetworkId: localNetworkID) 66 } 67 } 68 69 extension FfiComposerScopeRecord { 70 var composerAppValue: TeraComposerScope { 71 get throws { 72 try TeraGeneratedComposer.version(schemaVersion) 73 return TeraComposerScope(authorPublicKey: authorPublicKey, localNetworkID: localNetworkId) 74 } 75 } 76 } 77 78 extension TeraComposerSaveRequest { 79 var generatedValue: FfiComposerSaveRequest { 80 FfiComposerSaveRequest(schemaVersion: 1, scope: scope.generatedValue, id: id, 81 expectedRevision: expectedRevision, editSequence: editSequence, form: form.generatedValue) 82 } 83 } 84 85 extension FfiComposerDraftRecord { 86 var composerAppValue: TeraComposerDraft { 87 get throws { 88 try TeraGeneratedComposer.version(schemaVersion) 89 return try TeraComposerDraft(scope: scope.composerAppValue, id: id, revision: revision, 90 editSequence: editSequence, form: form.composerAppValue) 91 } 92 } 93 } 94 95 extension TeraComposerForm { 96 var generatedValue: FfiComposerFormRecord { 97 FfiComposerFormRecord( 98 schemaVersion: 1, 99 commandType: commandType.composerGeneratedValue, 100 content: content, 101 identifier: identifier, 102 title: title, 103 summary: summary, 104 location: location, 105 eventTiming: eventTiming?.composerGeneratedValue, 106 eventStartDate: eventStartDate, 107 eventEndDate: eventEndDate, 108 eventStartUnixS: eventStartUnixSeconds, 109 eventEndUnixS: eventEndUnixSeconds, 110 eventTimezone: eventTimezone, 111 priceAmount: priceAmount, 112 currency: currency, 113 unit: unit, 114 quantity: quantity, 115 foodPublishedAtUnixS: foodPublishedAtUnixSeconds, 116 foodStatus: foodStatus, 117 media: media.map(\.generatedValue) 118 ) 119 } 120 } 121 122 extension FfiComposerFormRecord { 123 var composerAppValue: TeraComposerForm { 124 get throws { 125 try TeraGeneratedComposer.version(schemaVersion) 126 var form = TeraComposerForm(commandType: commandType.composerAppValue) 127 form.content = content 128 form.identifier = identifier 129 form.title = title 130 form.summary = summary 131 form.location = location 132 form.eventTiming = eventTiming?.composerAppValue 133 form.eventStartDate = eventStartDate 134 form.eventEndDate = eventEndDate 135 form.eventStartUnixSeconds = eventStartUnixS 136 form.eventEndUnixSeconds = eventEndUnixS 137 form.eventTimezone = eventTimezone 138 form.priceAmount = priceAmount 139 form.currency = currency 140 form.unit = unit 141 form.quantity = quantity 142 form.foodPublishedAtUnixSeconds = foodPublishedAtUnixS 143 form.foodStatus = foodStatus 144 form.media = try media.map { try $0.composerAppValue } 145 return form 146 } 147 } 148 } 149 150 extension TeraComposerMedia { 151 var generatedValue: FfiComposerMediaRecord { 152 FfiComposerMediaRecord( 153 schemaVersion: 1, 154 opaqueReference: opaqueReference, 155 sha256: sha256, 156 mediaType: mediaType, 157 byteSize: byteSize, 158 width: width, 159 height: height, 160 alt: alt, 161 preparedAtUnixS: preparedAtUnixSeconds 162 ) 163 } 164 } 165 166 extension FfiComposerMediaRecord { 167 var composerAppValue: TeraComposerMedia { 168 get throws { 169 try TeraGeneratedComposer.version(schemaVersion) 170 return TeraComposerMedia( 171 opaqueReference: opaqueReference, 172 sha256: sha256, 173 mediaType: mediaType, 174 byteSize: byteSize, 175 width: width, 176 height: height, 177 alt: alt, 178 preparedAtUnixSeconds: preparedAtUnixS 179 ) 180 } 181 } 182 } 183 184 extension TeraAddCommandType { 185 fileprivate var composerGeneratedValue: FfiAddCommandType { 186 switch self { 187 case .createUpdate: .createUpdate 188 case .createPhotoUpdate: .createPhotoUpdate 189 case .createAsk: .createAsk 190 case .createEvent: .createEvent 191 case .createFoodAvailability: .createFoodAvailability 192 } 193 } 194 } 195 196 extension FfiAddCommandType { 197 fileprivate var composerAppValue: TeraAddCommandType { 198 switch self { 199 case .createUpdate: .createUpdate 200 case .createPhotoUpdate: .createPhotoUpdate 201 case .createAsk: .createAsk 202 case .createEvent: .createEvent 203 case .createFoodAvailability: .createFoodAvailability 204 } 205 } 206 } 207 208 extension TeraEventTiming { 209 fileprivate var composerGeneratedValue: FfiEventTimingKind { 210 switch self { 211 case .allDay: .allDay 212 case .timed: .timed 213 } 214 } 215 } 216 217 extension FfiEventTimingKind { 218 fileprivate var composerAppValue: TeraEventTiming { 219 switch self { 220 case .allDay: .allDay 221 case .timed: .timed 222 } 223 } 224 }