quicktype 17.0.4 → 17.0.6

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.
Files changed (139) hide show
  1. package/dist/cli/CompressedJSONFromStream.js +38 -26
  2. package/dist/cli/GraphQLIntrospection.js +41 -33
  3. package/dist/cli/TypeSource.d.ts +1 -1
  4. package/dist/cli/URLGrammar.js +3 -4
  5. package/dist/cli/index.js +364 -363
  6. package/dist/quicktype-core/Annotation.js +0 -2
  7. package/dist/quicktype-core/ConvenienceRenderer.d.ts +17 -17
  8. package/dist/quicktype-core/ConvenienceRenderer.js +81 -104
  9. package/dist/quicktype-core/DateTime.js +0 -1
  10. package/dist/quicktype-core/DeclarationIR.d.ts +1 -1
  11. package/dist/quicktype-core/DeclarationIR.js +10 -13
  12. package/dist/quicktype-core/EncodedMarkovChain.js +0 -1
  13. package/dist/quicktype-core/GatherNames.js +25 -47
  14. package/dist/quicktype-core/Graph.d.ts +2 -2
  15. package/dist/quicktype-core/Graph.js +19 -23
  16. package/dist/quicktype-core/GraphRewriting.d.ts +2 -2
  17. package/dist/quicktype-core/GraphRewriting.js +54 -65
  18. package/dist/quicktype-core/MakeTransformations.js +22 -23
  19. package/dist/quicktype-core/MarkovChain.d.ts +3 -3
  20. package/dist/quicktype-core/MarkovChain.js +8 -9
  21. package/dist/quicktype-core/Messages.d.ts +3 -3
  22. package/dist/quicktype-core/Messages.js +3 -8
  23. package/dist/quicktype-core/Naming.d.ts +11 -11
  24. package/dist/quicktype-core/Naming.js +61 -76
  25. package/dist/quicktype-core/Renderer.d.ts +6 -6
  26. package/dist/quicktype-core/Renderer.js +17 -34
  27. package/dist/quicktype-core/RendererOptions.d.ts +5 -5
  28. package/dist/quicktype-core/RendererOptions.js +3 -6
  29. package/dist/quicktype-core/Run.d.ts +9 -9
  30. package/dist/quicktype-core/Run.js +67 -72
  31. package/dist/quicktype-core/Source.d.ts +3 -3
  32. package/dist/quicktype-core/Source.js +18 -19
  33. package/dist/quicktype-core/TargetLanguage.d.ts +10 -10
  34. package/dist/quicktype-core/TargetLanguage.js +2 -6
  35. package/dist/quicktype-core/Transformers.d.ts +25 -25
  36. package/dist/quicktype-core/Transformers.js +66 -94
  37. package/dist/quicktype-core/Type.d.ts +43 -41
  38. package/dist/quicktype-core/Type.js +71 -96
  39. package/dist/quicktype-core/TypeBuilder.d.ts +2 -2
  40. package/dist/quicktype-core/TypeBuilder.js +47 -53
  41. package/dist/quicktype-core/TypeGraph.d.ts +4 -4
  42. package/dist/quicktype-core/TypeGraph.js +40 -49
  43. package/dist/quicktype-core/TypeUtils.d.ts +2 -2
  44. package/dist/quicktype-core/TypeUtils.js +22 -23
  45. package/dist/quicktype-core/UnifyClasses.js +12 -16
  46. package/dist/quicktype-core/UnionBuilder.d.ts +3 -3
  47. package/dist/quicktype-core/UnionBuilder.js +30 -33
  48. package/dist/quicktype-core/attributes/AccessorNames.d.ts +2 -2
  49. package/dist/quicktype-core/attributes/AccessorNames.js +17 -18
  50. package/dist/quicktype-core/attributes/Constraints.d.ts +3 -3
  51. package/dist/quicktype-core/attributes/Constraints.js +4 -8
  52. package/dist/quicktype-core/attributes/Description.js +5 -6
  53. package/dist/quicktype-core/attributes/EnumValues.js +3 -4
  54. package/dist/quicktype-core/attributes/StringTypes.d.ts +1 -1
  55. package/dist/quicktype-core/attributes/StringTypes.js +21 -24
  56. package/dist/quicktype-core/attributes/TypeAttributes.d.ts +3 -3
  57. package/dist/quicktype-core/attributes/TypeAttributes.js +10 -12
  58. package/dist/quicktype-core/attributes/TypeNames.d.ts +9 -9
  59. package/dist/quicktype-core/attributes/TypeNames.js +21 -49
  60. package/dist/quicktype-core/attributes/URIAttributes.d.ts +1 -1
  61. package/dist/quicktype-core/attributes/URIAttributes.js +6 -10
  62. package/dist/quicktype-core/index.js +125 -128
  63. package/dist/quicktype-core/input/CompressedJSON.d.ts +7 -7
  64. package/dist/quicktype-core/input/CompressedJSON.js +54 -49
  65. package/dist/quicktype-core/input/FetchingJSONSchemaStore.js +13 -5
  66. package/dist/quicktype-core/input/Inference.d.ts +2 -2
  67. package/dist/quicktype-core/input/Inference.js +17 -25
  68. package/dist/quicktype-core/input/Inputs.d.ts +2 -2
  69. package/dist/quicktype-core/input/Inputs.js +54 -38
  70. package/dist/quicktype-core/input/JSONSchemaInput.d.ts +11 -11
  71. package/dist/quicktype-core/input/JSONSchemaInput.js +523 -503
  72. package/dist/quicktype-core/input/JSONSchemaStore.d.ts +1 -1
  73. package/dist/quicktype-core/input/JSONSchemaStore.js +35 -24
  74. package/dist/quicktype-core/input/PostmanCollection.js +1 -2
  75. package/dist/quicktype-core/input/io/NodeIO.js +44 -55
  76. package/dist/quicktype-core/input/io/get-stream/index.js +2 -6
  77. package/dist/quicktype-core/language/All.js +2 -3
  78. package/dist/quicktype-core/language/CPlusPlus.d.ts +12 -9
  79. package/dist/quicktype-core/language/CPlusPlus.js +249 -251
  80. package/dist/quicktype-core/language/CSharp.d.ts +7 -7
  81. package/dist/quicktype-core/language/CSharp.js +72 -82
  82. package/dist/quicktype-core/language/Crystal.d.ts +2 -2
  83. package/dist/quicktype-core/language/Crystal.js +17 -18
  84. package/dist/quicktype-core/language/Dart.d.ts +4 -4
  85. package/dist/quicktype-core/language/Dart.js +34 -36
  86. package/dist/quicktype-core/language/Elm.d.ts +6 -6
  87. package/dist/quicktype-core/language/Elm.js +45 -47
  88. package/dist/quicktype-core/language/Golang.d.ts +4 -4
  89. package/dist/quicktype-core/language/Golang.js +20 -23
  90. package/dist/quicktype-core/language/Haskell.d.ts +5 -5
  91. package/dist/quicktype-core/language/Haskell.js +23 -25
  92. package/dist/quicktype-core/language/JSONSchema.d.ts +3 -3
  93. package/dist/quicktype-core/language/JSONSchema.js +16 -17
  94. package/dist/quicktype-core/language/Java.d.ts +2 -2
  95. package/dist/quicktype-core/language/Java.js +97 -97
  96. package/dist/quicktype-core/language/JavaScript.d.ts +7 -7
  97. package/dist/quicktype-core/language/JavaScript.js +17 -19
  98. package/dist/quicktype-core/language/JavaScriptPropTypes.js +13 -15
  99. package/dist/quicktype-core/language/JavaScriptUnicodeMaps.js +11 -12
  100. package/dist/quicktype-core/language/Kotlin.d.ts +2 -2
  101. package/dist/quicktype-core/language/Kotlin.js +37 -39
  102. package/dist/quicktype-core/language/Objective-C.d.ts +3 -3
  103. package/dist/quicktype-core/language/Objective-C.js +41 -45
  104. package/dist/quicktype-core/language/Php.d.ts +2 -2
  105. package/dist/quicktype-core/language/Php.js +41 -66
  106. package/dist/quicktype-core/language/Pike.d.ts +1 -1
  107. package/dist/quicktype-core/language/Pike.js +17 -18
  108. package/dist/quicktype-core/language/Python.d.ts +8 -8
  109. package/dist/quicktype-core/language/Python.js +64 -63
  110. package/dist/quicktype-core/language/Rust.d.ts +2 -2
  111. package/dist/quicktype-core/language/Rust.js +19 -21
  112. package/dist/quicktype-core/language/Swift.d.ts +7 -7
  113. package/dist/quicktype-core/language/Swift.js +804 -807
  114. package/dist/quicktype-core/language/TypeScriptFlow.d.ts +7 -7
  115. package/dist/quicktype-core/language/TypeScriptFlow.js +26 -28
  116. package/dist/quicktype-core/language/ruby/index.d.ts +4 -4
  117. package/dist/quicktype-core/language/ruby/index.js +24 -49
  118. package/dist/quicktype-core/language/ruby/keywords.js +0 -1
  119. package/dist/quicktype-core/rewrites/CombineClasses.js +8 -9
  120. package/dist/quicktype-core/rewrites/ExpandStrings.d.ts +1 -1
  121. package/dist/quicktype-core/rewrites/ExpandStrings.js +17 -18
  122. package/dist/quicktype-core/rewrites/FlattenStrings.js +6 -7
  123. package/dist/quicktype-core/rewrites/FlattenUnions.js +8 -9
  124. package/dist/quicktype-core/rewrites/InferMaps.js +10 -11
  125. package/dist/quicktype-core/rewrites/ReplaceObjectType.js +7 -8
  126. package/dist/quicktype-core/rewrites/ResolveIntersections.js +67 -68
  127. package/dist/quicktype-core/support/Acronyms.js +1 -3
  128. package/dist/quicktype-core/support/Chance.js +0 -10
  129. package/dist/quicktype-core/support/Converters.js +0 -1
  130. package/dist/quicktype-core/support/Strings.d.ts +4 -4
  131. package/dist/quicktype-core/support/Strings.js +16 -17
  132. package/dist/quicktype-core/support/Support.d.ts +1 -1
  133. package/dist/quicktype-core/support/Support.js +6 -30
  134. package/dist/quicktype-graphql-input/GraphQLSchema.d.ts +4 -4
  135. package/dist/quicktype-graphql-input/GraphQLSchema.js +0 -1
  136. package/dist/quicktype-graphql-input/index.d.ts +1 -1
  137. package/dist/quicktype-graphql-input/index.js +212 -229
  138. package/dist/quicktype-typescript-input/index.js +4 -28
  139. package/package.json +19 -25
@@ -1,6 +1,5 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.SwiftRenderer = exports.SwiftTargetLanguage = exports.swiftOptions = void 0;
4
3
  const collection_utils_1 = require("collection-utils");
5
4
  const Support_1 = require("../support/Support");
6
5
  const TargetLanguage_1 = require("../TargetLanguage");
@@ -27,7 +26,7 @@ exports.swiftOptions = {
27
26
  ["class", true]
28
27
  ]),
29
28
  mutableProperties: new RendererOptions_1.BooleanOption("mutable-properties", "Use var instead of let for object properties", false),
