@arrirpc/codegen-swift 0.60.0

Sign up to get free protection for your applications and to get access to all the features.
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 };