@arrirpc/codegen-swift 0.60.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs ADDED
@@ -0,0 +1,1528 @@
1
+ import fs from 'node:fs';
2
+ import { removeDisallowedChars, pascalCase, stringStartsWithNumber, camelCase, isSchemaFormRef, isServiceDefinition, isRpcDefinition, defineGeneratorPlugin, unflattenProcedures, isSchemaFormType, isSchemaFormEnum, isSchemaFormProperties, isSchemaFormElements, isSchemaFormValues, isSchemaFormDiscriminator } from '@arrirpc/codegen-utils';
3
+
4
+ function isNullableType(schema, context) {
5
+ return schema.nullable === true || context.isOptional === true;
6
+ }
7
+ function validTypeName(input) {
8
+ const formatted = removeDisallowedChars(
9
+ pascalCase(input.split("[").join("_").split("]").join("_"), {
10
+ normalize: true
11
+ }),
12
+ illegalPropertyChars
13
+ );
14
+ if (reservedKeywords[formatted]) {
15
+ return `_${formatted}`;
16
+ }
17
+ if (stringStartsWithNumber(formatted)) {
18
+ return `_${formatted}`;
19
+ }
20
+ return formatted;
21
+ }
22
+ function getTypeName(schema, context) {
23
+ if (schema.metadata?.id) {
24
+ const typeName2 = validTypeName(schema.metadata.id);
25
+ return typeName2;
26
+ }
27
+ if (context.discriminatorParent && context.discriminatorValue) {
28
+ const typeName2 = validTypeName(
29
+ `${context.discriminatorParent}_${context.discriminatorValue}`
30
+ );
31
+ return typeName2;
32
+ }
33
+ const typeName = validTypeName(context.instancePath.split("/").join("_"));
34
+ return typeName;
35
+ }
36
+ const reservedKeywords = {
37
+ associatedType: true,
38
+ class: true,
39
+ deinit: true,
40
+ enum: true,
41
+ extension: true,
42
+ fileprivate: true,
43
+ func: true,
44
+ import: true,
45
+ init: true,
46
+ inout: true,
47
+ internal: true,
48
+ let: true,
49
+ open: true,
50
+ operator: true,
51
+ private: true,
52
+ precedencegroup: true,
53
+ protocol: true,
54
+ public: true,
55
+ rethrows: true,
56
+ static: true,
57
+ subscript: true,
58
+ typealias: true,
59
+ var: true,
60
+ break: true,
61
+ case: true,
62
+ catch: true,
63
+ continue: true,
64
+ default: true,
65
+ defer: true,
66
+ do: true,
67
+ else: true,
68
+ fallthrough: true,
69
+ for: true,
70
+ guard: true,
71
+ if: true,
72
+ in: true,
73
+ repeat: true,
74
+ return: true,
75
+ throw: true,
76
+ switch: true,
77
+ where: true,
78
+ while: true,
79
+ Any: true,
80
+ as: true,
81
+ false: true,
82
+ is: true,
83
+ nil: true,
84
+ self: true,
85
+ Self: true,
86
+ super: true,
87
+ true: true,
88
+ try: true
89
+ };
90
+ const illegalPropertyChars = "!@#$%^&*()+=[]{}\\|;:'\",./?><`~";
91
+ function validSwiftKey(input) {
92
+ const key = removeDisallowedChars(
93
+ camelCase(input, { normalize: true }),
94
+ illegalPropertyChars
95
+ );
96
+ if (reservedKeywords[key]) {
97
+ return `\`${key}\``;
98
+ }
99
+ if (stringStartsWithNumber(key)) {
100
+ return `_${key}`;
101
+ }
102
+ return key;
103
+ }
104
+ function codeComments(schema, leading = "") {
105
+ const description = schema.metadata?.description?.split("\n").map((line) => `${leading}/// ${line}`).join("\n");
106
+ if (description && schema.metadata?.isDeprecated) {
107
+ return `${description}
108
+ ${leading}@available(*, deprecated)
109
+ `;
110
+ }
111
+ if (description) {
112
+ return `${description}
113
+ `;
114
+ }
115
+ if (schema.metadata?.isDeprecated) {
116
+ return `${leading}@available(*, deprecated)
117
+ `;
118
+ }
119
+ return "";
120
+ }
121
+
122
+ function swiftAnyFromSchema(schema, context) {
123
+ const isNullable = isNullableType(schema, context);
124
+ let defaultValue = "JSON()";
125
+ if (schema.nullable) {
126
+ defaultValue = 'JSON(parseJSON: "null")';
127
+ } else if (context.isOptional) {
128
+ defaultValue = "";
129
+ }
130
+ return {
131
+ typeName: context.isOptional ? "JSON?" : "JSON",
132
+ defaultValue,
133
+ isNullable,
134
+ canBeQueryString: false,
135
+ hasRequiredRef: false,
136
+ fromJsonTemplate(input, target) {
137
+ if (isNullable) {
138
+ return ` if ${input}.exists() {
139
+ ${target} = ${input}
140
+ }`;
141
+ }
142
+ return ` ${target} = ${input}`;
143
+ },
144
+ toJsonTemplate(input, target) {
145
+ if (context.isOptional) {
146
+ return ` ${target} += serializeAny(input: ${input}!)`;
147
+ }
148
+ return ` ${target} += serializeAny(input: ${input})`;
149
+ },
150
+ toQueryPartTemplate(_, __, ___) {
151
+ return ` print("[WARNING] any's cannot be serialized to query params. Skipping field at ${context.instancePath}.")`;
152
+ },
153
+ content: ""
154
+ };
155
+ }
156
+
157
+ function swiftArrayFromSchema(schema, context) {
158
+ const subType = swiftTypeFromSchema(schema.elements, {
159
+ clientVersion: context.clientVersion,
160
+ clientName: context.clientName,
161
+ typePrefix: context.typePrefix,
162
+ instancePath: `${context.instancePath}/[element]`,
163
+ schemaPath: `${context.schemaPath}/elements`,
164
+ generatedTypes: context.generatedTypes,
165
+ containsRequiredRef: context.containsRequiredRef
166
+ });
167
+ const isNullable = isNullableType(schema, context);
168
+ const typeName = isNullable ? `[${subType.typeName}]?` : `[${subType.typeName}]`;
169
+ const defaultValue = isNullable ? "" : "[]";
170
+ return {
171
+ typeName,
172
+ isNullable,
173
+ defaultValue,
174
+ canBeQueryString: false,
175
+ hasRequiredRef: false,
176
+ fromJsonTemplate(input, target, key) {
177
+ const innerKey = validSwiftKey(key);
178
+ const mainContent = ` ${target} = []
179
+ for __${innerKey}JsonElement in ${input}.array ?? [] {
180
+ var __${innerKey}JsonElementValue: ${subType.typeName}
181
+ ${subType.fromJsonTemplate(`__${innerKey}JsonElement`, `__${innerKey}JsonElementValue`, `element`)}
182
+ ${target}${isNullable ? "!" : ""}.append(__${innerKey}JsonElementValue)
183
+ }`;
184
+ if (context.isOptional) {
185
+ return ` if ${input}.exists() {
186
+ ${mainContent}
187
+ }`;
188
+ }
189
+ if (schema.nullable) {
190
+ return ` if ${input}.array != nil {
191
+ ${mainContent}
192
+ }`;
193
+ }
194
+ return mainContent;
195
+ },
196
+ toJsonTemplate(input, target) {
197
+ const mainContent = ` ${target} += "["
198
+ for (__index, __element) in ${input}${isNullable ? "!" : ""}.enumerated() {
199
+ if __index > 0 {
200
+ ${target} += ","
201
+ }
202
+ ${subType.toJsonTemplate(`__element`, target)}
203
+ }
204
+ ${target} += "]"`;
205
+ if (schema.nullable) {
206
+ return ` if ${input} != nil {
207
+ ${mainContent}
208
+ } else {
209
+ ${target} += "null"
210
+ }`;
211
+ }
212
+ return mainContent;
213
+ },
214
+ toQueryPartTemplate(_, __, ___) {
215
+ return ` print("[WARNING] arrays cannot be serialized to query params. Skipping field at ${context.instancePath}.")`;
216
+ },
217
+ cloneTemplate(input, key) {
218
+ const innerKey = validSwiftKey(key);
219
+ const subTypeClonedResult = subType.cloneTemplate?.(
220
+ `__${innerKey}Element`,
221
+ `__${innerKey}Element`
222
+ );
223
+ if (isNullable) {
224
+ return {
225
+ bodyContent: `var __${innerKey}Cloned: ${typeName}
226
+ if ${input} != nil {
227
+ __${innerKey}Cloned = []
228
+ for __${innerKey}Element in ${input}! {
229
+ ${subTypeClonedResult?.bodyContent ?? ""}
230
+ __${innerKey}Cloned!.append(${subTypeClonedResult?.fieldContent || `__${innerKey}Element`})
231
+ }
232
+ }`,
233
+ fieldContent: `__${innerKey}Cloned`
234
+ };
235
+ }
236
+ return {
237
+ bodyContent: `var __${innerKey}Cloned: ${typeName} = []
238
+ for __${innerKey}Element in ${input} {
239
+ ${subTypeClonedResult?.bodyContent ?? ""}
240
+ __${innerKey}Cloned.append(${subTypeClonedResult?.fieldContent || `__${innerKey}Element`})
241
+ }`,
242
+ fieldContent: `__${innerKey}Cloned`
243
+ };
244
+ },
245
+ content: subType.content
246
+ };
247
+ }
248
+
249
+ function swiftObjectFromSchema(schema, context) {
250
+ const typeName = getTypeName(schema, context);
251
+ const prefixedTypeName = `${context.typePrefix}${typeName}`;
252
+ const isNullable = isNullableType(schema, context);
253
+ const defaultValue = isNullable ? "" : `${prefixedTypeName}()`;
254
+ const result = {
255
+ typeName: isNullable ? `${prefixedTypeName}?` : prefixedTypeName,
256
+ defaultValue,
257
+ isNullable,
258
+ canBeQueryString: false,
259
+ hasRequiredRef: context.containsRequiredRef[typeName] ?? false,
260
+ fromJsonTemplate(input, target) {
261
+ if (context.isOptional) {
262
+ return ` if ${input}.exists() {
263
+ ${target} = ${prefixedTypeName}(json: ${input})
264
+ }`;
265
+ }
266
+ if (schema.nullable) {
267
+ return ` if ${input}.dictionary != nil {
268
+ ${target} = ${prefixedTypeName}(json: ${input})
269
+ }`;
270
+ }
271
+ return ` ${target} = ${prefixedTypeName}(json: ${input})`;
272
+ },
273
+ toJsonTemplate(input, target) {
274
+ if (context.isOptional) {
275
+ return ` ${target} += ${input}!.toJSONString()`;
276
+ }
277
+ if (schema.nullable) {
278
+ return ` if ${input} != nil {
279
+ ${target} += ${input}!.toJSONString()
280
+ } else {
281
+ ${target} += "null"
282
+ }`;
283
+ }
284
+ return ` ${target} += ${input}.toJSONString()`;
285
+ },
286
+ toQueryPartTemplate(_, __, ___) {
287
+ return ` print("[WARNING] nested objects cannot be serialized to query params. Skipping field at ${context.instancePath}.")`;
288
+ },
289
+ cloneTemplate(input, _key) {
290
+ let fieldContent = `${input}.clone()`;
291
+ if (isNullable) {
292
+ fieldContent = `${input}?.clone()`;
293
+ }
294
+ return {
295
+ tempKey: "",
296
+ bodyContent: "",
297
+ fieldContent
298
+ };
299
+ },
300
+ content: ""
301
+ };
302
+ if (context.generatedTypes.includes(typeName)) {
303
+ return result;
304
+ }
305
+ const fieldNames = [];
306
+ const fieldNameParts = [];
307
+ const initArgParts = [];
308
+ const initBodyParts = [];
309
+ const initFromJsonParts = [];
310
+ const toJsonParts = [];
311
+ const toQueryStringParts = [];
312
+ const cloneBodyParts = [];
313
+ const cloneFieldParts = [];
314
+ const subContent = [];
315
+ let numKeys = 0;
316
+ let canBeQueryString = false;
317
+ let hasRecursiveSubType = false;
318
+ if (context.discriminatorKey && context.discriminatorValue) {
319
+ numKeys++;
320
+ canBeQueryString = true;
321
+ const discriminatorKey = validSwiftKey(context.discriminatorKey);
322
+ fieldNames.push(discriminatorKey);
323
+ fieldNameParts.push(
324
+ ` let ${discriminatorKey}: String = "${context.discriminatorValue}"`
325
+ );
326
+ toJsonParts.push(
327
+ ` __json += "\\"${context.discriminatorKey}\\":\\"${context.discriminatorValue}\\""`
328
+ );
329
+ toQueryStringParts.push(
330
+ ` __queryParts.append(URLQueryItem(name: "${context.discriminatorKey}", value: "${context.discriminatorValue}"))`
331
+ );
332
+ }
333
+ for (const key of Object.keys(schema.properties)) {
334
+ const subSchema = schema.properties[key];
335
+ const subType = swiftTypeFromSchema(subSchema, {
336
+ clientVersion: context.clientVersion,
337
+ clientName: context.clientName,
338
+ typePrefix: context.typePrefix,
339
+ instancePath: `/${typeName}/${key}`,
340
+ schemaPath: `${context.schemaPath}/properties/${key}`,
341
+ generatedTypes: context.generatedTypes,
342
+ containsRequiredRef: context.containsRequiredRef
343
+ });
344
+ if (subType.content)
345
+ subContent.push(subType.content);
346
+ if (subType.hasRequiredRef && !subType.isNullable) {
347
+ context.containsRequiredRef[typeName] = true;
348
+ result.hasRequiredRef = true;
349
+ }
350
+ if (isSchemaFormRef(subSchema)) {
351
+ hasRecursiveSubType = true;
352
+ }
353
+ if (subType.canBeQueryString)
354
+ canBeQueryString = true;
355
+ const fieldName = validSwiftKey(key);
356
+ fieldNames.push(fieldName);
357
+ if (subType.defaultValue) {
358
+ fieldNameParts.push(
359
+ `${codeComments(subSchema, " ")} public var ${fieldName}: ${subType.typeName} = ${subType.defaultValue}`
360
+ );
361
+ } else {
362
+ fieldNameParts.push(
363
+ `${codeComments(subSchema, " ")} public var ${fieldName}: ${subType.typeName}`
364
+ );
365
+ }
366
+ initArgParts.push(` ${fieldName}: ${subType.typeName}`);
367
+ initBodyParts.push(` self.${fieldName} = ${fieldName}`);
368
+ initFromJsonParts.push(
369
+ subType.fromJsonTemplate(
370
+ `json["${key}"]`,
371
+ `self.${fieldName}`,
372
+ key
373
+ )
374
+ );
375
+ if (numKeys > 0) {
376
+ toJsonParts.push(` __json += ",\\"${key}\\":"`);
377
+ } else {
378
+ toJsonParts.push(` __json += "\\"${key}\\":"`);
379
+ }
380
+ toJsonParts.push(subType.toJsonTemplate(`self.${fieldName}`, `__json`));
381
+ if (subType.canBeQueryString)
382
+ canBeQueryString = true;
383
+ toQueryStringParts.push(
384
+ subType.toQueryPartTemplate(
385
+ `self.${fieldName}`,
386
+ `__queryParts`,
387
+ key
388
+ )
389
+ );
390
+ const cloneResult = subType.cloneTemplate?.(
391
+ `self.${fieldName}`,
392
+ fieldName
393
+ );
394
+ if (cloneResult) {
395
+ cloneBodyParts.push(cloneResult.bodyContent);
396
+ cloneFieldParts.push(
397
+ ` ${fieldName}: ${cloneResult.fieldContent}`
398
+ );
399
+ } else {
400
+ cloneFieldParts.push(
401
+ ` ${fieldName.split("`").join("")}: self.${fieldName}`
402
+ );
403
+ }
404
+ numKeys++;
405
+ }
406
+ let numOptionalKeys = 0;
407
+ for (const key of Object.keys(schema.optionalProperties ?? {})) {
408
+ const subSchema = schema.optionalProperties[key];
409
+ const subType = swiftTypeFromSchema(subSchema, {
410
+ clientVersion: context.clientVersion,
411
+ clientName: context.clientName,
412
+ typePrefix: context.typePrefix,
413
+ instancePath: `/${typeName}/${key}`,
414
+ schemaPath: `${context.schemaPath}/optionalProperties/${key}`,
415
+ generatedTypes: context.generatedTypes,
416
+ isOptional: true,
417
+ containsRequiredRef: context.containsRequiredRef
418
+ });
419
+ if (subType.content)
420
+ subContent.push(subType.content);
421
+ if (isSchemaFormRef(subSchema)) {
422
+ hasRecursiveSubType = true;
423
+ }
424
+ if (subType.canBeQueryString)
425
+ canBeQueryString = true;
426
+ const fieldName = validSwiftKey(key);
427
+ fieldNames.push(fieldName);
428
+ fieldNameParts.push(
429
+ `${codeComments(subSchema, " ")} public var ${fieldName}: ${subType.typeName}`
430
+ );
431
+ initArgParts.push(` ${fieldName}: ${subType.typeName}`);
432
+ initBodyParts.push(` self.${fieldName} = ${fieldName}`);
433
+ initFromJsonParts.push(
434
+ subType.fromJsonTemplate(
435
+ `json["${key}"]`,
436
+ `self.${fieldName}`,
437
+ key
438
+ )
439
+ );
440
+ let toJsonContent = ``;
441
+ if (numKeys > 0) {
442
+ toJsonContent += ` __json += ",\\"${key}\\":"
443
+ `;
444
+ } else {
445
+ if (numOptionalKeys > 0) {
446
+ toJsonContent += ` if __numKeys > 0 {
447
+ __json += ","
448
+ }
449
+ `;
450
+ }
451
+ toJsonContent += ` __json += "\\"${key}\\":"
452
+ `;
453
+ }
454
+ toJsonContent += subType.toJsonTemplate(`self.${fieldName}`, `__json`);
455
+ if (numKeys === 0) {
456
+ toJsonContent += `
457
+ __numKeys += 1`;
458
+ }
459
+ toJsonParts.push(` if self.${fieldName} != nil {
460
+ ${toJsonContent}
461
+ }`);
462
+ if (subType.canBeQueryString)
463
+ canBeQueryString = true;
464
+ toQueryStringParts.push(
465
+ subType.toQueryPartTemplate(
466
+ `self.${fieldName}`,
467
+ `__queryParts`,
468
+ key
469
+ )
470
+ );
471
+ const cloneResult = subType.cloneTemplate?.(
472
+ `self.${fieldName}`,
473
+ fieldName
474
+ );
475
+ if (cloneResult) {
476
+ cloneBodyParts.push(cloneResult.bodyContent);
477
+ cloneFieldParts.push(
478
+ ` ${fieldName}: ${cloneResult.fieldContent}`
479
+ );
480
+ } else {
481
+ cloneFieldParts.push(
482
+ ` ${fieldName.split("`").join("")}: self.${fieldName}`
483
+ );
484
+ }
485
+ numOptionalKeys++;
486
+ }
487
+ const declaration = hasRecursiveSubType ? `final class` : "struct";
488
+ const initPrefix = hasRecursiveSubType ? `public required` : `public`;
489
+ const initJsonStringPrefix = hasRecursiveSubType ? `public required convenience` : `public`;
490
+ let equalsPart = "";
491
+ if (hasRecursiveSubType) {
492
+ equalsPart = `public static func == (lhs: ${prefixedTypeName}, rhs: ${prefixedTypeName}) -> Bool {
493
+ return
494
+ ${fieldNames.map((field) => ` lhs.${field} == rhs.${field}`).join(" &&\n")}
495
+ }`;
496
+ }
497
+ result.content = `${codeComments(schema)}public ${declaration} ${prefixedTypeName}: ArriClientModel {
498
+ ${fieldNameParts.join("\n")}
499
+ ${initPrefix} init(
500
+ ${initArgParts.join(",\n")}
501
+ ) {
502
+ ${initBodyParts.join("\n")}
503
+ }
504
+ ${initPrefix} init() {}
505
+ ${initPrefix} init(json: JSON) {
506
+ ${initFromJsonParts.join("\n")}
507
+ }
508
+ ${initJsonStringPrefix} init(JSONData: Data) {
509
+ do {
510
+ let json = try JSON(data: JSONData)
511
+ self.init(json: json)
512
+ } catch {
513
+ print("[WARNING] Error parsing JSON: \\(error)")
514
+ self.init()
515
+ }
516
+ }
517
+ ${initJsonStringPrefix} init(JSONString: String) {
518
+ do {
519
+ let json = try JSON(data: JSONString.data(using: .utf8) ?? Data())
520
+ self.init(json: json)
521
+ } catch {
522
+ print("[WARNING] Error parsing JSON: \\(error)")
523
+ self.init()
524
+ }
525
+ }
526
+ public func toJSONString() -> String {
527
+ var __json = "{"
528
+ ${numKeys === 0 ? ` var __numKeys = 0` : ""}
529
+ ${toJsonParts.join("\n")}
530
+ __json += "}"
531
+ return __json
532
+ }
533
+ public func toURLQueryParts() -> [URLQueryItem] {
534
+ ${canBeQueryString ? `var __queryParts: [URLQueryItem] = []` : ""}
535
+ ${toQueryStringParts.join("\n")}
536
+ ${canBeQueryString ? `return __queryParts` : `return []`}
537
+ }
538
+ public func clone() -> ${prefixedTypeName} {
539
+ ${cloneBodyParts.join("\n")}
540
+ return ${prefixedTypeName}(
541
+ ${cloneFieldParts.join(",\n")}
542
+ )
543
+ }
544
+ ${equalsPart}
545
+ }
546
+
547
+ ${subContent.join("\n")}`;
548
+ context.generatedTypes.push(typeName);
549
+ return result;
550
+ }
551
+
552
+ function swiftTaggedUnionFromSchema(schema, context) {
553
+ const typeName = getTypeName(schema, context);
554
+ const prefixedTypeName = `${context.typePrefix}${typeName}`;
555
+ const isNullable = isNullableType(schema, context);
556
+ const defaultValue = isNullable ? `` : `${prefixedTypeName}()`;
557
+ const result = {
558
+ typeName: isNullable ? `${prefixedTypeName}?` : prefixedTypeName,
559
+ isNullable,
560
+ defaultValue,
561
+ canBeQueryString: false,
562
+ hasRequiredRef: context.containsRequiredRef[typeName] ?? false,
563
+ fromJsonTemplate(input, target, _) {
564
+ if (context.isOptional) {
565
+ return ` if ${input}.exists() {
566
+ ${target} = ${prefixedTypeName}(json: ${input})
567
+ }`;
568
+ }
569
+ if (schema.nullable) {
570
+ return ` if ${input}.dictionary != nil {
571
+ ${target} = ${prefixedTypeName}(json: ${input})
572
+ }`;
573
+ }
574
+ return ` ${target} = ${prefixedTypeName}(json: ${input})`;
575
+ },
576
+ toJsonTemplate(input, target) {
577
+ if (context.isOptional) {
578
+ return ` ${target} += ${input}!.toJSONString()`;
579
+ }
580
+ if (schema.nullable) {
581
+ return ` if ${input} != nil {
582
+ ${target} += ${input}!.toJSONString()
583
+ } else {
584
+ ${target} += "null"
585
+ }`;
586
+ }
587
+ return ` ${target} += ${input}.toJSONString()`;
588
+ },
589
+ toQueryPartTemplate(_, __, ___) {
590
+ return ` print("[WARNING] nested objects cannot be serialized to query params. Skipping field at ${context.instancePath}.")`;
591
+ },
592
+ cloneTemplate(input, _) {
593
+ return {
594
+ fieldContent: `${input}${isNullable ? "?" : ""}.clone()`,
595
+ bodyContent: ""
596
+ };
597
+ },
598
+ content: ""
599
+ };
600
+ if (context.generatedTypes.includes(typeName)) {
601
+ return result;
602
+ }
603
+ const discriminatorParts = [];
604
+ const discriminatorKey = schema.discriminator;
605
+ for (const key of Object.keys(schema.mapping)) {
606
+ const discriminatorValue = key;
607
+ const subSchema = schema.mapping[key];
608
+ const subType = swiftObjectFromSchema(subSchema, {
609
+ clientVersion: context.clientVersion,
610
+ clientName: context.clientName,
611
+ typePrefix: context.typePrefix,
612
+ instancePath: context.instancePath,
613
+ schemaPath: `${context.schemaPath}/mapping/${key}`,
614
+ generatedTypes: context.generatedTypes,
615
+ discriminatorKey,
616
+ discriminatorParent: typeName,
617
+ discriminatorValue: key,
618
+ containsRequiredRef: context.containsRequiredRef
619
+ });
620
+ const discriminatorCase = validSwiftKey(key);
621
+ const discriminatorPart = {
622
+ discriminatorValue,
623
+ discriminatorCase,
624
+ hasRequiredRef: subType.hasRequiredRef,
625
+ typeName: subType.typeName.replace("?", ""),
626
+ content: subType.content
627
+ };
628
+ discriminatorParts.push(discriminatorPart);
629
+ }
630
+ if (!discriminatorParts.length) {
631
+ throw new Error(
632
+ `Invalid schema at ${context.schemaPath}. Discriminators must have at least one mapping.`
633
+ );
634
+ }
635
+ let defaultPart;
636
+ for (const part of discriminatorParts) {
637
+ if (part.hasRequiredRef)
638
+ continue;
639
+ defaultPart = part;
640
+ break;
641
+ }
642
+ if (!defaultPart) {
643
+ throw new Error(
644
+ `Invalid schema a ${context.schemaPath}. All subtypes have a required recursive reference. This creates an infinite loop.`
645
+ );
646
+ }
647
+ result.content = `${codeComments(schema)}public enum ${prefixedTypeName}: ArriClientModel {
648
+ ${discriminatorParts.map((part) => ` case ${part.discriminatorCase}(${part.typeName})`).join("\n")}
649
+ public init() {
650
+ self = .${defaultPart.discriminatorCase}(${defaultPart.typeName}())
651
+ }
652
+ public init(json: JSON) {
653
+ let discriminator = json["${discriminatorKey}"].string ?? ""
654
+ switch (discriminator) {
655
+ ${discriminatorParts.map(
656
+ (part) => ` case "${part.discriminatorValue}":
657
+ self = .${part.discriminatorCase}(${part.typeName}(json: json))
658
+ break`
659
+ ).join("\n")}
660
+ default:
661
+ self = .${defaultPart.discriminatorCase}(${defaultPart.typeName}())
662
+ break
663
+ }
664
+ }
665
+ public init(JSONData: Data) {
666
+ do {
667
+ let json = try JSON(data: JSONData)
668
+ self.init(json: json)
669
+ } catch {
670
+ print("[WARNING] Error parsing JSON: \\(error)")
671
+ self.init()
672
+ }
673
+ }
674
+ public init(JSONString: String) {
675
+ do {
676
+ let json = try JSON(data: JSONString.data(using: .utf8) ?? Data())
677
+ self.init(json: json)
678
+ } catch {
679
+ print("[WARNING] Error parsing JSON: \\(error)")
680
+ self.init()
681
+ }
682
+ }
683
+ public func toJSONString() -> String {
684
+ switch(self) {
685
+ ${discriminatorParts.map(
686
+ (part) => ` case .${part.discriminatorCase}(let __innerVal):
687
+ return __innerVal.toJSONString()`
688
+ ).join("\n")}
689
+ }
690
+ }
691
+ public func toURLQueryParts() -> [URLQueryItem] {
692
+ switch(self) {
693
+ ${discriminatorParts.map(
694
+ (part) => ` case .${part.discriminatorCase}(let __innerVal):
695
+ return __innerVal.toURLQueryParts()`
696
+ ).join("\n")}
697
+ }
698
+ }
699
+ public func clone() -> ${prefixedTypeName} {
700
+ switch(self) {
701
+ ${discriminatorParts.map(
702
+ (part) => ` case .${part.discriminatorCase}(let __innerVal):
703
+ return .${part.discriminatorCase}(__innerVal.clone())`
704
+ ).join("\n")}
705
+ }
706
+ }
707
+ }
708
+
709
+ ${discriminatorParts.map((part) => part.content).join("\n")}`;
710
+ context.generatedTypes.push(typeName);
711
+ return result;
712
+ }
713
+
714
+ function swiftEnumFromSchema(schema, context) {
715
+ if (!schema.enum[0]) {
716
+ throw new Error(
717
+ `Error at ${context.instancePath}. Must have at least one enum value.`
718
+ );
719
+ }
720
+ const typeName = getTypeName(schema, context);
721
+ const isNullable = isNullableType(schema, context);
722
+ const defaultEnumValue = camelCase(schema.enum[0], { normalize: true });
723
+ const prefixedTypeName = `${context.typePrefix}${typeName}`;
724
+ const defaultValue = isNullable ? "" : `${prefixedTypeName}.${defaultEnumValue}`;
725
+ const result = {
726
+ typeName: isNullable ? `${typeName}?` : typeName,
727
+ isNullable,
728
+ defaultValue,
729
+ canBeQueryString: true,
730
+ hasRequiredRef: false,
731
+ fromJsonTemplate(input, target) {
732
+ if (context.isOptional) {
733
+ return ` if ${input}.exists() {
734
+ ${target} = ${prefixedTypeName}(serialValue: ${input}.string ?? "")
735
+ }`;
736
+ }
737
+ if (schema.nullable) {
738
+ return ` if ${input}.string != nil {
739
+ ${target} = ${prefixedTypeName}(serialValue: ${input}.string ?? "")
740
+ }`;
741
+ }
742
+ return ` ${target} = ${prefixedTypeName}(serialValue: ${input}.string ?? "")`;
743
+ },
744
+ toJsonTemplate(input, target) {
745
+ if (context.isOptional) {
746
+ return ` ${target} += "\\"\\(${input}!.serialValue())\\""`;
747
+ }
748
+ if (schema.nullable) {
749
+ return ` if ${input} != nil {
750
+ ${target} += "\\"\\(${input}!.serialValue())\\""
751
+ } else {
752
+ ${target} += "null"
753
+ }`;
754
+ }
755
+ return ` ${target} += "\\"\\(${input}.serialValue())\\""`;
756
+ },
757
+ toQueryPartTemplate(input, target, key) {
758
+ if (context.isOptional) {
759
+ return ` if ${input} != nil {
760
+ ${target}.append(URLQueryItem(name: "${key}", value: ${input}!.serialValue()))
761
+ }`;
762
+ }
763
+ if (schema.nullable) {
764
+ return ` if ${input} != nil {
765
+ ${target}.append(URLQueryItem(name: "${key}", value: ${input}!.serialValue()))
766
+ } else {
767
+ ${target}.append(URLQueryItem(name: "${key}", value: "null"))
768
+ }`;
769
+ }
770
+ return ` ${target}.append(URLQueryItem(name: "${key}", value: ${input}.serialValue()))`;
771
+ },
772
+ content: ""
773
+ };
774
+ if (context.generatedTypes.includes(typeName)) {
775
+ return result;
776
+ }
777
+ result.content = `public enum ${prefixedTypeName}: ArriClientEnum {
778
+ ${schema.enum.map((val) => ` case ${camelCase(val, { normalize: true })}`).join("\n")}
779
+ public init() {
780
+ self = .${defaultEnumValue}
781
+ }
782
+ public init(serialValue: String) {
783
+ switch(serialValue) {
784
+ ${schema.enum.map(
785
+ (val) => ` case "${val}":
786
+ self = .${camelCase(val, { normalize: true })}
787
+ break;`
788
+ ).join("\n")}
789
+ default:
790
+ self = .${defaultEnumValue}
791
+ }
792
+ }
793
+ public func serialValue() -> String {
794
+ switch (self) {
795
+ ${schema.enum.map(
796
+ (val) => ` case .${camelCase(val, { normalize: true })}:
797
+ return "${val}"`
798
+ ).join("\n")}
799
+ }
800
+ }
801
+ }`;
802
+ context.generatedTypes.push(typeName);
803
+ return result;
804
+ }
805
+
806
+ function swiftStringFromSchema(schema, context) {
807
+ const isNullable = isNullableType(schema, context);
808
+ const typeName = isNullable ? "String?" : "String";
809
+ const defaultValue = isNullable ? "" : '""';
810
+ return {
811
+ typeName,
812
+ isNullable,
813
+ defaultValue,
814
+ canBeQueryString: true,
815
+ hasRequiredRef: false,
816
+ fromJsonTemplate(input, target) {
817
+ if (context.isOptional) {
818
+ return ` if ${input}.exists() {
819
+ ${target} = ${input}.string
820
+ }`;
821
+ }
822
+ if (schema.nullable) {
823
+ return ` if ${input}.string != nil {
824
+ ${target} = ${input}.string
825
+ }`;
826
+ }
827
+ return ` ${target} = ${input}.string ?? ""`;
828
+ },
829
+ toJsonTemplate(input, target) {
830
+ if (context.isOptional) {
831
+ return ` ${target} += serializeString(input: ${input}!)`;
832
+ }
833
+ if (schema.nullable) {
834
+ return ` if ${input} != nil {
835
+ ${target} += serializeString(input: ${input}!)
836
+ } else {
837
+ ${target} += "null"
838
+ }`;
839
+ }
840
+ return ` ${target} += serializeString(input: ${input})`;
841
+ },
842
+ toQueryPartTemplate(input, target, key) {
843
+ if (context.isOptional) {
844
+ return ` if ${input} != nil {
845
+ ${target}.append(URLQueryItem(name: "${key}", value: ${input}!))
846
+ }`;
847
+ }
848
+ if (schema.nullable) {
849
+ return ` if ${input} != nil {
850
+ ${target}.append(URLQueryItem(name: "${key}", value: ${input}!))
851
+ } else {
852
+ ${target}.append(URLQueryItem(name: "${key}", value: "null"))
853
+ }`;
854
+ }
855
+ return ` ${target}.append(URLQueryItem(name: "${key}", value: ${input}))`;
856
+ },
857
+ content: ""
858
+ };
859
+ }
860
+ function swiftBooleanFromSchema(schema, context) {
861
+ const isNullable = isNullableType(schema, context);
862
+ const typeName = isNullable ? "Bool?" : "Bool";
863
+ const defaultValue = isNullable ? "" : "false";
864
+ return {
865
+ typeName,
866
+ isNullable,
867
+ defaultValue,
868
+ canBeQueryString: true,
869
+ hasRequiredRef: false,
870
+ fromJsonTemplate(input, target) {
871
+ if (context.isOptional) {
872
+ return ` if ${input}.exists() {
873
+ ${target} = ${input}.bool
874
+ }`;
875
+ }
876
+ if (schema.nullable) {
877
+ return ` if ${input}.bool != nil {
878
+ ${target} = ${input}.bool
879
+ }`;
880
+ }
881
+ return ` ${target} = ${input}.bool ?? false`;
882
+ },
883
+ toJsonTemplate(input, target) {
884
+ if (context.isOptional) {
885
+ return `${target} += "\\(${input}!)"`;
886
+ }
887
+ if (schema.nullable) {
888
+ return ` if ${input} != nil {
889
+ ${target} += "\\(${input}!)"
890
+ } else {
891
+ ${target} += "null"
892
+ }`;
893
+ }
894
+ return ` ${target} += "\\(${input})"`;
895
+ },
896
+ toQueryPartTemplate(input, target, key) {
897
+ if (context.isOptional) {
898
+ return ` if ${input} != nil {
899
+ ${target}.append(URLQueryItem(name: "${key}", value: "\\(${input}!)"))
900
+ }`;
901
+ }
902
+ if (schema.nullable) {
903
+ return ` if ${input} != nil {
904
+ ${target}.append(URLQueryItem(name: "${key}", value: "\\(${input}!)"))
905
+ } else {
906
+ ${target}.append(URLQueryItem(name: "${key}", value: "null"))
907
+ }`;
908
+ }
909
+ return ` ${target}.append(URLQueryItem(name: "${key}", value: "\\(${input})"))`;
910
+ },
911
+ content: ""
912
+ };
913
+ }
914
+ function swiftTimestampFromSchema(schema, context) {
915
+ const isNullable = isNullableType(schema, context);
916
+ const typeName = isNullable ? "Date?" : "Date";
917
+ const defaultValue = isNullable ? "" : "Date()";
918
+ return {
919
+ typeName,
920
+ defaultValue,
921
+ isNullable,
922
+ canBeQueryString: true,
923
+ hasRequiredRef: false,
924
+ fromJsonTemplate(input, target) {
925
+ if (context.isOptional) {
926
+ return ` if ${input}.exists() {
927
+ ${target} = parseDate(${input}.string ?? "") ?? Date()
928
+ }`;
929
+ }
930
+ if (schema.nullable) {
931
+ return ` if ${input}.string != nil {
932
+ ${target} = parseDate(${input}.string ?? "") ?? Date()
933
+ }`;
934
+ }
935
+ return ` ${target} = parseDate(${input}.string ?? "") ?? Date()`;
936
+ },
937
+ toJsonTemplate(input, target) {
938
+ if (context.isOptional) {
939
+ return ` ${target} += serializeDate(${input}!)`;
940
+ }
941
+ if (schema.nullable) {
942
+ return ` if ${input} != nil {
943
+ ${target} += serializeDate(${input}!)
944
+ } else {
945
+ ${target} += "null"
946
+ }`;
947
+ }
948
+ return ` ${target} += serializeDate(${input})`;
949
+ },
950
+ toQueryPartTemplate(input, target, key) {
951
+ if (context.isOptional) {
952
+ return ` if ${input} != nil {
953
+ ${target}.append(URLQueryItem(name: "${key}", value: serializeDate(${input}!, withQuotes: false)))
954
+ }`;
955
+ }
956
+ if (schema.nullable) {
957
+ return ` if ${input} != nil {
958
+ ${target}.append(URLQueryItem(name: "${key}", value: serializeDate(${input}!, withQuotes: false)))
959
+ } else {
960
+ ${target}.append(URLQueryItem(name: "${key}", value: "null"))
961
+ }`;
962
+ }
963
+ return ` ${target}.append(URLQueryItem(name: "${key}", value: serializeDate(${input}, withQuotes: false)))`;
964
+ },
965
+ content: ""
966
+ };
967
+ }
968
+ function swiftNumberFromSchema(schema, context, typeName, jsonAccessor, defaultValue) {
969
+ const isNullable = isNullableType(schema, context);
970
+ return {
971
+ typeName: isNullable ? `${typeName}?` : typeName,
972
+ defaultValue: isNullable ? "" : defaultValue,
973
+ isNullable,
974
+ canBeQueryString: true,
975
+ hasRequiredRef: false,
976
+ fromJsonTemplate(input, target) {
977
+ if (context.isOptional) {
978
+ return ` if ${input}.exists() {
979
+ ${target} = ${input}.${jsonAccessor}
980
+ }`;
981
+ }
982
+ if (schema.nullable) {
983
+ return ` if ${input}.${jsonAccessor} != nil {
984
+ ${target} = ${input}.${jsonAccessor}
985
+ }`;
986
+ }
987
+ return ` ${target} = ${input}.${jsonAccessor} ?? ${defaultValue}`;
988
+ },
989
+ toJsonTemplate(input, target) {
990
+ if (context.isOptional) {
991
+ return ` ${target} += "\\(${input}!)"`;
992
+ }
993
+ if (schema.nullable) {
994
+ return ` if ${input} != nil {
995
+ ${target} += "\\(${input}!)"
996
+ } else {
997
+ ${target} += "null"
998
+ }`;
999
+ }
1000
+ return ` ${target} += "\\(${input})"`;
1001
+ },
1002
+ toQueryPartTemplate(input, target, key) {
1003
+ if (context.isOptional) {
1004
+ return ` if ${input} != nil {
1005
+ ${target}.append(URLQueryItem(name: "${key}", value: "\\(${input}!)"))
1006
+ }`;
1007
+ }
1008
+ if (schema.nullable) {
1009
+ return ` if ${input} != nil {
1010
+ ${target}.append(URLQueryItem(name: "${key}", value: "\\(${input}!)"))
1011
+ } else {
1012
+ ${target}.append(URLQueryItem(name: "${key}", value: "null"))
1013
+ }`;
1014
+ }
1015
+ return ` ${target}.append(URLQueryItem(name: "${key}", value: "\\(${input})"))`;
1016
+ },
1017
+ content: ""
1018
+ };
1019
+ }
1020
+ function swiftLargeIntFromSchema(schema, context, typeName) {
1021
+ const isNullable = isNullableType(schema, context);
1022
+ return {
1023
+ typeName: isNullable ? `${typeName}?` : typeName,
1024
+ defaultValue: isNullable ? "" : "0",
1025
+ isNullable,
1026
+ canBeQueryString: true,
1027
+ hasRequiredRef: false,
1028
+ fromJsonTemplate(input, target) {
1029
+ if (context.isOptional) {
1030
+ return ` if ${input}.exists() {
1031
+ ${target} = ${typeName}(${input}.string ?? "0")
1032
+ }`;
1033
+ }
1034
+ if (schema.nullable) {
1035
+ return ` if ${input}.string != nil {
1036
+ ${target} = ${typeName}(${input}.string ?? "0")
1037
+ }`;
1038
+ }
1039
+ return ` ${target} = ${typeName}(${input}.string ?? "0") ?? 0`;
1040
+ },
1041
+ toJsonTemplate(input, target) {
1042
+ if (context.isOptional) {
1043
+ return ` ${target} += "\\"\\(${input}!)\\""`;
1044
+ }
1045
+ if (schema.nullable) {
1046
+ return ` if ${input} != nil {
1047
+ ${target} += "\\"\\(${input}!)\\""
1048
+ } else {
1049
+ ${target} += "null"
1050
+ }`;
1051
+ }
1052
+ return ` ${target} += "\\"\\(${input})\\""`;
1053
+ },
1054
+ toQueryPartTemplate(input, target, key) {
1055
+ if (context.isOptional) {
1056
+ return ` if ${input} != nil {
1057
+ ${target}.append(URLQueryItem(name: "${key}", value: "\\(${input}!)"))
1058
+ }`;
1059
+ }
1060
+ if (schema.nullable) {
1061
+ return ` if ${input} != nil {
1062
+ ${target}.append(URLQueryItem(name: "${key}", value: "\\(${input}!)"))
1063
+ } else {
1064
+ ${target}.append(URLQueryItem(name: "${key}", value: "null"))
1065
+ }`;
1066
+ }
1067
+ return ` ${target}.append(URLQueryItem(name: "${key}", value: "\\(${input})"))`;
1068
+ },
1069
+ content: ""
1070
+ };
1071
+ }
1072
+
1073
+ function swiftProcedureFromSchema(schema, context) {
1074
+ switch (schema.transport) {
1075
+ case "http":
1076
+ return swiftHttpProcedureFromSchema(schema, context);
1077
+ case "ws":
1078
+ return swiftWsProcedureFromSchema(schema, context);
1079
+ default:
1080
+ console.warn(
1081
+ `[swift-codegen] Unsupported transport type at ${context.instancePath}`
1082
+ );
1083
+ return "";
1084
+ }
1085
+ }
1086
+ function swiftHttpProcedureFromSchema(schema, context) {
1087
+ const rpcName = getRpcName(context.instancePath);
1088
+ const comments = codeComments(
1089
+ {
1090
+ metadata: {
1091
+ description: schema.description,
1092
+ isDeprecated: schema.isDeprecated
1093
+ }
1094
+ },
1095
+ " "
1096
+ );
1097
+ const params = schema.params ? `${context.typePrefix}${validTypeName(schema.params)}` : void 0;
1098
+ const response = schema.response ? `${context.typePrefix}${validTypeName(schema.response)}` : void 0;
1099
+ if (schema.isEventStream) {
1100
+ return `${comments} public func ${rpcName}(${params ? `_ params: ${params}, ` : ""}options: EventSourceOptions<${response ?? "EmptyArriModel"}>) -> Task<(), Never> {
1101
+ let task = Task {
1102
+ var eventSource = EventSource<${response ?? "EmptyArriModel"}>(
1103
+ url: "\\(self.baseURL)${schema.path}",
1104
+ method: "${schema.method.toUpperCase()}",
1105
+ headers: self.headers,
1106
+ params: ${params ? "params" : "nil"},
1107
+ delegate: self.delegate,
1108
+ clientVersion: "${context.clientVersion}",
1109
+ options: options
1110
+ )
1111
+ await eventSource.sendRequest()
1112
+ }
1113
+ return task
1114
+ }`;
1115
+ }
1116
+ return `${comments} public func ${rpcName}(${params ? `_ params: ${params}` : ""}) async throws -> ${response ?? "()"} {
1117
+ ${response ? `let result: ${response} = ` : "let _: EmptyArriModel = "}try await parsedArriHttpRequest(
1118
+ delegate: self.delegate,
1119
+ url: "\\(self.baseURL)${schema.path}",
1120
+ method: "${schema.method.toUpperCase()}",
1121
+ headers: self.headers,
1122
+ clientVersion: "${context.clientVersion}",
1123
+ ${params ? `params: params` : "params: EmptyArriModel()"}
1124
+ )
1125
+ ${response ? `return result` : ""}
1126
+ }`;
1127
+ }
1128
+ function getRpcName(instancePath) {
1129
+ const part = instancePath.split(".").pop();
1130
+ if (!part) {
1131
+ throw new Error(`Error determining procedure name at ${instancePath}`);
1132
+ }
1133
+ return validSwiftKey(part);
1134
+ }
1135
+ function swiftWsProcedureFromSchema(schema, context) {
1136
+ console.warn(
1137
+ "[swift-codegen] Websocket procedures are not supported at this time."
1138
+ );
1139
+ const name = getRpcName(context.instancePath);
1140
+ const params = schema.params ? `${context.typePrefix}${validTypeName(schema.params)}` : void 0;
1141
+ const response = schema.response ? `${context.typePrefix}${validTypeName(schema.response)}` : void 0;
1142
+ const comments = codeComments(
1143
+ {
1144
+ metadata: {
1145
+ description: schema.description,
1146
+ isDeprecated: schema.isDeprecated
1147
+ }
1148
+ },
1149
+ ` `
1150
+ );
1151
+ return `${comments} public func ${name}(${params ? `_ params: ${params}` : ""}) async throws -> ${response ?? "()"} {
1152
+ throw ArriRequestError.notImplemented
1153
+ }`;
1154
+ }
1155
+ function swiftServiceFromSchema(schema, context) {
1156
+ const serviceName = getServiceName(
1157
+ context.instancePath,
1158
+ context.clientName
1159
+ );
1160
+ const services = [];
1161
+ const procedureParts = [];
1162
+ const subContent = [];
1163
+ for (const key of Object.keys(schema)) {
1164
+ const subSchema = schema[key];
1165
+ if (isServiceDefinition(subSchema)) {
1166
+ const subService = swiftServiceFromSchema(subSchema, {
1167
+ clientVersion: context.clientVersion,
1168
+ clientName: context.clientName,
1169
+ typePrefix: context.typePrefix,
1170
+ instancePath: `${context.instancePath}.${key}`,
1171
+ schemaPath: `${context.schemaPath}.${key}`,
1172
+ generatedTypes: context.generatedTypes,
1173
+ containsRequiredRef: context.containsRequiredRef
1174
+ });
1175
+ if (subService) {
1176
+ const subServiceKey = validSwiftKey(key);
1177
+ const subServiceName = getServiceName(
1178
+ `${context.instancePath}.${key}`,
1179
+ context.clientName
1180
+ );
1181
+ services.push({
1182
+ key: subServiceKey,
1183
+ typeName: subServiceName
1184
+ });
1185
+ subContent.push(subService);
1186
+ }
1187
+ continue;
1188
+ }
1189
+ if (isRpcDefinition(subSchema)) {
1190
+ const rpc = swiftProcedureFromSchema(subSchema, {
1191
+ clientVersion: context.clientVersion,
1192
+ clientName: context.clientName,
1193
+ typePrefix: context.typePrefix,
1194
+ instancePath: `${context.instancePath}.${key}`,
1195
+ schemaPath: `${context.schemaPath}.${key}`,
1196
+ generatedTypes: context.generatedTypes,
1197
+ containsRequiredRef: context.containsRequiredRef
1198
+ });
1199
+ if (rpc) {
1200
+ procedureParts.push(rpc);
1201
+ }
1202
+ continue;
1203
+ }
1204
+ }
1205
+ return `public class ${serviceName} {
1206
+ let baseURL: String
1207
+ let delegate: ArriRequestDelegate
1208
+ let headers: () -> Dictionary<String, String>
1209
+ ${services.map((service) => ` public let ${service.key}: ${service.typeName}`).join("\n")}
1210
+ public init(
1211
+ baseURL: String,
1212
+ delegate: ArriRequestDelegate,
1213
+ headers: @escaping () -> Dictionary<String, String>
1214
+ ) {
1215
+ self.baseURL = baseURL
1216
+ self.delegate = delegate
1217
+ self.headers = headers
1218
+ ${services.map(
1219
+ (service) => ` self.${service.key} = ${service.typeName}(
1220
+ baseURL: baseURL,
1221
+ delegate: delegate,
1222
+ headers: headers
1223
+ )`
1224
+ ).join("\n")}
1225
+ }
1226
+ ${procedureParts.join("\n")}
1227
+
1228
+ }
1229
+
1230
+ ${subContent.join("\n")}`;
1231
+ }
1232
+ function getServiceName(instancePath, clientName) {
1233
+ if (instancePath.length === 0) {
1234
+ return clientName;
1235
+ }
1236
+ const name = `${clientName}${validTypeName(instancePath.split(".").join("_"))}Service`;
1237
+ return name;
1238
+ }
1239
+
1240
+ function swiftDictionaryFromSchema(schema, context) {
1241
+ const subType = swiftTypeFromSchema(schema.values, {
1242
+ clientVersion: context.clientVersion,
1243
+ clientName: context.clientName,
1244
+ typePrefix: context.typePrefix,
1245
+ instancePath: `${context.instancePath}/[value]`,
1246
+ schemaPath: `${context.schemaPath}/values`,
1247
+ generatedTypes: context.generatedTypes,
1248
+ containsRequiredRef: context.containsRequiredRef
1249
+ });
1250
+ const isNullable = isNullableType(schema, context);
1251
+ const typeName = isNullable ? `Dictionary<String, ${subType.typeName}>?` : `Dictionary<String, ${subType.typeName}>`;
1252
+ const defaultValue = isNullable ? `` : `Dictionary()`;
1253
+ return {
1254
+ typeName,
1255
+ isNullable,
1256
+ defaultValue,
1257
+ canBeQueryString: false,
1258
+ hasRequiredRef: false,
1259
+ fromJsonTemplate(input, target, _) {
1260
+ const mainContent = ` ${target} = Dictionary()
1261
+ for (__key, __value) in ${input}.dictionary ?? Dictionary() {
1262
+ var __parsedValue: ${subType.typeName}
1263
+ ${subType.fromJsonTemplate(`__value`, `__parsedValue`, `__parsedValue`)}
1264
+ ${target}${isNullable ? "!" : ""}[__key] = __parsedValue
1265
+ }`;
1266
+ if (context.isOptional) {
1267
+ return ` if ${input}.exists() {
1268
+ ${mainContent}
1269
+ }`;
1270
+ }
1271
+ if (schema.nullable) {
1272
+ return ` if ${input}.dictionary != nil {
1273
+ ${mainContent}
1274
+ }`;
1275
+ }
1276
+ return mainContent;
1277
+ },
1278
+ toJsonTemplate(input, target) {
1279
+ const mainContent = ` ${target} += "{"
1280
+ for (__index, (__key, __value)) in ${input}${isNullable ? "!" : ""}.enumerated() {
1281
+ if __index > 0 {
1282
+ ${target} += ","
1283
+ }
1284
+ ${target} += "\\"\\(__key)\\":"
1285
+ ${subType.toJsonTemplate("__value", target)}
1286
+ }
1287
+ ${target} += "}"`;
1288
+ if (schema.nullable) {
1289
+ return `if ${input} != nil {
1290
+ ${mainContent}
1291
+ } else {
1292
+ ${target} += "null"
1293
+ }`;
1294
+ }
1295
+ return mainContent;
1296
+ },
1297
+ toQueryPartTemplate(_, __, ___) {
1298
+ return ` print("[WARNING] nested objects cannot be serialized to query params. Skipping field at ${context.instancePath}.")`;
1299
+ },
1300
+ cloneTemplate(input, key) {
1301
+ const innerKey = validSwiftKey(key);
1302
+ const subTypeClonedResult = subType.cloneTemplate?.(
1303
+ `__${innerKey}Value`,
1304
+ `__${innerKey}Value`
1305
+ );
1306
+ if (isNullable) {
1307
+ return {
1308
+ bodyContent: `var __${innerKey}Cloned: ${typeName}
1309
+ if ${input} != nil {
1310
+ __${innerKey}Cloned = Dictionary()
1311
+ for (__${innerKey}Key, __${innerKey}Value) in ${input}! {
1312
+ ${subTypeClonedResult?.bodyContent ?? ""}
1313
+ __${innerKey}Cloned![__${innerKey}Key] = ${subTypeClonedResult?.fieldContent ?? `__${innerKey}Value`}
1314
+ }
1315
+ }`,
1316
+ fieldContent: `__${innerKey}Cloned`
1317
+ };
1318
+ }
1319
+ return {
1320
+ bodyContent: `var __${innerKey}Cloned: ${typeName} = Dictionary()
1321
+ for (__${innerKey}Key, __${innerKey}Value) in ${input} {
1322
+ ${subTypeClonedResult?.bodyContent ?? ""}
1323
+ __${innerKey}Cloned[__${innerKey}Key] = ${subTypeClonedResult?.fieldContent ?? `__${innerKey}Value`}
1324
+ }`,
1325
+ fieldContent: `__${innerKey}Cloned`
1326
+ };
1327
+ },
1328
+ content: subType.content
1329
+ };
1330
+ }
1331
+
1332
+ function swiftRefFromSchema(schema, context) {
1333
+ const typeName = validTypeName(schema.ref);
1334
+ const prefixedTypeName = `${context.typePrefix}${typeName}`;
1335
+ const isNullable = isNullableType(schema, context);
1336
+ const defaultValue = isNullable ? `` : `${prefixedTypeName}()`;
1337
+ return {
1338
+ typeName: isNullable ? `${prefixedTypeName}?` : prefixedTypeName,
1339
+ isNullable,
1340
+ defaultValue,
1341
+ canBeQueryString: false,
1342
+ hasRequiredRef: !isNullable,
1343
+ fromJsonTemplate(input, target, _key) {
1344
+ if (context.isOptional) {
1345
+ return `if ${input}.exists() {
1346
+ ${target} = ${prefixedTypeName}(json: ${input})
1347
+ }`;
1348
+ }
1349
+ if (schema.nullable) {
1350
+ return `if ${input}.dictionary != nil {
1351
+ ${target} = ${prefixedTypeName}(json: ${input})
1352
+ }`;
1353
+ }
1354
+ return `${target} = ${prefixedTypeName}(json: ${input})`;
1355
+ },
1356
+ toJsonTemplate(input, target) {
1357
+ const mainContent = `${target} += ${input}${isNullable ? "!" : ""}.toJSONString()`;
1358
+ if (schema.nullable) {
1359
+ return `if ${input} != nil {
1360
+ ${mainContent}
1361
+ } else {
1362
+ ${target} += "null"
1363
+ }`;
1364
+ }
1365
+ return mainContent;
1366
+ },
1367
+ toQueryPartTemplate(_, __, ___) {
1368
+ return `print("[WARNING] nested objects cannot be serialized to query params. Skipping field at ${context.instancePath}.")`;
1369
+ },
1370
+ cloneTemplate(input, _) {
1371
+ return {
1372
+ bodyContent: "",
1373
+ fieldContent: `${input}${isNullable ? "?" : ""}.clone()`
1374
+ };
1375
+ },
1376
+ content: ""
1377
+ };
1378
+ }
1379
+
1380
+ const swiftClientGenerator = defineGeneratorPlugin(
1381
+ (options) => {
1382
+ return {
1383
+ async generator(def, _isDevServer) {
1384
+ const content = createSwiftClient(def, options);
1385
+ fs.writeFileSync(options.outputFile, content, "utf8");
1386
+ },
1387
+ options
1388
+ };
1389
+ }
1390
+ );
1391
+ function createSwiftClient(def, options) {
1392
+ const context = {
1393
+ clientVersion: def.info?.version ?? "",
1394
+ clientName: options.clientName,
1395
+ typePrefix: options.typePrefix ?? "",
1396
+ instancePath: "",
1397
+ schemaPath: "",
1398
+ generatedTypes: [],
1399
+ containsRequiredRef: {}
1400
+ };
1401
+ const services = unflattenProcedures(def.procedures);
1402
+ const mainService = swiftServiceFromSchema(services, context);
1403
+ const typeContent = [];
1404
+ for (const key of Object.keys(def.definitions)) {
1405
+ const subSchema = def.definitions[key];
1406
+ const subType = swiftTypeFromSchema(subSchema, {
1407
+ clientVersion: context.clientVersion,
1408
+ clientName: context.clientName,
1409
+ typePrefix: context.typePrefix,
1410
+ instancePath: `/${key}`,
1411
+ schemaPath: `/${key}`,
1412
+ generatedTypes: context.generatedTypes,
1413
+ containsRequiredRef: context.containsRequiredRef
1414
+ });
1415
+ if (subType.content) {
1416
+ typeContent.push(subType.content);
1417
+ }
1418
+ }
1419
+ return `import Foundation
1420
+ import ArriClient
1421
+
1422
+ ${mainService}
1423
+ ${typeContent.join("\n")}`;
1424
+ }
1425
+ function swiftTypeFromSchema(schema, context) {
1426
+ if (isSchemaFormType(schema)) {
1427
+ switch (schema.type) {
1428
+ case "string":
1429
+ return swiftStringFromSchema(schema, context);
1430
+ case "boolean":
1431
+ return swiftBooleanFromSchema(schema, context);
1432
+ case "timestamp":
1433
+ return swiftTimestampFromSchema(schema, context);
1434
+ case "float32":
1435
+ return swiftNumberFromSchema(
1436
+ schema,
1437
+ context,
1438
+ "Float32",
1439
+ "float",
1440
+ "0.0"
1441
+ );
1442
+ case "float64":
1443
+ return swiftNumberFromSchema(
1444
+ schema,
1445
+ context,
1446
+ "Float64",
1447
+ "double",
1448
+ "0.0"
1449
+ );
1450
+ case "int8":
1451
+ return swiftNumberFromSchema(
1452
+ schema,
1453
+ context,
1454
+ "Int8",
1455
+ "int8",
1456
+ "0"
1457
+ );
1458
+ case "uint8":
1459
+ return swiftNumberFromSchema(
1460
+ schema,
1461
+ context,
1462
+ "UInt8",
1463
+ "uInt8",
1464
+ "0"
1465
+ );
1466
+ case "int16":
1467
+ return swiftNumberFromSchema(
1468
+ schema,
1469
+ context,
1470
+ "Int16",
1471
+ "int16",
1472
+ "0"
1473
+ );
1474
+ case "uint16":
1475
+ return swiftNumberFromSchema(
1476
+ schema,
1477
+ context,
1478
+ "UInt16",
1479
+ "uInt16",
1480
+ "0"
1481
+ );
1482
+ case "int32":
1483
+ return swiftNumberFromSchema(
1484
+ schema,
1485
+ context,
1486
+ "Int32",
1487
+ "int32",
1488
+ "0"
1489
+ );
1490
+ case "uint32":
1491
+ return swiftNumberFromSchema(
1492
+ schema,
1493
+ context,
1494
+ "UInt32",
1495
+ "uInt32",
1496
+ "0"
1497
+ );
1498
+ case "int64":
1499
+ return swiftLargeIntFromSchema(schema, context, "Int64");
1500
+ case "uint64":
1501
+ return swiftLargeIntFromSchema(schema, context, "UInt64");
1502
+ default:
1503
+ schema.type;
1504
+ break;
1505
+ }
1506
+ }
1507
+ if (isSchemaFormEnum(schema)) {
1508
+ return swiftEnumFromSchema(schema, context);
1509
+ }
1510
+ if (isSchemaFormProperties(schema)) {
1511
+ return swiftObjectFromSchema(schema, context);
1512
+ }
1513
+ if (isSchemaFormElements(schema)) {
1514
+ return swiftArrayFromSchema(schema, context);
1515
+ }
1516
+ if (isSchemaFormValues(schema)) {
1517
+ return swiftDictionaryFromSchema(schema, context);
1518
+ }
1519
+ if (isSchemaFormDiscriminator(schema)) {
1520
+ return swiftTaggedUnionFromSchema(schema, context);
1521
+ }
1522
+ if (isSchemaFormRef(schema)) {
1523
+ return swiftRefFromSchema(schema, context);
1524
+ }
1525
+ return swiftAnyFromSchema(schema, context);
1526
+ }
1527
+
1528
+ export { createSwiftClient, swiftClientGenerator, swiftTypeFromSchema };