30
- acronymStyle: (0, Acronyms_1.acronymOption)(Acronyms_1.AcronymStyleOptions.Pascal),
29
+ acronymStyle: Acronyms_1.acronymOption(Acronyms_1.AcronymStyleOptions.Pascal),
31
30
  dense: new RendererOptions_1.EnumOption("density", "Code density", [
32
31
  ["dense", true],
33
32
  ["normal", false]
@@ -99,7 +98,7 @@ class SwiftTargetLanguage extends TargetLanguage_1.TargetLanguage {
99
98
  return true;
100
99
  }
101
100
  makeRenderer(renderContext, untypedOptionValues) {
102
- return new SwiftRenderer(this, renderContext, (0, RendererOptions_1.getOptionValues)(exports.swiftOptions, untypedOptionValues));
101
+ return new SwiftRenderer(this, renderContext, RendererOptions_1.getOptionValues(exports.swiftOptions, untypedOptionValues));
103
102
  }
104
103
  get dateTimeRecognizer() {
105
104
  return new SwiftDateTimeRecognizer();
@@ -208,504 +207,476 @@ const keywords = [
208
207
  "jsonData"
209
208
  ];
210
209
  function isPartCharacter(codePoint) {
211
- return (0, Strings_1.isLetterOrUnderscore)(codePoint) || (0, Strings_1.isNumeric)(codePoint);
210
+ return Strings_1.isLetterOrUnderscore(codePoint) || Strings_1.isNumeric(codePoint);
212
211
  }
213
212
  function isStartCharacter(codePoint) {
214
- return isPartCharacter(codePoint) && !(0, Strings_1.isDigit)(codePoint);
213
+ return isPartCharacter(codePoint) && !Strings_1.isDigit(codePoint);
215
214
  }
216
- const legalizeName = (0, Strings_1.legalizeCharacters)(isPartCharacter);
215
+ const legalizeName = Strings_1.legalizeCharacters(isPartCharacter);
217
216
  function swiftNameStyle(prefix, isUpper, original, acronymsStyle = Strings_1.allUpperWordStyle) {
218
- const words = (0, Strings_1.splitIntoWords)(original);
219
- const combined = (0, Strings_1.combineWords)(words, legalizeName, isUpper ? Strings_1.firstUpperWordStyle : Strings_1.allLowerWordStyle, Strings_1.firstUpperWordStyle, isUpper ? Strings_1.allUpperWordStyle : Strings_1.allLowerWordStyle, acronymsStyle, "", isStartCharacter);
220
- return (0, Strings_1.addPrefixIfNecessary)(prefix, combined);
217
+ const words = Strings_1.splitIntoWords(original);
218
+ const combined = Strings_1.combineWords(words, legalizeName, isUpper ? Strings_1.firstUpperWordStyle : Strings_1.allLowerWordStyle, Strings_1.firstUpperWordStyle, isUpper ? Strings_1.allUpperWordStyle : Strings_1.allLowerWordStyle, acronymsStyle, "", isStartCharacter);
219
+ return Strings_1.addPrefixIfNecessary(prefix, combined);
221
220
  }
222
221
  function unicodeEscape(codePoint) {
223
- return "\\u{" + (0, Strings_1.intToHex)(codePoint, 0) + "}";
222
+ return "\\u{" + Strings_1.intToHex(codePoint, 0) + "}";
224
223
  }
225
- const stringEscape = (0, Strings_1.utf32ConcatMap)((0, Strings_1.escapeNonPrintableMapper)(Strings_1.isPrintable, unicodeEscape));
224
+ const stringEscape = Strings_1.utf32ConcatMap(Strings_1.escapeNonPrintableMapper(Strings_1.isPrintable, unicodeEscape));
226
225
  class SwiftRenderer extends ConvenienceRenderer_1.ConvenienceRenderer {
227
- _options;
228
- _currentFilename;
229
- _needAny = false;
230
- _needNull = false;
231
226
  constructor(targetLanguage, renderContext, _options) {
232
227
  super(targetLanguage, renderContext);
233
228
  this._options = _options;
234
- }
235
- forbiddenNamesForGlobalNamespace() {
236
- if (this._options.alamofire) {
237
- return ["DataRequest", ...keywords];
229
+ this._needAny = false;
230
+ this._needNull = false;
231
+ this.emitSupportFunctions4 = () => {
232
+ this.startFile("JSONSchemaSupport");
233
+ this.emitLineOnce("import Foundation");
234
+ this.forEachTopLevel("leading", (t, name) => this.renderTopLevelAlias(t, name), t => this.namedTypeToNameForTopLevel(t) === undefined);
235
+ if (this._options.convenienceInitializers) {
236
+ this.ensureBlankLine();
237
+ this.forEachTopLevel("leading-and-interposing", (t, name) => this.emitTopLevelMapAndArrayConvenienceInitializerExtensions(t, name));
238
+ }
239
+ if ((!this._options.justTypes && this._options.convenienceInitializers) || this._options.alamofire) {
240
+ this.ensureBlankLine();
241
+ this.emitMark("Helper functions for creating encoders and decoders", true);
242
+ this.ensureBlankLine();
243
+ this.emitNewEncoderDecoder();
244
+ }
245
+ if (this._options.alamofire) {
246
+ this.ensureBlankLine();
247
+ this.emitMark("Alamofire response handlers", true);
248
+ this.ensureBlankLine();
249
+ this.emitAlamofireExtension();
250
+ }
251
+ // This assumes that this method is called after declarations
252
+ // are emitted.
253
+ if (this._needAny || this._needNull) {
254
+ this.ensureBlankLine();
255
+ this.emitMark("Encode/decode helpers", true);
256
+ this.ensureBlankLine();
257
+ if (this._options.objcSupport) {
258
+ this.emitLine(this.objcMembersDeclaration, this.accessLevel, "class JSONNull: NSObject, Codable {");
259
+ }
260
+ else {
261
+ this.emitLine(this.accessLevel, "class JSONNull: Codable, Hashable {");
262
+ }
263
+ this.ensureBlankLine();
264
+ this.emitMultiline(` public static func == (lhs: JSONNull, rhs: JSONNull) -> Bool {
265
+ return true
266
+ }`);
267
+ if (this._options.objcSupport === false) {
268
+ this.ensureBlankLine();
269
+ this.emitMultiline(` public var hashValue: Int {
270
+ return 0
271
+ }`);
272
+ if (this._options.swift5Support) {
273
+ this.ensureBlankLine();
274
+ this.emitMultiline(` public func hash(into hasher: inout Hasher) {
275
+ // No-op
276
+ }`);
277
+ }
278
+ }
279
+ this.ensureBlankLine();
280
+ if (this._options.objcSupport) {
281
+ this.emitItem(" override ");
282
+ }
283
+ else {
284
+ this.emitItem(" ");
285
+ }
286
+ this.emitMultiline(`public init() {}
287
+
288
+ public required init(from decoder: Decoder) throws {
289
+ let container = try decoder.singleValueContainer()
290
+ if !container.decodeNil() {
291
+ throw DecodingError.typeMismatch(JSONNull.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for JSONNull"))
238
292
  }
239
- return keywords;
240
- }
241
- forbiddenForObjectProperties(_c, _classNamed) {
242
- return { names: ["fromURL", "json"], includeGlobalForbidden: true };
243
- }
244
- forbiddenForEnumCases(_e, _enumName) {
245
- return { names: [], includeGlobalForbidden: true };
246
- }
247
- forbiddenForUnionMembers(_u, _unionName) {
248
- return { names: [], includeGlobalForbidden: true };
249
293
  }
250
- makeNamedTypeNamer() {
251
- return (0, Naming_1.funPrefixNamer)("upper", s => swiftNameStyle(this._options.namedTypePrefix, true, s, (0, Acronyms_1.acronymStyle)(this._options.acronymStyle)));
252
- }
253
- namerForObjectProperty() {
254
- return this.lowerNamingFunction;
255
- }
256
- makeUnionMemberNamer() {
257
- return this.lowerNamingFunction;
258
- }
259
- makeEnumCaseNamer() {
260
- return this.lowerNamingFunction;
294
+
295
+ public func encode(to encoder: Encoder) throws {
296
+ var container = encoder.singleValueContainer()
297
+ try container.encodeNil()
261
298
  }
262
- isImplicitCycleBreaker(t) {
263
- const kind = t.kind;
264
- return kind === "array" || kind === "map";
299
+ }`);
300
+ }
301
+ if (this._needAny) {
302
+ this.ensureBlankLine();
303
+ this.emitMultiline(`class JSONCodingKey: CodingKey {
304
+ let key: String
305
+
306
+ required init?(intValue: Int) {
307
+ return nil
265
308
  }
266
- emitDescriptionBlock(lines) {
267
- this.emitCommentLines(lines, "/// ");
309
+
310
+ required init?(stringValue: String) {
311
+ key = stringValue
268
312
  }
269
- emitBlock(line, f) {
270
- this.emitLine(line, " {");
271
- this.indent(f);
272
- this.emitLine("}");
313
+
314
+ var intValue: Int? {
315
+ return nil
273
316
  }
274
- emitBlockWithAccess(line, f) {
275
- this.emitBlock([this.accessLevel, line], f);
317
+
318
+ var stringValue: String {
319
+ return key
276
320
  }
277
- justTypesCase(justTypes, notJustTypes) {
278
- if (this._options.justTypes)
279
- return justTypes;
280
- else
281
- return notJustTypes;
321
+ }`);
322
+ this.ensureBlankLine();
323
+ if (this._options.objcSupport) {
324
+ this.emitLine(this.objcMembersDeclaration, this.accessLevel, "class JSONAny: NSObject, Codable {");
325
+ }
326
+ else {
327
+ this.emitLine(this.accessLevel, "class JSONAny: Codable {");
328
+ }
329
+ this.ensureBlankLine();
330
+ this.emitMultiline(` ${this.accessLevel}let value: Any
331
+
332
+ static func decodingError(forCodingPath codingPath: [CodingKey]) -> DecodingError {
333
+ let context = DecodingError.Context(codingPath: codingPath, debugDescription: "Cannot decode JSONAny")
334
+ return DecodingError.typeMismatch(JSONAny.self, context)
282
335
  }
283
- get lowerNamingFunction() {
284
- return (0, Naming_1.funPrefixNamer)("lower", s => swiftNameStyle("", false, s, (0, Acronyms_1.acronymStyle)(this._options.acronymStyle)));
336
+
337
+ static func encodingError(forValue value: Any, codingPath: [CodingKey]) -> EncodingError {
338
+ let context = EncodingError.Context(codingPath: codingPath, debugDescription: "Cannot encode JSONAny")
339
+ return EncodingError.invalidValue(value, context)
285
340
  }
286
- swiftPropertyType(p) {
287
- if (p.isOptional || (this._options.optionalEnums && p.type.kind === "enum")) {
288
- return [this.swiftType(p.type, true, true), "?"];
341
+
342
+ static func decode(from container: SingleValueDecodingContainer) throws -> Any {
343
+ if let value = try? container.decode(Bool.self) {
344
+ return value
289
345
  }
290
- else {
291
- return this.swiftType(p.type, true);
346
+ if let value = try? container.decode(Int64.self) {
347
+ return value
292
348
  }
293
- }
294
- swiftType(t, withIssues = false, noOptional = false) {
295
- const optional = noOptional ? "" : "?";
296
- return (0, TypeUtils_1.matchType)(t, _anyType => {
297
- this._needAny = true;
298
- return (0, Source_1.maybeAnnotated)(withIssues, Annotation_1.anyTypeIssueAnnotation, this.justTypesCase(["Any", optional], "JSONAny"));
299
- }, _nullType => {
300
- this._needNull = true;
301
- return (0, Source_1.maybeAnnotated)(withIssues, Annotation_1.nullTypeIssueAnnotation, this.justTypesCase("NSNull", ["JSONNull", optional]));
302
- }, _boolType => "Bool", _integerType => "Int", _doubleType => "Double", _stringType => "String", arrayType => ["[", this.swiftType(arrayType.items, withIssues), "]"], classType => this.nameForNamedType(classType), mapType => ["[String: ", this.swiftType(mapType.values, withIssues), "]"], enumType => this.nameForNamedType(enumType), unionType => {
303
- const nullable = (0, TypeUtils_1.nullableFromUnion)(unionType);
304
- if (nullable !== null)
305
- return [this.swiftType(nullable, withIssues), optional];
306
- return this.nameForNamedType(unionType);
307
- }, transformedStringType => {
308
- if (transformedStringType.kind === "date-time") {
309
- return "Date";
310
- }
311
- else {
312
- return (0, Support_2.panic)(`Transformed string type ${transformedStringType.kind} not supported`);
313
- }
314
- });
315
- }
316
- proposedUnionMemberNameForTypeKind(kind) {
317
- if (kind === "enum") {
318
- return "enumeration";
349
+ if let value = try? container.decode(Double.self) {
350
+ return value
319
351
  }
320
- if (kind === "union") {
321
- return "one_of";
352
+ if let value = try? container.decode(String.self) {
353
+ return value
322
354
  }
323
- return null;
324
- }
325
- renderSingleFileHeaderComments() {
326
- this.emitLineOnce("// This file was generated from JSON Schema using quicktype, do not modify it directly.");
327
- this.emitLineOnce("// To parse the JSON, add this file to your project and do:");
328
- this.emitLineOnce("//");
329
- this.forEachTopLevel("none", (t, topLevelName) => {
330
- if (this._options.convenienceInitializers && !(t instanceof Type_1.EnumType)) {
331
- this.emitLineOnce("// let ", (0, Source_1.modifySource)(Strings_1.camelCase, topLevelName), " = try ", topLevelName, "(json)");
332
- }
333
- else {
334
- this.emitLineOnce("// let ", (0, Source_1.modifySource)(Strings_1.camelCase, topLevelName), " = ", "try? JSONDecoder().decode(", topLevelName, ".self, from: jsonData)");
335
- }
336
- });
355
+ if container.decodeNil() {
356
+ return JSONNull()
357
+ }
358
+ throw decodingError(forCodingPath: container.codingPath)
337
359
  }
338
- renderHeader(type, name) {
339
- if (this.leadingComments !== undefined) {
340
- this.emitCommentLines(this.leadingComments);
360
+
361
+ static func decode(from container: inout UnkeyedDecodingContainer) throws -> Any {
362
+ if let value = try? container.decode(Bool.self) {
363
+ return value
341
364
  }
342
- else if (!this._options.justTypes) {
343
- if (this._options.multiFileOutput) {
344
- this.emitLineOnce("// This file was generated from JSON Schema using quicktype, do not modify it directly.");
345
- this.emitLineOnce("// To parse the JSON, add this file to your project and do:");
346
- this.emitLineOnce("//");
347
- if (this._options.convenienceInitializers && !(type instanceof Type_1.EnumType)) {
348
- this.emitLine("// let ", (0, Source_1.modifySource)(Strings_1.camelCase, name), " = try ", name, "(json)");
349
- }
350
- else {
351
- this.emitLine("// let ", (0, Source_1.modifySource)(Strings_1.camelCase, name), " = ", "try? newJSONDecoder().decode(", name, ".self, from: jsonData)");
352
- }
353
- }
354
- if (this._options.alamofire) {
355
- this.emitLine("//");
356
- this.emitLine("// To parse values from Alamofire responses:");
357
- this.emitLine("//");
358
- this.emitLine("// Alamofire.request(url).response", name, " { response in");
359
- this.emitLine("// if let ", (0, Source_1.modifySource)(Strings_1.camelCase, name), " = response.result.value {");
360
- this.emitLine("// ...");
361
- this.emitLine("// }");
362
- this.emitLine("// }");
363
- }
364
- if (this._options.protocol.hashable || this._options.protocol.equatable) {
365
- this.emitLine("//");
366
- this.emitLine("// Hashable or Equatable:");
367
- this.emitLine("// The compiler will not be able to synthesize the implementation of Hashable or Equatable");
368
- this.emitLine("// for types that require the use of JSONAny, nor will the implementation of Hashable be");
369
- this.emitLine("// synthesized for types that have collections (such as arrays or dictionaries).");
365
+ if let value = try? container.decode(Int64.self) {
366
+ return value
367
+ }
368
+ if let value = try? container.decode(Double.self) {
369
+ return value
370
+ }
371
+ if let value = try? container.decode(String.self) {
372
+ return value
373
+ }
374
+ if let value = try? container.decodeNil() {
375
+ if value {
376
+ return JSONNull()
370
377
  }
371
378
  }
372
- this.ensureBlankLine();
373
- this.emitLineOnce("import Foundation");
374
- if (!this._options.justTypes && this._options.alamofire) {
375
- this.emitLineOnce("import Alamofire");
379
+ if var container = try? container.nestedUnkeyedContainer() {
380
+ return try decodeArray(from: &container)
376
381
  }
377
- this.ensureBlankLine();
378
- }
379
- renderTopLevelAlias(t, name) {
380
- this.emitLine(this.accessLevel, "typealias ", name, " = ", this.swiftType(t, true));
382
+ if var container = try? container.nestedContainer(keyedBy: JSONCodingKey.self) {
383
+ return try decodeDictionary(from: &container)
384
+ }
385
+ throw decodingError(forCodingPath: container.codingPath)
381
386
  }
382
- getProtocolsArray(_t, isClass) {
383
- const protocols = [];
384
- // [Michael Fey (@MrRooni), 2019-4-24] Technically NSObject isn't a "protocol" in this instance, but this felt like the best place to slot in this superclass declaration.
385
- if (isClass && this._options.objcSupport) {
386
- protocols.push("NSObject");
387
+
388
+ static func decode(from container: inout KeyedDecodingContainer<JSONCodingKey>, forKey key: JSONCodingKey) throws -> Any {
389
+ if let value = try? container.decode(Bool.self, forKey: key) {
390
+ return value
387
391
  }
388
- if (!this._options.justTypes) {
389
- protocols.push("Codable");
392
+ if let value = try? container.decode(Int64.self, forKey: key) {
393
+ return value
390
394
  }
391
- if (this._options.protocol.hashable) {
392
- protocols.push("Hashable");
395
+ if let value = try? container.decode(Double.self, forKey: key) {
396
+ return value
393
397
  }
394
- if (this._options.protocol.equatable) {
395
- protocols.push("Equatable");
398
+ if let value = try? container.decode(String.self, forKey: key) {
399
+ return value
396
400
  }
397
- return protocols;
401
+ if let value = try? container.decodeNil(forKey: key) {
402
+ if value {
403
+ return JSONNull()
404
+ }
405
+ }
406
+ if var container = try? container.nestedUnkeyedContainer(forKey: key) {
407
+ return try decodeArray(from: &container)
408
+ }
409
+ if var container = try? container.nestedContainer(keyedBy: JSONCodingKey.self, forKey: key) {
410
+ return try decodeDictionary(from: &container)
411
+ }
412
+ throw decodingError(forCodingPath: container.codingPath)
398
413
  }
399
- getProtocolString(_t, isClass) {
400
- const protocols = this.getProtocolsArray(_t, isClass);
401
- return protocols.length > 0 ? ": " + protocols.join(", ") : "";
402
- }
403
- getEnumPropertyGroups(c) {
404
- let groups = [];
405
- let group = [];
406
- this.forEachClassProperty(c, "none", (name, jsonName) => {
407
- const label = stringEscape(jsonName);
408
- const redundant = this.sourcelikeToString(name) === label;
409
- if (this._options.dense && redundant) {
410
- group.push({ name });
411
- }
412
- else {
413
- if (group.length > 0) {
414
- groups.push(group);
415
- group = [];
416
- }
417
- groups.push([{ name, label }]);
418
- }
419
- });
420
- if (group.length > 0) {
421
- groups.push(group);
414
+
415
+ static func decodeArray(from container: inout UnkeyedDecodingContainer) throws -> [Any] {
416
+ var arr: [Any] = []
417
+ while !container.isAtEnd {
418
+ let value = try decode(from: &container)
419
+ arr.append(value)
422
420
  }
423
- return groups;
421
+ return arr
424
422
  }
425
- /// Access level with trailing space (e.g. "public "), or empty string
426
- get accessLevel() {
427
- return this._options.accessLevel === "internal"
428
- ? "" // internal is default, so we don't have to emit it
429
- : this._options.accessLevel + " ";
423
+
424
+ static func decodeDictionary(from container: inout KeyedDecodingContainer<JSONCodingKey>) throws -> [String: Any] {
425
+ var dict = [String: Any]()
426
+ for key in container.allKeys {
427
+ let value = try decode(from: &container, forKey: key)
428
+ dict[key.stringValue] = value
429
+ }
430
+ return dict
430
431
  }
431
- get objcMembersDeclaration() {
432
- if (this._options.objcSupport) {
433
- return "@objcMembers ";
432
+
433
+ static func encode(to container: inout UnkeyedEncodingContainer, array: [Any]) throws {
434
+ for value in array {
435
+ if let value = value as? Bool {
436
+ try container.encode(value)
437
+ } else if let value = value as? Int64 {
438
+ try container.encode(value)
439
+ } else if let value = value as? Double {
440
+ try container.encode(value)
441
+ } else if let value = value as? String {
442
+ try container.encode(value)
443
+ } else if value is JSONNull {
444
+ try container.encodeNil()
445
+ } else if let value = value as? [Any] {
446
+ var container = container.nestedUnkeyedContainer()
447
+ try encode(to: &container, array: value)
448
+ } else if let value = value as? [String: Any] {
449
+ var container = container.nestedContainer(keyedBy: JSONCodingKey.self)
450
+ try encode(to: &container, dictionary: value)
451
+ } else {
452
+ throw encodingError(forValue: value, codingPath: container.codingPath)
453
+ }
434
454
  }
435
- return "";
436
455
  }
437
- /// startFile takes a file name, appends ".swift" to it and sets it as the current filename.
438
- startFile(basename) {
439
- if (this._options.multiFileOutput === false) {
440
- return;
456
+
457
+ static func encode(to container: inout KeyedEncodingContainer<JSONCodingKey>, dictionary: [String: Any]) throws {
458
+ for (key, value) in dictionary {
459
+ let key = JSONCodingKey(stringValue: key)!
460
+ if let value = value as? Bool {
461
+ try container.encode(value, forKey: key)
462
+ } else if let value = value as? Int64 {
463
+ try container.encode(value, forKey: key)
464
+ } else if let value = value as? Double {
465
+ try container.encode(value, forKey: key)
466
+ } else if let value = value as? String {
467
+ try container.encode(value, forKey: key)
468
+ } else if value is JSONNull {
469
+ try container.encodeNil(forKey: key)
470
+ } else if let value = value as? [Any] {
471
+ var container = container.nestedUnkeyedContainer(forKey: key)
472
+ try encode(to: &container, array: value)
473
+ } else if let value = value as? [String: Any] {
474
+ var container = container.nestedContainer(keyedBy: JSONCodingKey.self, forKey: key)
475
+ try encode(to: &container, dictionary: value)
476
+ } else {
477
+ throw encodingError(forValue: value, codingPath: container.codingPath)
478
+ }
441
479
  }
442
- (0, Support_1.assert)(this._currentFilename === undefined, "Previous file wasn't finished: " + this._currentFilename);
443
- // FIXME: The filenames should actually be Sourcelikes, too
444
- this._currentFilename = `${this.sourcelikeToString(basename)}.swift`;
445
- this.initializeEmitContextForFilename(this._currentFilename);
446
480
  }
447
- /// endFile pushes the current file name onto the collection of finished files and then resets the current file name. These finished files are used in index.ts to write the output.
448
- endFile() {
449
- if (this._options.multiFileOutput === false) {
450
- return;
481
+
482
+ static func encode(to container: inout SingleValueEncodingContainer, value: Any) throws {
483
+ if let value = value as? Bool {
484
+ try container.encode(value)
485
+ } else if let value = value as? Int64 {
486
+ try container.encode(value)
487
+ } else if let value = value as? Double {
488
+ try container.encode(value)
489
+ } else if let value = value as? String {
490
+ try container.encode(value)
491
+ } else if value is JSONNull {
492
+ try container.encodeNil()
493
+ } else {
494
+ throw encodingError(forValue: value, codingPath: container.codingPath)
451
495
  }
452
- this.finishFile((0, Support_1.defined)(this._currentFilename));
453
- this._currentFilename = undefined;
454
496
  }
455
- propertyLinesDefinition(name, parameter) {
456
- const useMutableProperties = this._options.mutableProperties;
457
- return [
458
- this.accessLevel,
459
- useMutableProperties ? "var " : "let ",
460
- name,
461
- ": ",
462
- this.swiftPropertyType(parameter)
463
- ];
497
+
498
+ public required init(from decoder: Decoder) throws {
499
+ if var arrayContainer = try? decoder.unkeyedContainer() {
500
+ self.value = try JSONAny.decodeArray(from: &arrayContainer)
501
+ } else if var container = try? decoder.container(keyedBy: JSONCodingKey.self) {
502
+ self.value = try JSONAny.decodeDictionary(from: &container)
503
+ } else {
504
+ let container = try decoder.singleValueContainer()
505
+ self.value = try JSONAny.decode(from: container)
506
+ }
464
507
  }
465
- renderClassDefinition(c, className) {
466
- this.startFile(className);
467
- this.renderHeader(c, className);
468
- this.emitDescription(this.descriptionForType(c));
469
- this.emitMark(this.sourcelikeToString(className), true);
470
- const isClass = this._options.useClasses || this.isCycleBreakerType(c);
471
- const structOrClass = isClass ? "class" : "struct";
472
- if (isClass && this._options.objcSupport) {
473
- // [Michael Fey (@MrRooni), 2019-4-24] Swift 5 or greater, must come before the access declaration for the class.
474
- this.emitItem(this.objcMembersDeclaration);
508
+
509
+ public func encode(to encoder: Encoder) throws {
510
+ if let arr = self.value as? [Any] {
511
+ var container = encoder.unkeyedContainer()
512
+ try JSONAny.encode(to: &container, array: arr)
513
+ } else if let dict = self.value as? [String: Any] {
514
+ var container = encoder.container(keyedBy: JSONCodingKey.self)
515
+ try JSONAny.encode(to: &container, dictionary: dict)
516
+ } else {
517
+ var container = encoder.singleValueContainer()
518
+ try JSONAny.encode(to: &container, value: self.value)
475
519
  }
476
- this.emitBlockWithAccess([structOrClass, " ", className, this.getProtocolString(c, isClass)], () => {
477
- if (this._options.dense) {
478
- let lastProperty = undefined;
479
- let lastNames = [];
480
- const emitLastProperty = () => {
481
- if (lastProperty === undefined)
482
- return;
483
- const useMutableProperties = this._options.mutableProperties;
484
- let sources = [
485
- [
486
- this._options.optionalEnums && lastProperty.type.kind === "enum"
487
- ? `@NilOnFail${this._options.namedTypePrefix} `
488
- : "",
489
- this.accessLevel,
490
- useMutableProperties || (this._options.optionalEnums && lastProperty.type.kind === "enum")
491
- ? "var "
492
- : "let "
493
- ]
494
- ];
495
- lastNames.forEach((n, i) => {
496
- if (i > 0)
497
- sources.push(", ");
498
- sources.push(n);
499
- });
500
- sources.push(": ");
501
- sources.push(this.swiftPropertyType(lastProperty));
502
- this.emitLine(sources);
503
- lastProperty = undefined;
504
- lastNames = [];
505
- };
506
- this.forEachClassProperty(c, "none", (name, jsonName, p) => {
507
- const description = this.descriptionForClassProperty(c, jsonName);
508
- if (!p.equals(lastProperty) ||
509
- lastNames.length >= MAX_SAMELINE_PROPERTIES ||
510
- description !== undefined) {
511
- emitLastProperty();
512
- }
513
- if (lastProperty === undefined) {
514
- lastProperty = p;
515
- }
516
- lastNames.push(name);
517
- if (description !== undefined) {
518
- this.emitDescription(description);
519
- emitLastProperty();
520
- }
521
- });
522
- emitLastProperty();
523
- }
524
- else {
525
- this.forEachClassProperty(c, "none", (name, jsonName, p) => {
526
- const description = this.descriptionForClassProperty(c, jsonName);
527
- const propertyLines = this.propertyLinesDefinition(name, p);
528
- this.emitDescription(description);
529
- this.emitLine(propertyLines);
530
- });
531
- }
532
- if (!this._options.justTypes) {
533
- const groups = this.getEnumPropertyGroups(c);
534
- const allPropertiesRedundant = groups.every(group => {
535
- return group.every(p => p.label === undefined);
536
- });
537
- if (!allPropertiesRedundant && c.getProperties().size > 0) {
538
- this.ensureBlankLine();
539
- this.emitBlock("enum CodingKeys: String, CodingKey", () => {
540
- for (const group of groups) {
541
- const { name, label } = group[0];
542
- if (this._options.explicitCodingKeys && label !== undefined) {
543
- this.emitLine("case ", name, ' = "', label, '"');
544
- }
545
- else {
546
- const names = (0, collection_utils_1.arrayIntercalate)(", ", group.map(p => p.name));
547
- this.emitLine("case ", names);
548
- }
549
- }
550
- });
551
- }
552
- }
553
- // this main initializer must be defined within the class
554
- // declaration since it assigns let constants
555
- if (isClass ||
556
- // Public structs need explicit initializers
557
- // https://github.com/quicktype/quicktype/issues/899
558
- this._options.accessLevel === "public") {
559
- // Make an initializer that initalizes all fields
560
- this.ensureBlankLine();
561
- let initProperties = this.initializableProperties(c);
562
- let propertiesLines = [];
563
- for (let property of initProperties) {
564
- if (propertiesLines.length > 0)
565
- propertiesLines.push(", ");
566
- propertiesLines.push(property.name, ": ", this.swiftPropertyType(property.parameter));
567
- }
568
- if (this.propertyCount(c) === 0 && this._options.objcSupport) {
569
- this.emitBlockWithAccess(["override init()"], () => {
570
- return "";
571
- });
572
- }
573
- else {
574
- this.emitBlockWithAccess(["init(", ...propertiesLines, ")"], () => {
575
- for (let property of initProperties) {
576
- this.emitLine("self.", property.name, " = ", property.name);
577
- }
578
- });
579
- }
580
- }
581
- });
582
- if (!this._options.justTypes) {
583
- // FIXME: We emit only the MARK line for top-level-enum.schema
584
- if (this._options.convenienceInitializers) {
585
- this.ensureBlankLine();
586
- this.emitMark(this.sourcelikeToString(className) + " convenience initializers and mutators");
587
- this.ensureBlankLine();
588
- this.emitConvenienceInitializersExtension(c, className);
589
- this.ensureBlankLine();
520
+ }
521
+ }`);
590
522
  }
523
+ this.endFile();
524
+ };
525
+ }
526
+ forbiddenNamesForGlobalNamespace() {
527
+ if (this._options.alamofire) {
528
+ return ["DataRequest", ...keywords];
591
529
  }
592
- this.endFile();
530
+ return keywords;
593
531
  }
594
- initializableProperties(c) {
595
- const properties = [];
596
- this.forEachClassProperty(c, "none", (name, jsonName, parameter, position) => {
597
- const property = { name, jsonName, parameter, position };
598
- properties.push(property);
599
- });
600
- return properties;
532
+ forbiddenForObjectProperties(_c, _classNamed) {
533
+ return { names: ["fromURL", "json"], includeGlobalForbidden: true };
601
534
  }
602
- emitNewEncoderDecoder() {
603
- this.emitBlock("func newJSONDecoder() -> JSONDecoder", () => {
604
- this.emitLine("let decoder = JSONDecoder()");
605
- if (!this._options.linux) {
606
- this.emitBlock("if #available(iOS 10.0, OSX 10.12, tvOS 10.0, watchOS 3.0, *)", () => {
607
- this.emitLine("decoder.dateDecodingStrategy = .iso8601");
608
- });
609
- }
610
- else {
611
- this.emitMultiline(`decoder.dateDecodingStrategy = .custom({ (decoder) -> Date in
612
- let container = try decoder.singleValueContainer()
613
- let dateStr = try container.decode(String.self)
614
-
615
- let formatter = DateFormatter()
616
- formatter.calendar = Calendar(identifier: .iso8601)
617
- formatter.locale = Locale(identifier: "en_US_POSIX")
618
- formatter.timeZone = TimeZone(secondsFromGMT: 0)
619
- formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSXXXXX"
620
- if let date = formatter.date(from: dateStr) {
621
- return date
535
+ forbiddenForEnumCases(_e, _enumName) {
536
+ return { names: [], includeGlobalForbidden: true };
622
537
  }
623
- formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssXXXXX"
624
- if let date = formatter.date(from: dateStr) {
625
- return date
538
+ forbiddenForUnionMembers(_u, _unionName) {
539
+ return { names: [], includeGlobalForbidden: true };
626
540
  }
627
- throw DecodingError.typeMismatch(Date.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Could not decode date"))
628
- })`);
629
- }
630
- this.emitLine("return decoder");
631
- });
632
- this.ensureBlankLine();
633
- this.emitBlock("func newJSONEncoder() -> JSONEncoder", () => {
634
- this.emitLine("let encoder = JSONEncoder()");
635
- if (!this._options.linux) {
636
- this.emitBlock("if #available(iOS 10.0, OSX 10.12, tvOS 10.0, watchOS 3.0, *)", () => {
637
- this.emitLine("encoder.dateEncodingStrategy = .iso8601");
638
- });
541
+ makeNamedTypeNamer() {
542
+ return Naming_1.funPrefixNamer("upper", s => swiftNameStyle(this._options.namedTypePrefix, true, s, Acronyms_1.acronymStyle(this._options.acronymStyle)));
543
+ }
544
+ namerForObjectProperty() {
545
+ return this.lowerNamingFunction;
546
+ }
547
+ makeUnionMemberNamer() {
548
+ return this.lowerNamingFunction;
549
+ }
550
+ makeEnumCaseNamer() {
551
+ return this.lowerNamingFunction;
552
+ }
553
+ isImplicitCycleBreaker(t) {
554
+ const kind = t.kind;
555
+ return kind === "array" || kind === "map";
556
+ }
557
+ emitDescriptionBlock(lines) {
558
+ this.emitCommentLines(lines, "/// ");
559
+ }
560
+ emitBlock(line, f) {
561
+ this.emitLine(line, " {");
562
+ this.indent(f);
563
+ this.emitLine("}");
564
+ }
565
+ emitBlockWithAccess(line, f) {
566
+ this.emitBlock([this.accessLevel, line], f);
567
+ }
568
+ justTypesCase(justTypes, notJustTypes) {
569
+ if (this._options.justTypes)
570
+ return justTypes;
571
+ else
572
+ return notJustTypes;
573
+ }
574
+ get lowerNamingFunction() {
575
+ return Naming_1.funPrefixNamer("lower", s => swiftNameStyle("", false, s, Acronyms_1.acronymStyle(this._options.acronymStyle)));
576
+ }
577
+ swiftPropertyType(p) {
578
+ if (p.isOptional || (this._options.optionalEnums && p.type.kind === "enum")) {
579
+ return [this.swiftType(p.type, true, true), "?"];
580
+ }
581
+ else {
582
+ return this.swiftType(p.type, true);
583
+ }
584
+ }
585
+ swiftType(t, withIssues = false, noOptional = false) {
586
+ const optional = noOptional ? "" : "?";
587
+ return TypeUtils_1.matchType(t, _anyType => {
588
+ this._needAny = true;
589
+ return Source_1.maybeAnnotated(withIssues, Annotation_1.anyTypeIssueAnnotation, this.justTypesCase(["Any", optional], "JSONAny"));
590
+ }, _nullType => {
591
+ this._needNull = true;
592
+ return Source_1.maybeAnnotated(withIssues, Annotation_1.nullTypeIssueAnnotation, this.justTypesCase("NSNull", ["JSONNull", optional]));
593
+ }, _boolType => "Bool", _integerType => "Int", _doubleType => "Double", _stringType => "String", arrayType => ["[", this.swiftType(arrayType.items, withIssues), "]"], classType => this.nameForNamedType(classType), mapType => ["[String: ", this.swiftType(mapType.values, withIssues), "]"], enumType => this.nameForNamedType(enumType), unionType => {
594
+ const nullable = TypeUtils_1.nullableFromUnion(unionType);
595
+ if (nullable !== null)
596
+ return [this.swiftType(nullable, withIssues), optional];
597
+ return this.nameForNamedType(unionType);
598
+ }, transformedStringType => {
599
+ if (transformedStringType.kind === "date-time") {
600
+ return "Date";
639
601
  }
640
602
  else {
641
- this.emitMultiline(`let formatter = DateFormatter()
642
- formatter.calendar = Calendar(identifier: .iso8601)
643
- formatter.locale = Locale(identifier: "en_US_POSIX")
644
- formatter.timeZone = TimeZone(secondsFromGMT: 0)
645
- formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssXXXXX"
646
- encoder.dateEncodingStrategy = .formatted(formatter)`);
603
+ return Support_2.panic(`Transformed string type ${transformedStringType.kind} not supported`);
647
604
  }
648
- this.emitLine("return encoder");
649
605
  });
650
606
  }
651
- emitConvenienceInitializersExtension(c, className) {
652
- const isClass = this._options.useClasses || this.isCycleBreakerType(c);
653
- const convenience = isClass ? "convenience " : "";
654
- this.emitBlockWithAccess(["extension ", className], () => {
655
- if (isClass) {
656
- this.emitBlock("convenience init(data: Data) throws", () => {
657
- if (this.propertyCount(c) > 0) {
658
- this.emitLine("let me = try newJSONDecoder().decode(", this.swiftType(c), ".self, from: data)");
659
- }
660
- else {
661
- this.emitLine("let _ = try newJSONDecoder().decode(", this.swiftType(c), ".self, from: data)");
662
- }
663
- let args = [];
664
- this.forEachClassProperty(c, "none", name => {
665
- if (args.length > 0)
666
- args.push(", ");
667
- args.push(name, ": ", "me.", name);
668
- });
669
- this.emitLine("self.init(", ...args, ")");
670
- });
607
+ proposedUnionMemberNameForTypeKind(kind) {
608
+ if (kind === "enum") {
609
+ return "enumeration";
610
+ }
611
+ if (kind === "union") {
612
+ return "one_of";
613
+ }
614
+ return null;
615
+ }
616
+ renderSingleFileHeaderComments() {
617
+ this.emitLineOnce("// This file was generated from JSON Schema using quicktype, do not modify it directly.");
618
+ this.emitLineOnce("// To parse the JSON, add this file to your project and do:");
619
+ this.emitLineOnce("//");
620
+ this.forEachTopLevel("none", (t, topLevelName) => {
621
+ if (this._options.convenienceInitializers && !(t instanceof Type_1.EnumType)) {
622
+ this.emitLineOnce("// let ", Source_1.modifySource(Strings_1.camelCase, topLevelName), " = try ", topLevelName, "(json)");
671
623
  }
672
624
  else {
673
- this.emitBlock("init(data: Data) throws", () => {
674
- this.emitLine("self = try newJSONDecoder().decode(", this.swiftType(c), ".self, from: data)");
675
- });
625
+ this.emitLineOnce("// let ", Source_1.modifySource(Strings_1.camelCase, topLevelName), " = ", "try? JSONDecoder().decode(", topLevelName, ".self, from: jsonData)");
676
626
  }
677
- this.ensureBlankLine();
678
- this.emitBlock([convenience, "init(_ json: String, using encoding: String.Encoding = .utf8) throws"], () => {
679
- this.emitBlock("guard let data = json.data(using: encoding) else", () => {
680
- this.emitLine(`throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)`);
681
- });
682
- this.emitLine("try self.init(data: data)");
683
- });
684
- this.ensureBlankLine();
685
- this.emitBlock([convenience, `init(fromURL url: URL) throws`], () => {
686
- this.emitLine("try self.init(data: try Data(contentsOf: url))");
687
- });
688
- this.ensureBlankLine();
689
- this.emitConvenienceMutator(c, className);
690
- // Convenience serializers
691
- this.ensureBlankLine();
692
- this.emitBlock(`func jsonData() throws -> Data`, () => {
693
- this.emitLine("return try newJSONEncoder().encode(self)");
694
- });
695
- this.ensureBlankLine();
696
- this.emitBlock(`func jsonString(encoding: String.Encoding = .utf8) throws -> String?`, () => {
697
- this.emitLine("return String(data: try self.jsonData(), encoding: encoding)");
698
- });
699
627
  });
700
628
  }
701
- renderEnumDefinition(e, enumName) {
702
- this.startFile(enumName);
629
+ renderHeader(type, name) {
630
+ if (this.leadingComments !== undefined) {
631
+ this.emitCommentLines(this.leadingComments);
632
+ }
633
+ else if (!this._options.justTypes) {
634
+ if (this._options.multiFileOutput) {
635
+ this.emitLineOnce("// This file was generated from JSON Schema using quicktype, do not modify it directly.");
636
+ this.emitLineOnce("// To parse the JSON, add this file to your project and do:");
637
+ this.emitLineOnce("//");
638
+ if (this._options.convenienceInitializers && !(type instanceof Type_1.EnumType)) {
639
+ this.emitLine("// let ", Source_1.modifySource(Strings_1.camelCase, name), " = try ", name, "(json)");
640
+ }
641
+ else {
642
+ this.emitLine("// let ", Source_1.modifySource(Strings_1.camelCase, name), " = ", "try? newJSONDecoder().decode(", name, ".self, from: jsonData)");
643
+ }
644
+ }
645
+ if (this._options.alamofire) {
646
+ this.emitLine("//");
647
+ this.emitLine("// To parse values from Alamofire responses:");
648
+ this.emitLine("//");
649
+ this.emitLine("// Alamofire.request(url).response", name, " { response in");
650
+ this.emitLine("// if let ", Source_1.modifySource(Strings_1.camelCase, name), " = response.result.value {");
651
+ this.emitLine("// ...");
652
+ this.emitLine("// }");
653
+ this.emitLine("// }");
654
+ }
655
+ if (this._options.protocol.hashable || this._options.protocol.equatable) {
656
+ this.emitLine("//");
657
+ this.emitLine("// Hashable or Equatable:");
658
+ this.emitLine("// The compiler will not be able to synthesize the implementation of Hashable or Equatable");
659
+ this.emitLine("// for types that require the use of JSONAny, nor will the implementation of Hashable be");
660
+ this.emitLine("// synthesized for types that have collections (such as arrays or dictionaries).");
661
+ }
662
+ }
663
+ this.ensureBlankLine();
703
664
  this.emitLineOnce("import Foundation");
665
+ if (!this._options.justTypes && this._options.alamofire) {
666
+ this.emitLineOnce("import Alamofire");
667
+ }
704
668
  this.ensureBlankLine();
705
- this.emitDescription(this.descriptionForType(e));
669
+ }
670
+ renderTopLevelAlias(t, name) {
671
+ this.emitLine(this.accessLevel, "typealias ", name, " = ", this.swiftType(t, true));
672
+ }
673
+ getProtocolsArray(_t, isClass) {
706
674
  const protocols = [];
675
+ // [Michael Fey (@MrRooni), 2019-4-24] Technically NSObject isn't a "protocol" in this instance, but this felt like the best place to slot in this superclass declaration.
676
+ if (isClass && this._options.objcSupport) {
677
+ protocols.push("NSObject");
678
+ }
707
679
  if (!this._options.justTypes) {
708
- protocols.push("String"); // Not a protocol
709
680
  protocols.push("Codable");
710
681
  }
711
682
  if (this._options.protocol.hashable) {
@@ -714,423 +685,449 @@ encoder.dateEncodingStrategy = .formatted(formatter)`);
714
685
  if (this._options.protocol.equatable) {
715
686
  protocols.push("Equatable");
716
687
  }
717
- const protocolString = protocols.length > 0 ? ": " + protocols.join(", ") : "";
718
- if (this._options.justTypes) {
719
- this.emitBlockWithAccess(["enum ", enumName, protocolString], () => {
720
- this.forEachEnumCase(e, "none", name => {
721
- this.emitLine("case ", name);
722
- });
723
- });
724
- }
725
- else {
726
- this.emitBlockWithAccess(["enum ", enumName, protocolString], () => {
727
- this.forEachEnumCase(e, "none", (name, jsonName) => {
728
- this.emitLine("case ", name, ' = "', stringEscape(jsonName), '"');
729
- });
730
- });
731
- }
732
- this.endFile();
688
+ return protocols;
733
689
  }
734
- renderUnionDefinition(u, unionName) {
735
- this.startFile(unionName);
736
- this.emitLineOnce("import Foundation");
737
- this.ensureBlankLine();
738
- function sortBy(t) {
739
- const kind = t.kind;
740
- if (kind === "class")
741
- return kind;
742
- return "_" + kind;
743
- }
744
- const renderUnionCase = (t) => {
745
- this.emitBlock(["if let x = try? container.decode(", this.swiftType(t), ".self)"], () => {
746
- this.emitLine("self = .", this.nameForUnionMember(u, t), "(x)");
747
- this.emitLine("return");
748
- });
749
- };
750
- this.emitDescription(this.descriptionForType(u));
751
- const indirect = this.isCycleBreakerType(u) ? "indirect " : "";
752
- const [maybeNull, nonNulls] = (0, TypeUtils_1.removeNullFromUnion)(u, sortBy);
753
- this.emitBlockWithAccess([indirect, "enum ", unionName, this.getProtocolString(u, false)], () => {
754
- this.forEachUnionMember(u, nonNulls, "none", null, (name, t) => {
755
- this.emitLine("case ", name, "(", this.swiftType(t), ")");
756
- });
757
- if (maybeNull !== null) {
758
- this.emitLine("case ", this.nameForUnionMember(u, maybeNull));
690
+ getProtocolString(_t, isClass) {
691
+ const protocols = this.getProtocolsArray(_t, isClass);
692
+ return protocols.length > 0 ? ": " + protocols.join(", ") : "";
693
+ }
694
+ getEnumPropertyGroups(c) {
695
+ let groups = [];
696
+ let group = [];
697
+ this.forEachClassProperty(c, "none", (name, jsonName) => {
698
+ const label = stringEscape(jsonName);
699
+ const redundant = this.sourcelikeToString(name) === label;
700
+ if (this._options.dense && redundant) {
701
+ group.push({ name });
759
702
  }
760
- if (!this._options.justTypes) {
761
- this.ensureBlankLine();
762
- this.emitBlockWithAccess("init(from decoder: Decoder) throws", () => {
763
- this.emitLine("let container = try decoder.singleValueContainer()");
764
- const boolMember = u.findMember("bool");
765
- if (boolMember !== undefined)
766
- renderUnionCase(boolMember);
767
- const integerMember = u.findMember("integer");
768
- if (integerMember !== undefined)
769
- renderUnionCase(integerMember);
770
- for (const t of nonNulls) {
771
- if (t.kind === "bool" || t.kind === "integer")
772
- continue;
773
- renderUnionCase(t);
774
- }
775
- if (maybeNull !== null) {
776
- this.emitBlock("if container.decodeNil()", () => {
777
- this.emitLine("self = .", this.nameForUnionMember(u, maybeNull));
778
- this.emitLine("return");
779
- });
780
- }
781
- this.emitDecodingError(unionName);
782
- });
783
- this.ensureBlankLine();
784
- this.emitBlockWithAccess("func encode(to encoder: Encoder) throws", () => {
785
- this.emitLine("var container = encoder.singleValueContainer()");
786
- this.emitLine("switch self {");
787
- this.forEachUnionMember(u, nonNulls, "none", null, (name, _) => {
788
- this.emitLine("case .", name, "(let x):");
789
- this.indent(() => this.emitLine("try container.encode(x)"));
790
- });
791
- if (maybeNull !== null) {
792
- this.emitLine("case .", this.nameForUnionMember(u, maybeNull), ":");
793
- this.indent(() => this.emitLine("try container.encodeNil()"));
794
- }
795
- this.emitLine("}");
796
- });
703
+ else {
704
+ if (group.length > 0) {
705
+ groups.push(group);
706
+ group = [];
707
+ }
708
+ groups.push([{ name, label }]);
797
709
  }
798
710
  });
799
- this.endFile();
711
+ if (group.length > 0) {
712
+ groups.push(group);
713
+ }
714
+ return groups;
800
715
  }
801
- emitTopLevelMapAndArrayConvenienceInitializerExtensions(t, name) {
802
- let extensionSource;
803
- if (t instanceof Type_1.ArrayType) {
804
- extensionSource = ["Array where Element == ", name, ".Element"];
716
+ /// Access level with trailing space (e.g. "public "), or empty string
717
+ get accessLevel() {
718
+ return this._options.accessLevel === "internal"
719
+ ? "" // internal is default, so we don't have to emit it
720
+ : this._options.accessLevel + " ";
721
+ }
722
+ get objcMembersDeclaration() {
723
+ if (this._options.objcSupport) {
724
+ return "@objcMembers ";
805
725
  }
806
- else if (t instanceof Type_1.MapType) {
807
- extensionSource = ["Dictionary where Key == String, Value == ", this.swiftType(t.values)];
726
+ return "";
727
+ }
728
+ /// startFile takes a file name, appends ".swift" to it and sets it as the current filename.
729
+ startFile(basename) {
730
+ if (this._options.multiFileOutput === false) {
731
+ return;
808
732
  }
809
- else {
733
+ Support_1.assert(this._currentFilename === undefined, "Previous file wasn't finished: " + this._currentFilename);
734
+ // FIXME: The filenames should actually be Sourcelikes, too
735
+ this._currentFilename = `${this.sourcelikeToString(basename)}.swift`;
736
+ this.initializeEmitContextForFilename(this._currentFilename);
737
+ }
738
+ /// endFile pushes the current file name onto the collection of finished files and then resets the current file name. These finished files are used in index.ts to write the output.
739
+ endFile() {
740
+ if (this._options.multiFileOutput === false) {
810
741
  return;
811
742
  }
812
- this.emitBlockWithAccess(["extension ", extensionSource], () => {
813
- this.emitBlock(["init(data: Data) throws"], () => {
814
- this.emitLine("self = try newJSONDecoder().decode(", name, ".self, from: data)");
815
- });
816
- this.ensureBlankLine();
817
- this.emitBlock("init(_ json: String, using encoding: String.Encoding = .utf8) throws", () => {
818
- this.emitBlock("guard let data = json.data(using: encoding) else", () => {
819
- this.emitLine(`throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)`);
820
- });
821
- this.emitLine("try self.init(data: data)");
822
- });
823
- this.ensureBlankLine();
824
- this.emitBlock(`init(fromURL url: URL) throws`, () => {
825
- this.emitLine("try self.init(data: try Data(contentsOf: url))");
826
- });
827
- this.ensureBlankLine();
828
- this.emitBlock("func jsonData() throws -> Data", () => {
829
- this.emitLine("return try newJSONEncoder().encode(self)");
830
- });
831
- this.ensureBlankLine();
832
- this.emitBlock("func jsonString(encoding: String.Encoding = .utf8) throws -> String?", () => {
833
- this.emitLine("return String(data: try self.jsonData(), encoding: encoding)");
834
- });
835
- });
743
+ this.finishFile(Support_1.defined(this._currentFilename));
744
+ this._currentFilename = undefined;
836
745
  }
837
- emitDecodingError(name) {
838
- this.emitLine("throw DecodingError.typeMismatch(", name, '.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for ', name, '"))');
746
+ propertyLinesDefinition(name, parameter) {
747
+ const useMutableProperties = this._options.mutableProperties;
748
+ return [
749
+ this.accessLevel,
750
+ useMutableProperties ? "var " : "let ",
751
+ name,
752
+ ": ",
753
+ this.swiftPropertyType(parameter)
754
+ ];
839
755
  }
840
- emitSupportFunctions4 = () => {
841
- this.startFile("JSONSchemaSupport");
842
- this.emitLineOnce("import Foundation");
843
- this.forEachTopLevel("leading", (t, name) => this.renderTopLevelAlias(t, name), t => this.namedTypeToNameForTopLevel(t) === undefined);
844
- if (this._options.convenienceInitializers) {
845
- this.ensureBlankLine();
846
- this.forEachTopLevel("leading-and-interposing", (t, name) => this.emitTopLevelMapAndArrayConvenienceInitializerExtensions(t, name));
847
- }
848
- if ((!this._options.justTypes && this._options.convenienceInitializers) || this._options.alamofire) {
849
- this.ensureBlankLine();
850
- this.emitMark("Helper functions for creating encoders and decoders", true);
851
- this.ensureBlankLine();
852
- this.emitNewEncoderDecoder();
853
- }
854
- if (this._options.alamofire) {
855
- this.ensureBlankLine();
856
- this.emitMark("Alamofire response handlers", true);
857
- this.ensureBlankLine();
858
- this.emitAlamofireExtension();
756
+ renderClassDefinition(c, className) {
757
+ this.startFile(className);
758
+ this.renderHeader(c, className);
759
+ this.emitDescription(this.descriptionForType(c));
760
+ this.emitMark(this.sourcelikeToString(className), true);
761
+ const isClass = this._options.useClasses || this.isCycleBreakerType(c);
762
+ const structOrClass = isClass ? "class" : "struct";
763
+ if (isClass && this._options.objcSupport) {
764
+ // [Michael Fey (@MrRooni), 2019-4-24] Swift 5 or greater, must come before the access declaration for the class.
765
+ this.emitItem(this.objcMembersDeclaration);
859
766
  }
860
- // This assumes that this method is called after declarations
861
- // are emitted.
862
- if (this._needAny || this._needNull) {
863
- this.ensureBlankLine();
864
- this.emitMark("Encode/decode helpers", true);
865
- this.ensureBlankLine();
866
- if (this._options.objcSupport) {
867
- this.emitLine(this.objcMembersDeclaration, this.accessLevel, "class JSONNull: NSObject, Codable {");
767
+ this.emitBlockWithAccess([structOrClass, " ", className, this.getProtocolString(c, isClass)], () => {
768
+ if (this._options.dense) {
769
+ let lastProperty = undefined;
770
+ let lastNames = [];
771
+ const emitLastProperty = () => {
772
+ if (lastProperty === undefined)
773
+ return;
774
+ const useMutableProperties = this._options.mutableProperties;
775
+ let sources = [
776
+ [
777
+ this._options.optionalEnums && lastProperty.type.kind === "enum"
778
+ ? `@NilOnFail${this._options.namedTypePrefix} `
779
+ : "",
780
+ this.accessLevel,
781
+ useMutableProperties || (this._options.optionalEnums && lastProperty.type.kind === "enum")
782
+ ? "var "
783
+ : "let "
784
+ ]
785
+ ];
786
+ lastNames.forEach((n, i) => {
787
+ if (i > 0)
788
+ sources.push(", ");
789
+ sources.push(n);
790
+ });
791
+ sources.push(": ");
792
+ sources.push(this.swiftPropertyType(lastProperty));
793
+ this.emitLine(sources);
794
+ lastProperty = undefined;
795
+ lastNames = [];
796
+ };
797
+ this.forEachClassProperty(c, "none", (name, jsonName, p) => {
798
+ const description = this.descriptionForClassProperty(c, jsonName);
799
+ if (!p.equals(lastProperty) ||
800
+ lastNames.length >= MAX_SAMELINE_PROPERTIES ||
801
+ description !== undefined) {
802
+ emitLastProperty();
803
+ }
804
+ if (lastProperty === undefined) {
805
+ lastProperty = p;
806
+ }
807
+ lastNames.push(name);
808
+ if (description !== undefined) {
809
+ this.emitDescription(description);
810
+ emitLastProperty();
811
+ }
812
+ });
813
+ emitLastProperty();
868
814
  }
869
815
  else {
870
- this.emitLine(this.accessLevel, "class JSONNull: Codable, Hashable {");
816
+ this.forEachClassProperty(c, "none", (name, jsonName, p) => {
817
+ const description = this.descriptionForClassProperty(c, jsonName);
818
+ const propertyLines = this.propertyLinesDefinition(name, p);
819
+ this.emitDescription(description);
820
+ this.emitLine(propertyLines);
821
+ });
871
822
  }
872
- this.ensureBlankLine();
873
- this.emitMultiline(` public static func == (lhs: JSONNull, rhs: JSONNull) -> Bool {
874
- return true
875
- }`);
876
- if (this._options.objcSupport === false) {
877
- this.ensureBlankLine();
878
- this.emitMultiline(` public var hashValue: Int {
879
- return 0
880
- }`);
881
- if (this._options.swift5Support) {
823
+ if (!this._options.justTypes) {
824
+ const groups = this.getEnumPropertyGroups(c);
825
+ const allPropertiesRedundant = groups.every(group => {
826
+ return group.every(p => p.label === undefined);
827
+ });
828
+ if (!allPropertiesRedundant && c.getProperties().size > 0) {
882
829
  this.ensureBlankLine();
883
- this.emitMultiline(` public func hash(into hasher: inout Hasher) {
884
- // No-op
885
- }`);
830
+ this.emitBlock("enum CodingKeys: String, CodingKey", () => {
831
+ for (const group of groups) {
832
+ const { name, label } = group[0];
833
+ if (this._options.explicitCodingKeys && label !== undefined) {
834
+ this.emitLine("case ", name, ' = "', label, '"');
835
+ }
836
+ else {
837
+ const names = collection_utils_1.arrayIntercalate(", ", group.map(p => p.name));
838
+ this.emitLine("case ", names);
839
+ }
840
+ }
841
+ });
886
842
  }
887
843
  }
888
- this.ensureBlankLine();
889
- if (this._options.objcSupport) {
890
- this.emitItem(" override ");
844
+ // this main initializer must be defined within the class
845
+ // declaration since it assigns let constants
846
+ if (isClass ||
847
+ // Public structs need explicit initializers
848
+ // https://github.com/quicktype/quicktype/issues/899
849
+ this._options.accessLevel === "public") {
850
+ // Make an initializer that initalizes all fields
851
+ this.ensureBlankLine();
852
+ let initProperties = this.initializableProperties(c);
853
+ let propertiesLines = [];
854
+ for (let property of initProperties) {
855
+ if (propertiesLines.length > 0)
856
+ propertiesLines.push(", ");
857
+ propertiesLines.push(property.name, ": ", this.swiftPropertyType(property.parameter));
858
+ }
859
+ if (this.propertyCount(c) === 0 && this._options.objcSupport) {
860
+ this.emitBlockWithAccess(["override init()"], () => {
861
+ return "";
862
+ });
863
+ }
864
+ else {
865
+ this.emitBlockWithAccess(["init(", ...propertiesLines, ")"], () => {
866
+ for (let property of initProperties) {
867
+ this.emitLine("self.", property.name, " = ", property.name);
868
+ }
869
+ });
870
+ }
891
871
  }
892
- else {
893
- this.emitItem(" ");
872
+ });
873
+ if (!this._options.justTypes) {
874
+ // FIXME: We emit only the MARK line for top-level-enum.schema
875
+ if (this._options.convenienceInitializers) {
876
+ this.ensureBlankLine();
877
+ this.emitMark(this.sourcelikeToString(className) + " convenience initializers and mutators");
878
+ this.ensureBlankLine();
879
+ this.emitConvenienceInitializersExtension(c, className);
880
+ this.ensureBlankLine();
894
881
  }
895
- this.emitMultiline(`public init() {}
896
-
897
- public required init(from decoder: Decoder) throws {
898
- let container = try decoder.singleValueContainer()
899
- if !container.decodeNil() {
900
- throw DecodingError.typeMismatch(JSONNull.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for JSONNull"))
901
- }
902
- }
903
-
904
- public func encode(to encoder: Encoder) throws {
905
- var container = encoder.singleValueContainer()
906
- try container.encodeNil()
907
- }
908
- }`);
909
882
  }
910
- if (this._needAny) {
911
- this.ensureBlankLine();
912
- this.emitMultiline(`class JSONCodingKey: CodingKey {
913
- let key: String
914
-
915
- required init?(intValue: Int) {
916
- return nil
883
+ this.endFile();
917
884
  }
918
-
919
- required init?(stringValue: String) {
920
- key = stringValue
885
+ initializableProperties(c) {
886
+ const properties = [];
887
+ this.forEachClassProperty(c, "none", (name, jsonName, parameter, position) => {
888
+ const property = { name, jsonName, parameter, position };
889
+ properties.push(property);
890
+ });
891
+ return properties;
921
892
  }
922
-
923
- var intValue: Int? {
924
- return nil
893
+ emitNewEncoderDecoder() {
894
+ this.emitBlock("func newJSONDecoder() -> JSONDecoder", () => {
895
+ this.emitLine("let decoder = JSONDecoder()");
896
+ if (!this._options.linux) {
897
+ this.emitBlock("if #available(iOS 10.0, OSX 10.12, tvOS 10.0, watchOS 3.0, *)", () => {
898
+ this.emitLine("decoder.dateDecodingStrategy = .iso8601");
899
+ });
900
+ }
901
+ else {
902
+ this.emitMultiline(`decoder.dateDecodingStrategy = .custom({ (decoder) -> Date in
903
+ let container = try decoder.singleValueContainer()
904
+ let dateStr = try container.decode(String.self)
905
+
906
+ let formatter = DateFormatter()
907
+ formatter.calendar = Calendar(identifier: .iso8601)
908
+ formatter.locale = Locale(identifier: "en_US_POSIX")
909
+ formatter.timeZone = TimeZone(secondsFromGMT: 0)
910
+ formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSXXXXX"
911
+ if let date = formatter.date(from: dateStr) {
912
+ return date
925
913
  }
926
-
927
- var stringValue: String {
928
- return key
914
+ formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssXXXXX"
915
+ if let date = formatter.date(from: dateStr) {
916
+ return date
929
917
  }
930
- }`);
931
- this.ensureBlankLine();
932
- if (this._options.objcSupport) {
933
- this.emitLine(this.objcMembersDeclaration, this.accessLevel, "class JSONAny: NSObject, Codable {");
918
+ throw DecodingError.typeMismatch(Date.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Could not decode date"))
919
+ })`);
920
+ }
921
+ this.emitLine("return decoder");
922
+ });
923
+ this.ensureBlankLine();
924
+ this.emitBlock("func newJSONEncoder() -> JSONEncoder", () => {
925
+ this.emitLine("let encoder = JSONEncoder()");
926
+ if (!this._options.linux) {
927
+ this.emitBlock("if #available(iOS 10.0, OSX 10.12, tvOS 10.0, watchOS 3.0, *)", () => {
928
+ this.emitLine("encoder.dateEncodingStrategy = .iso8601");
929
+ });
934
930
  }
935
931
  else {
936
- this.emitLine(this.accessLevel, "class JSONAny: Codable {");
932
+ this.emitMultiline(`let formatter = DateFormatter()
933
+ formatter.calendar = Calendar(identifier: .iso8601)
934
+ formatter.locale = Locale(identifier: "en_US_POSIX")
935
+ formatter.timeZone = TimeZone(secondsFromGMT: 0)
936
+ formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssXXXXX"
937
+ encoder.dateEncodingStrategy = .formatted(formatter)`);
937
938
  }
938
- this.ensureBlankLine();
939
- this.emitMultiline(` ${this.accessLevel}let value: Any
940
-
941
- static func decodingError(forCodingPath codingPath: [CodingKey]) -> DecodingError {
942
- let context = DecodingError.Context(codingPath: codingPath, debugDescription: "Cannot decode JSONAny")
943
- return DecodingError.typeMismatch(JSONAny.self, context)
944
- }
945
-
946
- static func encodingError(forValue value: Any, codingPath: [CodingKey]) -> EncodingError {
947
- let context = EncodingError.Context(codingPath: codingPath, debugDescription: "Cannot encode JSONAny")
948
- return EncodingError.invalidValue(value, context)
949
- }
950
-
951
- static func decode(from container: SingleValueDecodingContainer) throws -> Any {
952
- if let value = try? container.decode(Bool.self) {
953
- return value
954
- }
955
- if let value = try? container.decode(Int64.self) {
956
- return value
957
- }
958
- if let value = try? container.decode(Double.self) {
959
- return value
960
- }
961
- if let value = try? container.decode(String.self) {
962
- return value
963
- }
964
- if container.decodeNil() {
965
- return JSONNull()
966
- }
967
- throw decodingError(forCodingPath: container.codingPath)
939
+ this.emitLine("return encoder");
940
+ });
968
941
  }
969
-
970
- static func decode(from container: inout UnkeyedDecodingContainer) throws -> Any {
971
- if let value = try? container.decode(Bool.self) {
972
- return value
973
- }
974
- if let value = try? container.decode(Int64.self) {
975
- return value
976
- }
977
- if let value = try? container.decode(Double.self) {
978
- return value
979
- }
980
- if let value = try? container.decode(String.self) {
981
- return value
982
- }
983
- if let value = try? container.decodeNil() {
984
- if value {
985
- return JSONNull()
942
+ emitConvenienceInitializersExtension(c, className) {
943
+ const isClass = this._options.useClasses || this.isCycleBreakerType(c);
944
+ const convenience = isClass ? "convenience " : "";
945
+ this.emitBlockWithAccess(["extension ", className], () => {
946
+ if (isClass) {
947
+ this.emitBlock("convenience init(data: Data) throws", () => {
948
+ if (this.propertyCount(c) > 0) {
949
+ this.emitLine("let me = try newJSONDecoder().decode(", this.swiftType(c), ".self, from: data)");
950
+ }
951
+ else {
952
+ this.emitLine("let _ = try newJSONDecoder().decode(", this.swiftType(c), ".self, from: data)");
953
+ }
954
+ let args = [];
955
+ this.forEachClassProperty(c, "none", name => {
956
+ if (args.length > 0)
957
+ args.push(", ");
958
+ args.push(name, ": ", "me.", name);
959
+ });
960
+ this.emitLine("self.init(", ...args, ")");
961
+ });
986
962
  }
987
- }
988
- if var container = try? container.nestedUnkeyedContainer() {
989
- return try decodeArray(from: &container)
990
- }
991
- if var container = try? container.nestedContainer(keyedBy: JSONCodingKey.self) {
992
- return try decodeDictionary(from: &container)
993
- }
994
- throw decodingError(forCodingPath: container.codingPath)
963
+ else {
964
+ this.emitBlock("init(data: Data) throws", () => {
965
+ this.emitLine("self = try newJSONDecoder().decode(", this.swiftType(c), ".self, from: data)");
966
+ });
967
+ }
968
+ this.ensureBlankLine();
969
+ this.emitBlock([convenience, "init(_ json: String, using encoding: String.Encoding = .utf8) throws"], () => {
970
+ this.emitBlock("guard let data = json.data(using: encoding) else", () => {
971
+ this.emitLine(`throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)`);
972
+ });
973
+ this.emitLine("try self.init(data: data)");
974
+ });
975
+ this.ensureBlankLine();
976
+ this.emitBlock([convenience, `init(fromURL url: URL) throws`], () => {
977
+ this.emitLine("try self.init(data: try Data(contentsOf: url))");
978
+ });
979
+ this.ensureBlankLine();
980
+ this.emitConvenienceMutator(c, className);
981
+ // Convenience serializers
982
+ this.ensureBlankLine();
983
+ this.emitBlock(`func jsonData() throws -> Data`, () => {
984
+ this.emitLine("return try newJSONEncoder().encode(self)");
985
+ });
986
+ this.ensureBlankLine();
987
+ this.emitBlock(`func jsonString(encoding: String.Encoding = .utf8) throws -> String?`, () => {
988
+ this.emitLine("return String(data: try self.jsonData(), encoding: encoding)");
989
+ });
990
+ });
995
991
  }
996
-
997
- static func decode(from container: inout KeyedDecodingContainer<JSONCodingKey>, forKey key: JSONCodingKey) throws -> Any {
998
- if let value = try? container.decode(Bool.self, forKey: key) {
999
- return value
1000
- }
1001
- if let value = try? container.decode(Int64.self, forKey: key) {
1002
- return value
1003
- }
1004
- if let value = try? container.decode(Double.self, forKey: key) {
1005
- return value
1006
- }
1007
- if let value = try? container.decode(String.self, forKey: key) {
1008
- return value
992
+ renderEnumDefinition(e, enumName) {
993
+ this.startFile(enumName);
994
+ this.emitLineOnce("import Foundation");
995
+ this.ensureBlankLine();
996
+ this.emitDescription(this.descriptionForType(e));
997
+ const protocols = [];
998
+ if (!this._options.justTypes) {
999
+ protocols.push("String"); // Not a protocol
1000
+ protocols.push("Codable");
1009
1001
  }
1010
- if let value = try? container.decodeNil(forKey: key) {
1011
- if value {
1012
- return JSONNull()
1013
- }
1002
+ if (this._options.protocol.hashable) {
1003
+ protocols.push("Hashable");
1014
1004
  }
1015
- if var container = try? container.nestedUnkeyedContainer(forKey: key) {
1016
- return try decodeArray(from: &container)
1005
+ if (this._options.protocol.equatable) {
1006
+ protocols.push("Equatable");
1017
1007
  }
1018
- if var container = try? container.nestedContainer(keyedBy: JSONCodingKey.self, forKey: key) {
1019
- return try decodeDictionary(from: &container)
1008
+ const protocolString = protocols.length > 0 ? ": " + protocols.join(", ") : "";
1009
+ if (this._options.justTypes) {
1010
+ this.emitBlockWithAccess(["enum ", enumName, protocolString], () => {
1011
+ this.forEachEnumCase(e, "none", name => {
1012
+ this.emitLine("case ", name);
1013
+ });
1014
+ });
1020
1015
  }
1021
- throw decodingError(forCodingPath: container.codingPath)
1022
- }
1023
-
1024
- static func decodeArray(from container: inout UnkeyedDecodingContainer) throws -> [Any] {
1025
- var arr: [Any] = []
1026
- while !container.isAtEnd {
1027
- let value = try decode(from: &container)
1028
- arr.append(value)
1016
+ else {
1017
+ this.emitBlockWithAccess(["enum ", enumName, protocolString], () => {
1018
+ this.forEachEnumCase(e, "none", (name, jsonName) => {
1019
+ this.emitLine("case ", name, ' = "', stringEscape(jsonName), '"');
1020
+ });
1021
+ });
1029
1022
  }
1030
- return arr
1023
+ this.endFile();
1031
1024
  }
1032
-
1033
- static func decodeDictionary(from container: inout KeyedDecodingContainer<JSONCodingKey>) throws -> [String: Any] {
1034
- var dict = [String: Any]()
1035
- for key in container.allKeys {
1036
- let value = try decode(from: &container, forKey: key)
1037
- dict[key.stringValue] = value
1025
+ renderUnionDefinition(u, unionName) {
1026
+ this.startFile(unionName);
1027
+ this.emitLineOnce("import Foundation");
1028
+ this.ensureBlankLine();
1029
+ function sortBy(t) {
1030
+ const kind = t.kind;
1031
+ if (kind === "class")
1032
+ return kind;
1033
+ return "_" + kind;
1038
1034
  }
1039
- return dict
1040
- }
1041
-
1042
- static func encode(to container: inout UnkeyedEncodingContainer, array: [Any]) throws {
1043
- for value in array {
1044
- if let value = value as? Bool {
1045
- try container.encode(value)
1046
- } else if let value = value as? Int64 {
1047
- try container.encode(value)
1048
- } else if let value = value as? Double {
1049
- try container.encode(value)
1050
- } else if let value = value as? String {
1051
- try container.encode(value)
1052
- } else if value is JSONNull {
1053
- try container.encodeNil()
1054
- } else if let value = value as? [Any] {
1055
- var container = container.nestedUnkeyedContainer()
1056
- try encode(to: &container, array: value)
1057
- } else if let value = value as? [String: Any] {
1058
- var container = container.nestedContainer(keyedBy: JSONCodingKey.self)
1059
- try encode(to: &container, dictionary: value)
1060
- } else {
1061
- throw encodingError(forValue: value, codingPath: container.codingPath)
1035
+ const renderUnionCase = (t) => {
1036
+ this.emitBlock(["if let x = try? container.decode(", this.swiftType(t), ".self)"], () => {
1037
+ this.emitLine("self = .", this.nameForUnionMember(u, t), "(x)");
1038
+ this.emitLine("return");
1039
+ });
1040
+ };
1041
+ this.emitDescription(this.descriptionForType(u));
1042
+ const indirect = this.isCycleBreakerType(u) ? "indirect " : "";
1043
+ const [maybeNull, nonNulls] = TypeUtils_1.removeNullFromUnion(u, sortBy);
1044
+ this.emitBlockWithAccess([indirect, "enum ", unionName, this.getProtocolString(u, false)], () => {
1045
+ this.forEachUnionMember(u, nonNulls, "none", null, (name, t) => {
1046
+ this.emitLine("case ", name, "(", this.swiftType(t), ")");
1047
+ });
1048
+ if (maybeNull !== null) {
1049
+ this.emitLine("case ", this.nameForUnionMember(u, maybeNull));
1062
1050
  }
1063
- }
1064
- }
1065
-
1066
- static func encode(to container: inout KeyedEncodingContainer<JSONCodingKey>, dictionary: [String: Any]) throws {
1067
- for (key, value) in dictionary {
1068
- let key = JSONCodingKey(stringValue: key)!
1069
- if let value = value as? Bool {
1070
- try container.encode(value, forKey: key)
1071
- } else if let value = value as? Int64 {
1072
- try container.encode(value, forKey: key)
1073
- } else if let value = value as? Double {
1074
- try container.encode(value, forKey: key)
1075
- } else if let value = value as? String {
1076
- try container.encode(value, forKey: key)
1077
- } else if value is JSONNull {
1078
- try container.encodeNil(forKey: key)
1079
- } else if let value = value as? [Any] {
1080
- var container = container.nestedUnkeyedContainer(forKey: key)
1081
- try encode(to: &container, array: value)
1082
- } else if let value = value as? [String: Any] {
1083
- var container = container.nestedContainer(keyedBy: JSONCodingKey.self, forKey: key)
1084
- try encode(to: &container, dictionary: value)
1085
- } else {
1086
- throw encodingError(forValue: value, codingPath: container.codingPath)
1051
+ if (!this._options.justTypes) {
1052
+ this.ensureBlankLine();
1053
+ this.emitBlockWithAccess("init(from decoder: Decoder) throws", () => {
1054
+ this.emitLine("let container = try decoder.singleValueContainer()");
1055
+ const boolMember = u.findMember("bool");
1056
+ if (boolMember !== undefined)
1057
+ renderUnionCase(boolMember);
1058
+ const integerMember = u.findMember("integer");
1059
+ if (integerMember !== undefined)
1060
+ renderUnionCase(integerMember);
1061
+ for (const t of nonNulls) {
1062
+ if (t.kind === "bool" || t.kind === "integer")
1063
+ continue;
1064
+ renderUnionCase(t);
1065
+ }
1066
+ if (maybeNull !== null) {
1067
+ this.emitBlock("if container.decodeNil()", () => {
1068
+ this.emitLine("self = .", this.nameForUnionMember(u, maybeNull));
1069
+ this.emitLine("return");
1070
+ });
1071
+ }
1072
+ this.emitDecodingError(unionName);
1073
+ });
1074
+ this.ensureBlankLine();
1075
+ this.emitBlockWithAccess("func encode(to encoder: Encoder) throws", () => {
1076
+ this.emitLine("var container = encoder.singleValueContainer()");
1077
+ this.emitLine("switch self {");
1078
+ this.forEachUnionMember(u, nonNulls, "none", null, (name, _) => {
1079
+ this.emitLine("case .", name, "(let x):");
1080
+ this.indent(() => this.emitLine("try container.encode(x)"));
1081
+ });
1082
+ if (maybeNull !== null) {
1083
+ this.emitLine("case .", this.nameForUnionMember(u, maybeNull), ":");
1084
+ this.indent(() => this.emitLine("try container.encodeNil()"));
1085
+ }
1086
+ this.emitLine("}");
1087
+ });
1087
1088
  }
1088
- }
1089
+ });
1090
+ this.endFile();
1089
1091
  }
1090
-
1091
- static func encode(to container: inout SingleValueEncodingContainer, value: Any) throws {
1092
- if let value = value as? Bool {
1093
- try container.encode(value)
1094
- } else if let value = value as? Int64 {
1095
- try container.encode(value)
1096
- } else if let value = value as? Double {
1097
- try container.encode(value)
1098
- } else if let value = value as? String {
1099
- try container.encode(value)
1100
- } else if value is JSONNull {
1101
- try container.encodeNil()
1102
- } else {
1103
- throw encodingError(forValue: value, codingPath: container.codingPath)
1092
+ emitTopLevelMapAndArrayConvenienceInitializerExtensions(t, name) {
1093
+ let extensionSource;
1094
+ if (t instanceof Type_1.ArrayType) {
1095
+ extensionSource = ["Array where Element == ", name, ".Element"];
1104
1096
  }
1105
- }
1106
-
1107
- public required init(from decoder: Decoder) throws {
1108
- if var arrayContainer = try? decoder.unkeyedContainer() {
1109
- self.value = try JSONAny.decodeArray(from: &arrayContainer)
1110
- } else if var container = try? decoder.container(keyedBy: JSONCodingKey.self) {
1111
- self.value = try JSONAny.decodeDictionary(from: &container)
1112
- } else {
1113
- let container = try decoder.singleValueContainer()
1114
- self.value = try JSONAny.decode(from: container)
1097
+ else if (t instanceof Type_1.MapType) {
1098
+ extensionSource = ["Dictionary where Key == String, Value == ", this.swiftType(t.values)];
1115
1099
  }
1116
- }
1117
-
1118
- public func encode(to encoder: Encoder) throws {
1119
- if let arr = self.value as? [Any] {
1120
- var container = encoder.unkeyedContainer()
1121
- try JSONAny.encode(to: &container, array: arr)
1122
- } else if let dict = self.value as? [String: Any] {
1123
- var container = encoder.container(keyedBy: JSONCodingKey.self)
1124
- try JSONAny.encode(to: &container, dictionary: dict)
1125
- } else {
1126
- var container = encoder.singleValueContainer()
1127
- try JSONAny.encode(to: &container, value: self.value)
1100
+ else {
1101
+ return;
1128
1102
  }
1103
+ this.emitBlockWithAccess(["extension ", extensionSource], () => {
1104
+ this.emitBlock(["init(data: Data) throws"], () => {
1105
+ this.emitLine("self = try newJSONDecoder().decode(", name, ".self, from: data)");
1106
+ });
1107
+ this.ensureBlankLine();
1108
+ this.emitBlock("init(_ json: String, using encoding: String.Encoding = .utf8) throws", () => {
1109
+ this.emitBlock("guard let data = json.data(using: encoding) else", () => {
1110
+ this.emitLine(`throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)`);
1111
+ });
1112
+ this.emitLine("try self.init(data: data)");
1113
+ });
1114
+ this.ensureBlankLine();
1115
+ this.emitBlock(`init(fromURL url: URL) throws`, () => {
1116
+ this.emitLine("try self.init(data: try Data(contentsOf: url))");
1117
+ });
1118
+ this.ensureBlankLine();
1119
+ this.emitBlock("func jsonData() throws -> Data", () => {
1120
+ this.emitLine("return try newJSONEncoder().encode(self)");
1121
+ });
1122
+ this.ensureBlankLine();
1123
+ this.emitBlock("func jsonString(encoding: String.Encoding = .utf8) throws -> String?", () => {
1124
+ this.emitLine("return String(data: try self.jsonData(), encoding: encoding)");
1125
+ });
1126
+ });
1127
+ }
1128
+ emitDecodingError(name) {
1129
+ this.emitLine("throw DecodingError.typeMismatch(", name, '.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for ', name, '"))');
1129
1130
  }
1130
- }`);
1131
- }
1132
- this.endFile();
1133
- };
1134
1131
  emitConvenienceMutator(c, className) {
1135
1132
  this.emitLine("func with(");
1136
1133
  this.indent(() => {