diff --git a/.gitignore b/.gitignore index 2c22487..aa87d97 100644 --- a/.gitignore +++ b/.gitignore @@ -63,3 +63,8 @@ fastlane/report.xml fastlane/Preview.html fastlane/screenshots fastlane/test_output + +# SPM + +.swiftpm/* +Build/* diff --git a/.swiftformat b/.swiftformat new file mode 100644 index 0000000..04e2352 --- /dev/null +++ b/.swiftformat @@ -0,0 +1,108 @@ +# SwiftFormat config compliant with Google Swift Guideline +# https://google.github.io/swift/#control-flow-statements + +# Specify version used in a project + +--swiftversion 5.7 + +# Rules explicitly required by the guideline + +--rules \ +blankLinesAroundMark, \ +blankLinesAtEndOfScope, \ +blankLinesAtStartOfScope, \ +blankLinesBetweenScopes, \ +braces, \ +consecutiveBlankLines, \ +consecutiveSpaces, \ +duplicateImports, \ +elseOnSameLine, \ +emptyBraces, \ +enumNamespaces, \ +extensionAccessControl, \ +hoistPatternLet, \ +indent, \ +leadingDelimiters, \ +linebreakAtEndOfFile, \ +markTypes, \ +organizeDeclarations, \ +redundantInit, \ +redundantParens, \ +redundantPattern, \ +redundantRawValues, \ +redundantType, \ +redundantVoidReturnType, \ +semicolons, \ +sortedImports, \ +sortedSwitchCases, \ +spaceAroundBraces, \ +spaceAroundBrackets, \ +spaceAroundComments, \ +spaceAroundGenerics, \ +spaceAroundOperators, \ +spaceAroundParens, \ +spaceInsideBraces, \ +spaceInsideBrackets, \ +spaceInsideComments, \ +spaceInsideGenerics, \ +spaceInsideParens, \ +todos, \ +trailingClosures, \ +trailingCommas, \ +trailingSpace, \ +typeSugar, \ +void, \ +wrap, \ +wrapArguments, \ +wrapAttributes, \ +# +# +# Additional rules not mentioned in the guideline, but helping to keep the codebase clean +# Quoting the guideline: +# Common themes among the rules in this section are: +# avoid redundancy, avoid ambiguity, and prefer implicitness over explicitness +# unless being explicit improves readability and/or reduces ambiguity. +# +# +andOperator, \ +isEmpty, \ +redundantBackticks, \ +redundantBreak, \ +redundantExtensionACL, \ +redundantGet, \ +redundantLetError, \ +redundantNilInit, \ +redundantObjc, \ +redundantReturn, \ +redundantSelf, \ +strongifiedSelf + + +# Options for basic rules + +--extensionacl on-declarations +--funcattributes prev-line +--indent 4 +--maxwidth 120 +--typeattributes prev-line +--varattributes same-line +--voidtype void +--wraparguments before-first +--wrapparameters before-first +--wrapcollections before-first +--wrapreturntype if-multiline +--wrapconditions after-first + +# Option for additional rules + +--self init-only + +# Excluded folders + +--exclude Pods,**/UNTESTED_TODO,vendor,fastlane + +# https://github.com/NoemiRozpara/Google-SwiftFormat-Config + +# Following is by Lava + +--ifdef no-indent \ No newline at end of file diff --git a/.swiftlint.yml b/.swiftlint.yml new file mode 100644 index 0000000..bd2f749 --- /dev/null +++ b/.swiftlint.yml @@ -0,0 +1,78 @@ +# +# Copyright (c) 2019 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +disabled_rules: + - multiple_closures_with_trailing_closure + - nesting + +opt_in_rules: + - convenience_type + # - no_magic_numbers + # - force_unwrapping + +force_cast: warning # implicitly +force_try: + severity: warning # explicitly +line_length: 120 +function_body_length: + warning: 120 + error: 400 +type_body_length: + warning: 500 + error: 1200 +file_length: + warning: 900 + error: 1600 +type_name: + min_length: 3 + max_length: + warning: 50 + error: 50 + excluded: + - OS +identifier_name: + min_length: 3 + excluded: # excluded via string array + - id + - URL + - url + - x + - y + - i + - j + - OS + - Defaults # Make use of `SwiftyUserDefaults` +reporter: "xcode" # reporter type (xcode, json, csv, checkstyle, junit, html, emoji) +trailing_comma: + severity: warning + mandatory_comma: true +large_tuple: + - warning: 4 + - error: 4 +# force_unwrapping: +# excluded: +# - ".*Test\\.swift" + +custom_rules: + sf_safe_symbol: + name: "Safe SFSymbol" + message: "Use `SFSafeSymbols` via `systemSymbol` parameters for type safety." + regex: "(Image\\(systemName:)|(NSImage\\(symbolName:)|(Label[^,]+?,\\s*systemImage:)|(UIApplicationShortcutIcon\\(systemImageName:)" + severity: warning + +excluded: + - "Build/*" + - "*/.build/*" diff --git a/LICENSE b/LICENSE index 7e8948c..0f806a4 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2016 Josh Smith +Original Copyright (c) 2016 Josh Smith Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..ad902dc --- /dev/null +++ b/Makefile @@ -0,0 +1,8 @@ +SHELL := /bin/sh +.PHONY: format lint + +format: + @swiftformat --swiftversion 5.7 ./ + +lint: + @git ls-files --exclude-standard | grep -E '\.swift$$' | swiftlint --fix --autocorrect diff --git a/Package.swift b/Package.swift new file mode 100644 index 0000000..67d887c --- /dev/null +++ b/Package.swift @@ -0,0 +1,30 @@ +// swift-tools-version:5.5 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "json2swift", + products: [ + // Products define the executables and libraries a package produces, making them visible to other packages. + .library( + name: "libJson2Swift", + targets: ["libJson2Swift"] + ), + ], + targets: [ + // Targets are the basic building blocks of a package, defining a module or a test suite. + // Targets can depend on other targets in this package and products from dependencies. + .executableTarget( + name: "json2swift", + dependencies: ["libJson2Swift"] + ), + .target( + name: "libJson2Swift" + ), + .testTarget( + name: "json2swiftTests", + dependencies: ["json2swift"] + ), + ] +) diff --git a/json2swift/main.swift b/Sources/json2swift/main.swift similarity index 94% rename from json2swift/main.swift rename to Sources/json2swift/main.swift index 04f9695..5858d4b 100644 --- a/json2swift/main.swift +++ b/Sources/json2swift/main.swift @@ -7,8 +7,9 @@ // import Foundation +import libJson2Swift -/* +/* * This tool and its documentation are hosted at https://github.com/ijoshsmith/json2swift */ @@ -17,8 +18,7 @@ let arguments = Array(CommandLine.arguments.dropFirst()) if let errorMessage = run(with: arguments) { print("Error: \(errorMessage)") exit(1) -} -else { +} else { print("Success: Created Swift data model(s)") exit(0) } diff --git a/unit_tests/JSONType+JSONType.swift b/Sources/libJson2Swift/JSONType+JSONType.swift similarity index 68% rename from unit_tests/JSONType+JSONType.swift rename to Sources/libJson2Swift/JSONType+JSONType.swift index 08273c5..70f0900 100644 --- a/unit_tests/JSONType+JSONType.swift +++ b/Sources/libJson2Swift/JSONType+JSONType.swift @@ -8,7 +8,7 @@ extension JSONType { /// A shortcut function that helps create concise unit tests. - static func +(lhs: JSONType, rhs: JSONType) -> JSONType { - return lhs.findCompatibleType(with: rhs) + static func + (lhs: JSONType, rhs: JSONType) -> JSONType { + lhs.findCompatibleType(with: rhs) } } diff --git a/json2swift/command-line-interface.swift b/Sources/libJson2Swift/command-line-interface.swift similarity index 64% rename from json2swift/command-line-interface.swift rename to Sources/libJson2Swift/command-line-interface.swift index 6dbb411..0bf588e 100644 --- a/json2swift/command-line-interface.swift +++ b/Sources/libJson2Swift/command-line-interface.swift @@ -8,26 +8,26 @@ import Foundation -typealias ErrorMessage = String +public typealias ErrorMessage = String -func run(with arguments: [String]) -> ErrorMessage? { +public func run(with arguments: [String]) -> ErrorMessage? { guard arguments.isEmpty == false else { return "Please provide a JSON file path or directory path." } - + let path = (arguments[0] as NSString).resolvingSymlinksInPath - + var isDirectory: ObjCBool = false - guard FileManager.default.fileExists(atPath: path, isDirectory: &isDirectory) else { return "No such file or directory exists." } - + guard FileManager.default.fileExists(atPath: path, isDirectory: &isDirectory) + else { return "No such file or directory exists." } + let jsonFilePaths: [String] if isDirectory.boolValue { guard let filePaths = findJSONFilePaths(in: path) else { return "Unable to read contents of directory." } guard filePaths.isEmpty == false else { return "The directory does not contain any JSON files." } jsonFilePaths = filePaths - } - else { + } else { jsonFilePaths = [path] } - + let jsonUtilitiesFilePath: String? = jsonFilePaths.count > 1 ? (path as NSString).appendingPathComponent("JSONUtilities.swift") : nil @@ -35,20 +35,25 @@ func run(with arguments: [String]) -> ErrorMessage? { let shouldIncludeUtils = jsonUtilitiesFilePath == nil let rootType = arguments.count >= 2 ? arguments[1] : "root-type" for jsonFilePath in jsonFilePaths { - if let errorMessage = generateSwiftFileBasedOnJSON(inFile: jsonFilePath, includeJSONUtilities: shouldIncludeUtils, rootTypeName: rootType) { + if let errorMessage = generateSwiftFileBasedOnJSON( + inFile: jsonFilePath, + includeJSONUtilities: shouldIncludeUtils, + rootTypeName: rootType + ) { return errorMessage } } - + if let jsonUtilitiesFilePath = jsonUtilitiesFilePath { - guard writeJSONUtilitiesFile(to: jsonUtilitiesFilePath) else { return "Unable to write JSON utilities file to \(jsonUtilitiesFilePath)" } + guard writeJSONUtilitiesFile(to: jsonUtilitiesFilePath) + else { return "Unable to write JSON utilities file to \(jsonUtilitiesFilePath)" } } - + return nil } - // MARK: - Finding JSON files in directory + private func findJSONFilePaths(in directory: String) -> [String]? { guard let jsonFileNames = findJSONFileNames(in: directory) else { return nil } return resolveAbsolutePaths(for: jsonFileNames, inDirectory: directory) @@ -56,39 +61,45 @@ private func findJSONFilePaths(in directory: String) -> [String]? { private func findJSONFileNames(in directory: String) -> [String]? { let isJSONFile: (String) -> Bool = { $0.lowercased().hasSuffix(".json") } - do { return try FileManager.default.contentsOfDirectory(atPath: directory).filter(isJSONFile) } - catch { return nil } + do { return try FileManager.default.contentsOfDirectory(atPath: directory).filter(isJSONFile) } catch { return nil } } private func resolveAbsolutePaths(for jsonFileNames: [String], inDirectory directory: String) -> [String] { - return jsonFileNames.map { (directory as NSString).appendingPathComponent($0) } + jsonFileNames.map { (directory as NSString).appendingPathComponent($0) } } - // MARK: - Generating Swift file based on JSON -private func generateSwiftFileBasedOnJSON(inFile jsonFilePath: String, includeJSONUtilities: Bool, rootTypeName: String) -> ErrorMessage? { + +private func generateSwiftFileBasedOnJSON( + inFile jsonFilePath: String, + includeJSONUtilities: Bool, + rootTypeName: String +) + -> ErrorMessage? { let url = URL(fileURLWithPath: jsonFilePath) let data: Data - do { data = try Data(contentsOf: url) } - catch { return "Unable to read file: \(jsonFilePath)" } - + do { data = try Data(contentsOf: url) } catch { return "Unable to read file: \(jsonFilePath)" } + let jsonObject: Any - do { jsonObject = try JSONSerialization.jsonObject(with: data, options: []) } - catch { return "File does not contain valid JSON: \(jsonFilePath)" } - + do { jsonObject = try JSONSerialization.jsonObject(with: data, options: []) } catch { + return "File does not contain valid JSON: \(jsonFilePath)" + } + let jsonSchema: JSONElementSchema - if let jsonElement = jsonObject as? JSONElement { jsonSchema = JSONElementSchema.inferred(from: jsonElement, named: rootTypeName) } - else if let jsonArray = jsonObject as? [JSONElement] { jsonSchema = JSONElementSchema.inferred(from: jsonArray, named: rootTypeName) } - else { return "Unsupported JSON format: must be a dictionary or array of dictionaries." } - + if let jsonElement = jsonObject as? JSONElement { + jsonSchema = JSONElementSchema.inferred(from: jsonElement, named: rootTypeName) + } else if let jsonArray = jsonObject as? [JSONElement] { + jsonSchema = JSONElementSchema.inferred(from: jsonArray, named: rootTypeName) + } else { return "Unsupported JSON format: must be a dictionary or array of dictionaries." } + let swiftStruct = SwiftStruct.create(from: jsonSchema) let swiftCode = includeJSONUtilities ? SwiftCodeGenerator.generateCodeWithJSONUtilities(for: swiftStruct) : SwiftCodeGenerator.generateCode(for: swiftStruct) - + let swiftFilePath = (jsonFilePath as NSString).deletingPathExtension + ".swift" guard write(swiftCode: swiftCode, toFile: swiftFilePath) else { return "Unable to write to file: \(swiftFilePath)" } - + return nil } @@ -101,8 +112,7 @@ private func write(swiftCode: String, toFile filePath: String) -> Bool { do { try swiftCode.write(toFile: filePath, atomically: true, encoding: String.Encoding.utf8) return true - } - catch { + } catch { return false } } diff --git a/Sources/libJson2Swift/failable-initializer-translation.swift b/Sources/libJson2Swift/failable-initializer-translation.swift new file mode 100644 index 0000000..90eb30d --- /dev/null +++ b/Sources/libJson2Swift/failable-initializer-translation.swift @@ -0,0 +1,97 @@ +// +// failable-initializer-translation.swift +// json2swift +// +// Created by Joshua Smith on 10/28/16. +// Copyright © 2016 iJoshSmith. All rights reserved. +// + +// MARK: - JSONElementSchema --> SwiftFailableInitializer + +extension SwiftFailableInitializer { + internal static func create(forStructBasedOn jsonElementSchema: JSONElementSchema) -> SwiftFailableInitializer { + let attributeMap = jsonElementSchema.attributes + let allAttributeNames = Set(attributeMap.keys) + let requiredAttributeNames = allAttributeNames.filter { attributeMap[$0]!.isRequired } + let optionalAttributeNames = allAttributeNames.subtracting(requiredAttributeNames) + let requiredTransformations: [TransformationFromJSON] = requiredAttributeNames.map { + TransformationFromJSON.create(forAttributeNamed: $0, inAttributeMap: attributeMap) + } + let optionalTransformations: [TransformationFromJSON] = optionalAttributeNames.map { + TransformationFromJSON.create(forAttributeNamed: $0, inAttributeMap: attributeMap) + } + return SwiftFailableInitializer( + requiredTransformations: requiredTransformations, + optionalTransformations: optionalTransformations + ) + } +} + +// MARK: - JSON attribute --> TransformationFromJSON + +extension TransformationFromJSON { + fileprivate static func create( + forAttributeNamed attributeName: String, + inAttributeMap attributeMap: JSONAttributeMap + ) + -> TransformationFromJSON { + let propertyName = attributeName.toSwiftPropertyName() + let jsonType = attributeMap[attributeName]! + switch jsonType { + case let .element(_, schema): + let swiftStruct = SwiftStruct.create(from: schema) + return TransformationFromJSON.toCustomStruct( + attributeName: attributeName, + propertyName: propertyName, + type: swiftStruct + ) + + case let .elementArray(_, elementSchema, hasNullableElements): + let swiftStruct = SwiftStruct.create(from: elementSchema) + return TransformationFromJSON.toCustomStructArray( + attributeName: attributeName, + propertyName: propertyName, + elementType: swiftStruct, + hasOptionalElements: hasNullableElements + ) + + case let .valueArray(_, valueType): + let elementType = SwiftPrimitiveValueType.create(from: valueType) + let hasOptionalElements = valueType.isRequired == false + return TransformationFromJSON.toPrimitiveValueArray( + attributeName: attributeName, + propertyName: propertyName, + elementType: elementType, + hasOptionalElements: hasOptionalElements + ) + + default: + let valueType = SwiftPrimitiveValueType.create(from: jsonType) + return TransformationFromJSON.toPrimitiveValue( + attributeName: attributeName, + propertyName: propertyName, + type: valueType + ) + } + } +} + +// MARK: - JSONType --> SwiftPrimitiveValueType + +extension SwiftPrimitiveValueType { + fileprivate static func create(from jsonType: JSONType) -> SwiftPrimitiveValueType { + switch jsonType { + case .anything, + .element, + .elementArray, + .nullable, + .valueArray: return .any + case .emptyArray: return .emptyArray + case let .number(_, isFloatingPoint): return isFloatingPoint ? .double : .int + case let .date(_, format): return .date(format: format) + case .url: return .url + case .string: return .string + case .bool: return .bool + } + } +} diff --git a/Sources/libJson2Swift/json-attribute-merging.swift b/Sources/libJson2Swift/json-attribute-merging.swift new file mode 100644 index 0000000..68b56cc --- /dev/null +++ b/Sources/libJson2Swift/json-attribute-merging.swift @@ -0,0 +1,132 @@ +// +// json-attribute-merging.swift +// json2swift +// +// Created by Joshua Smith on 10/11/16. +// Copyright © 2016 iJoshSmith. All rights reserved. +// + +// MARK: - JSONElementSchema extension + +extension JSONElementSchema { + static func inferredByMergingAttributes(of schemas: [JSONElementSchema], named name: String) -> JSONElementSchema { + switch schemas.count { + case 0: return JSONElementSchema(name: name) + case 1: return schemas[0] + default: return schemas.dropFirst().reduce(schemas[0], merge(schema:withSchema:)) + } + } + + private static func merge( + schema: JSONElementSchema, + withSchema otherSchema: JSONElementSchema + ) + -> JSONElementSchema { + schema.merged(with: otherSchema) + } + + fileprivate func merged(with schema: JSONElementSchema) -> JSONElementSchema { + let mergedAttributes = JSONType.merge(attributes: attributes, with: schema.attributes) + return JSONElementSchema(name: name, attributes: mergedAttributes) + } +} + +// MARK: - JSONType extension + +extension JSONType { + fileprivate static func merge( + attributes: JSONAttributeMap, + with otherAttributes: JSONAttributeMap + ) + -> JSONAttributeMap { + let attributeNames = Set(Array(attributes.keys) + Array(otherAttributes.keys)) + let mergedAttributes = attributeNames.map { name -> (String, JSONType) in + let type = attributes[name] ?? .nullable + let otherType = otherAttributes[name] ?? .nullable + let compatibleType = type.findCompatibleType(with: otherType) + return (name, compatibleType) + } + return JSONAttributeMap(entries: mergedAttributes) + } + + public func findCompatibleType(with type: JSONType) -> JSONType { + JSONType.compatible(with: self, and: type) + } + + private static func compatible(with type1: JSONType, and type2: JSONType) -> JSONType { + switch (type1, type2) { + // Element + case let (.element(r1, s1), .element(r2, s2)): return .element( + isRequired: r1 && r2, + schema: s1.merged(with: s2) + ) + case let (.element(_, s), .nullable): return .element(isRequired: false, schema: s) + case let (.nullable, .element(_, s)): return .element(isRequired: false, schema: s) + + // Element Array + case let (.elementArray(r1, s1, n1), .elementArray(r2, s2, n2)): return .elementArray( + isRequired: r1 && r2, + elementSchema: s1.merged(with: s2), + hasNullableElements: n1 || n2 + ) + case let (.elementArray(_, s, n), .nullable): return .elementArray( + isRequired: false, + elementSchema: s, + hasNullableElements: n + ) + case let (.nullable, .elementArray(_, s, n)): return .elementArray( + isRequired: false, + elementSchema: s, + hasNullableElements: n + ) + + // Value Array + case let (.valueArray(r1, t1), .valueArray(r2, t2)): return .valueArray( + isRequired: r1 && r2, + valueType: t1.findCompatibleType(with: t2) + ) + case let (.valueArray(_, t), .nullable): return .valueArray(isRequired: false, valueType: t) + case let (.nullable, .valueArray(_, t)): return .valueArray(isRequired: false, valueType: t) + + // Numeric + case let (.number(r1, fp1), .number(r2, fp2)): return .number(isRequired: r1 && r2, isFloatingPoint: fp1 || fp2) + case let (.number(_, fp), .nullable): return .number(isRequired: false, isFloatingPoint: fp) + case let (.nullable, .number(_, fp)): return .number(isRequired: false, isFloatingPoint: fp) + + // Text + case let (.date(r1, f), .date(r2, _)): return .date(isRequired: r1 && r2, format: f) + case let (.date(r1, _), .url(r2)): return .string(isRequired: r1 && r2) + case let (.date(r1, f), .string(r2)): return .date(isRequired: r1 && r2, format: f) + case let (.date(_, f), .nullable): return .date(isRequired: false, format: f) + case let (.nullable, .date(_, f)): return .date(isRequired: false, format: f) + case let (.url(r1), .date(r2, _)): return .string(isRequired: r1 && r2) + case let (.url(r1), .url(r2)): return .url(isRequired: r1 && r2) + case let (.url(r1), .string(r2)): return .string(isRequired: r1 && r2) + case (.url, .nullable): return .url(isRequired: false) + case (.nullable, .url): return .url(isRequired: false) + case let (.string(r1), .date(r2, f)): return .date(isRequired: r1 && r2, format: f) + case let (.string(r1), .url(r2)): return .string(isRequired: r1 && r2) + case let (.string(r1), .string(r2)): return .string(isRequired: r1 && r2) + case (.string, .nullable): return .string(isRequired: false) + case (.nullable, .string): return .string(isRequired: false) + + // Boolean + case let (.bool(r1), .bool(r2)): return .bool(isRequired: r1 && r2) + case (.bool, .nullable): return .bool(isRequired: false) + case (.nullable, .bool): return .bool(isRequired: false) + + // Nullable (e.g. both attribute values were null) + case (.nullable, .nullable): return .nullable + + // Empty array + case (.emptyArray, .emptyArray): return .emptyArray + case (.elementArray, .emptyArray): return type1 + case (.valueArray, .emptyArray): return type1 + case (.emptyArray, .elementArray): return type2 + case (.emptyArray, .valueArray): return type2 + + // Unrelated types + default: return .anything + } + } +} diff --git a/Sources/libJson2Swift/json-data-model.swift b/Sources/libJson2Swift/json-data-model.swift new file mode 100644 index 0000000..de6d896 --- /dev/null +++ b/Sources/libJson2Swift/json-data-model.swift @@ -0,0 +1,104 @@ +// +// json-data-model.swift +// json2swift +// +// Created by Joshua Smith on 10/10/16. +// Copyright © 2016 iJoshSmith. All rights reserved. +// + +/// The prefix to use on a sample JSON attribute whose value is a date string. +/// This provides a hint to the JSON analyzer that the corresponding property is a Date. +/// For example, use "DATE_FORMAT=MM/dd/yyyy" for an attribute whose values are like "12/25/2016". +let dateFormatPrefix = "DATE_FORMAT=" + +public typealias JSONValue = Any +public typealias JSONElement = [String: JSONValue] + +// MARK: - JSONType + +public indirect enum JSONType { + case element(isRequired: Bool, schema: JSONElementSchema) + case elementArray(isRequired: Bool, elementSchema: JSONElementSchema, hasNullableElements: Bool) + case valueArray(isRequired: Bool, valueType: JSONType) + case number(isRequired: Bool, isFloatingPoint: Bool) + case date(isRequired: Bool, format: String) + case url(isRequired: Bool) + case string(isRequired: Bool) + case bool(isRequired: Bool) + case nullable // For an attribute that is missing or has a null value. + case anything // For an attribute with multiple values of unrelated types. + case emptyArray // For an attribute that contains an empty array. +} + +extension JSONType { + var isRequired: Bool { + switch self { + case let .element(isRequired, _): return isRequired + case let .elementArray(isRequired, _, _): return isRequired + case let .valueArray(isRequired, _): return isRequired + case let .number(isRequired, _): return isRequired + case let .date(isRequired, _): return isRequired + case let .url(isRequired): return isRequired + case let .string(isRequired): return isRequired + case let .bool(isRequired): return isRequired + case .anything, .nullable: return false + case .emptyArray: return true + } + } + + var jsonElementSchema: JSONElementSchema? { + switch self { + case let .element(_, schema): return schema + case let .elementArray(_, elementSchema, _): return elementSchema + default: return nil + } + } +} + +// MARK: Equatable + +extension JSONType: Equatable { + public static func == (lhs: JSONType, rhs: JSONType) -> Bool { + switch (lhs, rhs) { + case let (.element(r1, a), .element(r2, b)): return r1 == r2 && a == b + case let (.elementArray(r1, a, n1), .elementArray(r2, b, n2)): return r1 == r2 && a == b && n1 == n2 + case let (.valueArray(r1, a), .valueArray(r2, b)): return r1 == r2 && a == b + case let (.number(r1, a), .number(r2, b)): return r1 == r2 && a == b + case let (.date(r1, a), .date(r2, b)): return r1 == r2 && a == b + case let (.url(r1), .url(r2)): return r1 == r2 + case let (.string(r1), .string(r2)): return r1 == r2 + case let (.bool(r1), .bool(r2)): return r1 == r2 + case (.nullable, .nullable): return true + case (.anything, .anything): return true + case (.emptyArray, .emptyArray): return true + default: return false + } + } +} + +// MARK: - JSONElementSchema + +public typealias JSONAttributeMap = [String: JSONType] + +// MARK: - JSONElementSchema + +public struct JSONElementSchema { + // MARK: Lifecycle + + public init(name: String, attributes: JSONAttributeMap = [:]) { + self.name = name + self.attributes = attributes + } + + // MARK: Public + + public let name: String + public let attributes: JSONAttributeMap +} + +// MARK: Equatable + +extension JSONElementSchema: Equatable {} +public func == (lhs: JSONElementSchema, rhs: JSONElementSchema) -> Bool { + lhs.name == rhs.name && lhs.attributes == rhs.attributes +} diff --git a/json2swift/json-helpers.swift b/Sources/libJson2Swift/json-helpers.swift similarity index 100% rename from json2swift/json-helpers.swift rename to Sources/libJson2Swift/json-helpers.swift diff --git a/json2swift/json-schema-inference.swift b/Sources/libJson2Swift/json-schema-inference.swift similarity index 73% rename from json2swift/json-schema-inference.swift rename to Sources/libJson2Swift/json-schema-inference.swift index 149cfee..d9323e9 100644 --- a/json2swift/json-schema-inference.swift +++ b/Sources/libJson2Swift/json-schema-inference.swift @@ -9,25 +9,26 @@ import Foundation // MARK: - JSONElementSchema extension + extension JSONElementSchema { static func inferred(from jsonElementArray: [JSONElement], named name: String) -> JSONElementSchema { let jsonElement = [name: jsonElementArray] let schema = JSONElementSchema.inferred(from: jsonElement, named: name) let (_, attributeType) = schema.attributes.first! switch attributeType { - case .elementArray(_, let elementSchema, _): return elementSchema - case .emptyArray: return JSONElementSchema(name: name) - default: abort() + case let .elementArray(_, elementSchema, _): return elementSchema + case .emptyArray: return JSONElementSchema(name: name) + default: abort() } } - + static func inferred(from jsonElement: JSONElement, named name: String) -> JSONElementSchema { let attributes = createAttributeMap(for: jsonElement) return JSONElementSchema(name: name, attributes: attributes) } private static func createAttributeMap(for jsonElement: JSONElement) -> JSONAttributeMap { - let attributes = jsonElement.map { (name, value) -> (String, JSONType) in + let attributes = jsonElement.map { name, value -> (String, JSONType) in let type = JSONType.inferType(of: value, named: name) return (name, type) } @@ -36,101 +37,112 @@ extension JSONElementSchema { } // MARK: - JSONType extension -fileprivate extension JSONType { - static func inferType(of value: Any, named name: String) -> JSONType { + +extension JSONType { + fileprivate static func inferType(of value: Any, named name: String) -> JSONType { switch value { case let element as JSONElement: return inferType(of: element, named: name) - case let array as [Any]: return inferType(of: array, named: name) - case let nsValue as NSValue: return inferType(of: nsValue) - case let string as String: return inferType(of: string) - case is NSNull: return .nullable + case let array as [Any]: return inferType(of: array, named: name) + case let nsValue as NSValue: return inferType(of: nsValue) + case let string as String: return inferType(of: string) + case is NSNull: return .nullable default: assertionFailure("Unexpected type of value in JSON: \(value)") abort() } } - + private static func inferType(of element: JSONElement, named name: String) -> JSONType { let schema = JSONElementSchema.inferred(from: element, named: name) return .element(isRequired: true, schema: schema) } - + private static func inferType(of array: [Any], named name: String) -> JSONType { let arrayWithoutNulls = array.filter { $0 is NSNull == false } if let elements = arrayWithoutNulls as? [JSONElement] { let hasNullableElements = arrayWithoutNulls.count < array.count return inferType(ofElementArray: elements, named: name, hasNullableElements: hasNullableElements) - } - else { + } else { return inferType(ofValueArray: array, named: name) } } - - private static func inferType(ofElementArray elementArray: [JSONElement], named name: String, hasNullableElements: Bool) -> JSONType { + + private static func inferType( + ofElementArray elementArray: [JSONElement], + named name: String, + hasNullableElements: Bool + ) + -> JSONType { if elementArray.isEmpty { return .emptyArray } let schemas = elementArray.map { jsonElement in JSONElementSchema.inferred(from: jsonElement, named: name) } let mergedSchema = JSONElementSchema.inferredByMergingAttributes(of: schemas, named: name) return .elementArray(isRequired: true, elementSchema: mergedSchema, hasNullableElements: hasNullableElements) } - + private static func inferType(ofValueArray valueArray: [JSONValue], named name: String) -> JSONType { if valueArray.isEmpty { return .emptyArray } let valueType = inferValueType(of: valueArray, named: name) return .valueArray(isRequired: true, valueType: valueType) } - + private static func inferValueType(of values: [JSONValue], named name: String) -> JSONType { let types = values.map { inferType(of: $0, named: name) } switch types.count { - case 0: abort() // Should never introspect an empty array. - case 1: return types[0] + case 0: abort() // Should never introspect an empty array. + case 1: return types[0] default: return types.dropFirst().reduce(types[0]) { $0.findCompatibleType(with: $1) } } } - + private static func inferType(of nsValue: NSValue) -> JSONType { if nsValue.isBoolType { return .bool(isRequired: true) - } - else { + } else { return .number(isRequired: true, isFloatingPoint: nsValue.isFloatType) } } - + private static func inferType(of string: String) -> JSONType { if let dateFormat = string.extractDateFormat() { return .date(isRequired: true, format: dateFormat) - } - else if string.isWebAddress { + } else if string.isWebAddress { return .url(isRequired: true) - } - else { + } else { return .string(isRequired: true) } } } // MARK: - String extension -fileprivate extension String { - func extractDateFormat() -> String? { + +extension String { + fileprivate func extractDateFormat() -> String? { let trimmed = trimmingCharacters(in: CharacterSet.whitespaces) return String.extractDateFormat(from: trimmed) } - + private static func extractDateFormat(from string: String) -> String? { guard let prefixRange = string.range(of: dateFormatPrefix) else { return nil } guard prefixRange.lowerBound == string.startIndex else { return nil } return String(string[prefixRange.upperBound...]) } - - var isWebAddress: Bool { + + fileprivate var isWebAddress: Bool { guard let url = URL(string: self) else { return false } return url.scheme != nil && url.host != nil } } // MARK: - NSValue extension -fileprivate extension NSValue { - var isBoolType: Bool { return CFNumberGetType((self as! CFNumber)) == CFNumberType.charType } - var isFloatType: Bool { return CFNumberIsFloatType((self as! CFNumber)) } + +extension NSValue { + fileprivate var isBoolType: Bool { + guard isKind(of: NSNumber.self), let this = self as? NSNumber else { return false } + return CFNumberGetType(this as CFNumber) == CFNumberType.charType + } + + fileprivate var isFloatType: Bool { + guard isKind(of: NSNumber.self), let this = self as? NSNumber else { return false } + return CFNumberIsFloatType(this as CFNumber) + } } diff --git a/json2swift/name-translation.swift b/Sources/libJson2Swift/name-translation.swift similarity index 81% rename from json2swift/name-translation.swift rename to Sources/libJson2Swift/name-translation.swift index 447edc2..466ab79 100644 --- a/json2swift/name-translation.swift +++ b/Sources/libJson2Swift/name-translation.swift @@ -8,50 +8,50 @@ import Foundation -internal extension String { - func toSwiftStructName() -> String { +extension String { + internal func toSwiftStructName() -> String { let name = capitalizedWithoutInvalidChars.prefixedWithUnderscoreIfNecessary return name.isEmpty ? "DefaultStructName" : name } - - func toSwiftPropertyName() -> String { + + internal func toSwiftPropertyName() -> String { let name = capitalizedWithoutInvalidChars.lowercasedFirstCharacter.prefixedWithUnderscoreIfNecessary return name.isEmpty ? "defaultPropertyName" : name } - + private var capitalizedWithoutInvalidChars: String { let trimmed = trimmingCharacters(in: CharacterSet.whitespacesAndNewlines) let parts = trimmed.components(separatedBy: invalidSwiftNameCharacters) - let capitalizedParts = parts.map { $0.uppercasedFirstCharacter } + let capitalizedParts = parts.map(\.uppercasedFirstCharacter) return capitalizedParts.joined() } - + private var prefixedWithUnderscoreIfNecessary: String { - return isSwiftKeyword || startsWithNumber + isSwiftKeyword || startsWithNumber ? "_" + self : self } - + private var uppercasedFirstCharacter: String { - return modifyFirstCharacter(byApplying: { String($0).uppercased() }) + modifyFirstCharacter(byApplying: { String($0).uppercased() }) } - + private var lowercasedFirstCharacter: String { - return modifyFirstCharacter(byApplying: { String($0).lowercased() }) + modifyFirstCharacter(byApplying: { String($0).lowercased() }) } - + private func modifyFirstCharacter(byApplying characterTransform: (Character) -> String) -> String { guard isEmpty == false else { return self } - let firstChar = self.first! + let firstChar = first! let modifiedFirstChar = characterTransform(firstChar) let rangeOfFirstRange = Range(uncheckedBounds: (lower: startIndex, upper: index(after: startIndex))) return replacingCharacters(in: rangeOfFirstRange, with: modifiedFirstChar) } - + private var isSwiftKeyword: Bool { - return swiftKeywords.contains(self) + swiftKeywords.contains(self) } - + private var startsWithNumber: Bool { guard let digitRange = rangeOfCharacter(from: CharacterSet.decimalDigits) else { return false } return digitRange.lowerBound == startIndex @@ -109,8 +109,8 @@ private let swiftKeywords: Set = [ "throws", "true", "try", - "typealias", + "public typealias", "var", "where", - "while" + "while", ] diff --git a/Sources/libJson2Swift/schema-to-struct-translation.swift b/Sources/libJson2Swift/schema-to-struct-translation.swift new file mode 100644 index 0000000..44501a5 --- /dev/null +++ b/Sources/libJson2Swift/schema-to-struct-translation.swift @@ -0,0 +1,112 @@ +// +// schema-to-struct-translation.swift +// json2swift +// +// Created by Joshua Smith on 10/22/16. +// Copyright © 2016 iJoshSmith. All rights reserved. +// + +// MARK: - JSONElementSchema --> SwiftStruct + +extension SwiftStruct { + internal static func create(from jsonElementSchema: JSONElementSchema) -> SwiftStruct { + let name = jsonElementSchema.name.toSwiftStructName() + let properties = SwiftProperty.createProperties(forStructBasedOn: jsonElementSchema) + let parameters = SwiftParameter.createParameters(for: properties) + let initializer = SwiftInitializer(parameters: parameters) + let failableInitializer = SwiftFailableInitializer.create(forStructBasedOn: jsonElementSchema) + let nestedStructs = createNestedStructs(forElementsIn: jsonElementSchema) + return SwiftStruct( + name: name, + properties: properties, + initializer: initializer, + failableInitializer: failableInitializer, + nestedStructs: nestedStructs + ) + } + + private static func createNestedStructs(forElementsIn jsonElementSchema: JSONElementSchema) -> [SwiftStruct] { + jsonElementSchema.attributes.values.compactMap(SwiftStruct.tryToCreate(fromJSONType:)) + } + + private static func tryToCreate(fromJSONType jsonType: JSONType) -> SwiftStruct? { + if let schema = jsonType.jsonElementSchema { + return SwiftStruct.create(from: schema) + } else { + return nil + } + } +} + +// MARK: - JSONElementSchema --> SwiftProperty + +extension SwiftProperty { + fileprivate static func createProperties(forStructBasedOn jsonElementSchema: JSONElementSchema) -> [SwiftProperty] { + jsonElementSchema.attributes.map { name, type in + createProperty(basedOnJSONAttribute: name, and: type) + } + } + + private static func createProperty( + basedOnJSONAttribute attributeName: String, + and attributeType: JSONType + ) + -> SwiftProperty { + let propertyName = attributeName.toSwiftPropertyName() + let propertyType = SwiftType.createType(from: attributeType) + return SwiftProperty(name: propertyName, type: propertyType) + } +} + +// MARK: - SwiftProperty --> SwiftParameter + +extension SwiftParameter { + fileprivate static func createParameters(for properties: [SwiftProperty]) -> [SwiftParameter] { + properties.map { SwiftParameter(name: $0.name, type: $0.type) } + } +} + +// MARK: - JSONType --> SwiftType + +extension SwiftType { + fileprivate static func createType(from jsonType: JSONType) -> SwiftType { + let typeName = jsonType.swiftTypeName + let isOptional = jsonType.isRequired == false + return SwiftType(name: typeName, isOptional: isOptional) + } +} + +// MARK: - JSONType --> Swift type name + +extension JSONType { + fileprivate var swiftTypeName: String { + switch self { + case let .element(_, schema): return schema.name.toSwiftStructName() + case let .elementArray(_, elementSchema, hasNullableElements): return JSONType + .nameForArray(of: elementSchema, hasNullableElements) + case let .valueArray(_, valueType): return JSONType.nameForArray(of: valueType) + case let .number(_, isFloatingPoint): return isFloatingPoint ? "Double" : "Int" + case .date: return "Date" + case .url: return "URL" + case .string: return "String" + case .bool: return "Bool" + case .anything, .nullable: return "Any" + case .emptyArray: return "[Any?]" + } + } + + private static func nameForArray(of schema: JSONElementSchema, _ hasOptionalElements: Bool) -> String { + nameForArray(of: schema.name.toSwiftStructName(), hasOptionalElements: hasOptionalElements) + } + + private static func nameForArray(of valueType: JSONType) -> String { + nameForArray(of: valueType.swiftTypeName, hasOptionalElements: valueType.isRequired == false) + } + + private static func nameForArray(of typeName: String, hasOptionalElements: Bool) -> String { + let fullTypeName = hasOptionalElements + ? typeName + "?" + : typeName + return "[" + fullTypeName + "]" + } +} diff --git a/Sources/libJson2Swift/swift-code-generation.swift b/Sources/libJson2Swift/swift-code-generation.swift new file mode 100644 index 0000000..52732e8 --- /dev/null +++ b/Sources/libJson2Swift/swift-code-generation.swift @@ -0,0 +1,382 @@ +// +// swift-code-generation.swift +// json2swift +// +// Created by Joshua Smith on 10/14/16. +// Copyright © 2016 iJoshSmith. All rights reserved. +// + +import Foundation + +// MARK: - SwiftCodeGenerator + +struct SwiftCodeGenerator { + // MARK: Internal + + /// This method is used when only one Swift file is being generated. + static func generateCodeWithJSONUtilities(for swiftStruct: SwiftStruct) -> String { + [ + preamble, + "//", + "// MARK: - Data Model", + "//", + swiftStruct.toSwiftCode(), + "", + "//", + "// MARK: - JSON Utilities", + "//", + jsonUtilitiesTemplate, + "", + ].joined(separator: "\n") + } + + /// This method is used when multiple Swift files are being generated. + static func generateCode(for swiftStruct: SwiftStruct) -> String { + [ + preamble, + swiftStruct.toSwiftCode(), + "", + ].joined(separator: "\n") + } + + /// This method is used to only create the JSON utility code once when multiple Swift files are being generated. + static func generateJSONUtilities() -> String { + [ + preamble, + jsonUtilitiesTemplate, + "", + ].joined(separator: "\n") + } + + // MARK: Private + + private static let preamble = [ + "// This file was generated by json2swift. https://github.com/ijoshsmith/json2swift", + "", + "import Foundation", + "", + ].joined(separator: "\n") +} + +// MARK: - Implementation + +public typealias SwiftCode = String +public typealias LineOfCode = SwiftCode + +// MARK: - Indentation + +private struct Indentation { + // MARK: Lifecycle + + init(chars: String, level: Int = 0) { + precondition(level >= 0) + self.chars = chars + self.level = level + self.value = String(repeating: chars, count: level) + } + + // MARK: Internal + + func apply(toLineOfCode lineOfCode: LineOfCode) -> LineOfCode { + value + lineOfCode + } + + func apply( + toFirstLine firstLine: LineOfCode, + nestedLines generateNestedLines: (Indentation) -> [LineOfCode], + andLastLine lastLine: LineOfCode + ) -> [LineOfCode] { + let first = apply(toLineOfCode: firstLine) + let middle = generateNestedLines(increased()) + let last = apply(toLineOfCode: lastLine) + return [first] + middle + [last] + } + + // MARK: Private + + private let chars: String + private let level: Int + private let value: String + + private func increased() -> Indentation { + Indentation(chars: chars, level: level + 1) + } +} + +extension SwiftStruct { + fileprivate func toSwiftCode(indentedBy indentChars: String = " ") -> SwiftCode { + let indentation = Indentation(chars: indentChars) + let linesOfCode = toLinesOfCode(at: indentation) + return linesOfCode.joined(separator: "\n") + } + + private func toLinesOfCode(at indentation: Indentation) -> [LineOfCode] { + indentation.apply( + toFirstLine: "struct \(name): CreatableFromJSON {", + nestedLines: linesOfCodeForMembers(at:), + andLastLine: "}" + ) + } + + private func linesOfCodeForMembers(at indentation: Indentation) -> [LineOfCode] { + linesOfCodeForProperties(at: indentation) + + initializer.toLinesOfCode(at: indentation) + + failableInitializer.toLinesOfCode(at: indentation) + + linesOfCodeForNestedStructs(at: indentation) + } + + private func linesOfCodeForProperties(at indentation: Indentation) -> [LineOfCode] { + sortedProperties.map { property in + let propertyCode = property.toLineOfCode() + return indentation.apply(toLineOfCode: propertyCode) + } + } + + private var sortedProperties: [SwiftProperty] { + properties.sorted { lhs, rhs -> Bool in + lhs.name.compare(rhs.name) == .orderedAscending + } + } + + private func linesOfCodeForNestedStructs(at indentation: Indentation) -> [LineOfCode] { + sortedNestedStructs.flatMap { $0.toLinesOfCode(at: indentation) } + } + + private var sortedNestedStructs: [SwiftStruct] { + nestedStructs.sorted(by: { lhs, rhs -> Bool in + lhs.name.compare(rhs.name) == .orderedAscending + }) + } +} + +extension SwiftType { + fileprivate func toSwiftCode() -> SwiftCode { + isOptional ? name + "?" : name + } +} + +extension SwiftProperty { + fileprivate func toLineOfCode() -> LineOfCode { + "let \(name): \(type.toSwiftCode())" + } +} + +extension SwiftParameter { + fileprivate func toSwiftCode() -> SwiftCode { + "\(name): \(type.toSwiftCode())" + } +} + +extension SwiftInitializer { + fileprivate func toLinesOfCode(at indentation: Indentation) -> [LineOfCode] { + indentation.apply( + toFirstLine: "init(\(parameterList)) {", + nestedLines: linesOfCodeForPropertyAssignments(at:), + andLastLine: "}" + ) + } + + private var parameterList: SwiftCode { + sortedParameters + .map { $0.toSwiftCode() } + .joined(separator: ", ") + } + + private func linesOfCodeForPropertyAssignments(at indentation: Indentation) -> [LineOfCode] { + sortedParameters + .map { "self.\($0.name) = \($0.name)" } + .map(indentation.apply(toLineOfCode:)) + } + + private var sortedParameters: [SwiftParameter] { + parameters.sorted { lhs, rhs -> Bool in + lhs.name.compare(rhs.name) == .orderedAscending + } + } +} + +extension SwiftFailableInitializer { + fileprivate func toLinesOfCode(at indentation: Indentation) -> [LineOfCode] { + indentation.apply( + toFirstLine: "init?(json: [String: Any]) {", + nestedLines: linesOfCodeInMethodBody(at:), + andLastLine: "}" + ) + } + + private func linesOfCodeInMethodBody(at indentation: Indentation) -> [LineOfCode] { + let linesOfCode = linesOfCodeForTransformations + [lineOfCodeForCallingInitializer] + return linesOfCode.map(indentation.apply(toLineOfCode:)) + } + + private var linesOfCodeForTransformations: [LineOfCode] { + let requiredTransformationLines = sortedRequiredTransformations.map(\.guardedLetStatement) + let optionalTransformationLines = sortedOptionalTransformations.map(\.letStatement) + return (requiredTransformationLines + optionalTransformationLines) + } + + private var lineOfCodeForCallingInitializer: LineOfCode { + let sortedPropertyNames = (requiredTransformations + optionalTransformations).map(\.propertyName).sorted() + let labeledArguments = sortedPropertyNames.map { $0 + ": " + $0 } + let argumentList = labeledArguments.joined(separator: ", ") + return "self.init(" + argumentList + ")" + } + + private var sortedRequiredTransformations: [TransformationFromJSON] { + sort(transformations: requiredTransformations) + } + + private var sortedOptionalTransformations: [TransformationFromJSON] { + sort(transformations: optionalTransformations) + } + + private func sort(transformations: [TransformationFromJSON]) -> [TransformationFromJSON] { + transformations.sorted { lhs, rhs -> Bool in + lhs.propertyName.compare(rhs.propertyName) == .orderedAscending + } + } +} + +// Internal for unit test access. +extension TransformationFromJSON { + internal var propertyName: String { + switch self { + case let .toCustomStruct(_, propertyName, _): return propertyName + case let .toPrimitiveValue(_, propertyName, _): return propertyName + case let .toCustomStructArray(_, propertyName, _, _): return propertyName + case let .toPrimitiveValueArray(_, propertyName, _, _): return propertyName + } + } + + internal var guardedLetStatement: LineOfCode { + "guard \(letStatement) else { return nil }" + } + + internal var letStatement: LineOfCode { + switch self { + case let .toCustomStruct(attributeName, propertyName, type): return TransformationFromJSON + .letStatementForCustomStruct(attributeName, propertyName, type) + case let .toPrimitiveValue(attributeName, propertyName, type): return TransformationFromJSON + .letStatementForPrimitiveValue(attributeName, propertyName, type) + case let .toCustomStructArray( + attributeName, + propertyName, + elementType, + hasOptionalElements + ): return TransformationFromJSON.letStatementForCustomStructArray( + attributeName, + propertyName, + elementType, + hasOptionalElements + ) + case let .toPrimitiveValueArray( + attributeName, + propertyName, + elementType, + hasOptionalElements + ): return TransformationFromJSON.letStatementForPrimitiveValueArray( + attributeName, + propertyName, + elementType, + hasOptionalElements + ) + } + } + + private static func letStatementForCustomStruct( + _ attributeName: String, + _ propertyName: String, + _ type: SwiftStruct + ) + -> LineOfCode { + "let \(propertyName) = \(type.name)(json: json, key: \"\(attributeName)\")" + } + + private static func letStatementForPrimitiveValue( + _ attributeName: String, + _ propertyName: String, + _ type: SwiftPrimitiveValueType + ) + -> LineOfCode { + switch type { + case .any: return "let \(propertyName) = json[\"\(attributeName)\"] as? Any" + case .emptyArray: return "let \(propertyName) = json[\"\(attributeName)\"] as? [Any?]" + case .bool, .int, .string: return "let \(propertyName) = json[\"\(attributeName)\"] as? \(type.name)" + case .double: return "let \(propertyName) = Double(json: json, key: \"\(attributeName)\")" // Allows an integer to be interpreted as a double. + case .url: return "let \(propertyName) = URL(json: json, key: \"\(attributeName)\")" + case let .date(format): return "let \(propertyName) = Date(json: json, key: \"\(attributeName)\", format: \"\(format)\")" + } + } + + private static func letStatementForCustomStructArray( + _ attributeName: String, + _ propertyName: String, + _ elementType: SwiftStruct, + _ hasOptionalElements: Bool + ) + -> LineOfCode { + hasOptionalElements + ? + "let \(propertyName) = \(elementType.name).createOptionalInstances(from: json, arrayKey: \"\(attributeName)\")" + : + "let \(propertyName) = \(elementType.name).createRequiredInstances(from: json, arrayKey: \"\(attributeName)\")" + } + + private static func letStatementForPrimitiveValueArray( + _ attributeName: String, + _ propertyName: String, + _ elementType: SwiftPrimitiveValueType, + _ hasOptionalElements: Bool + ) + -> LineOfCode { + hasOptionalElements + ? letStatementForArrayOfOptionalPrimitiveValues(attributeName, propertyName, elementType) + : letStatementForArrayOfRequiredPrimitiveValues(attributeName, propertyName, elementType) + } + + private static func letStatementForArrayOfOptionalPrimitiveValues( + _ attributeName: String, + _ propertyName: String, + _ elementType: SwiftPrimitiveValueType + ) + -> LineOfCode { + switch elementType { + case .any, .bool, .emptyArray, .int, + .string: return "let \(propertyName) = (json[\"\(attributeName)\"] as? [Any]).map({ $0.toOptionalValueArray() as [\(elementType.name)?] })" + case let .date(format): return "let \(propertyName) = (json[\"\(attributeName)\"] as? [Any]).map({ $0.toOptionalDateArray(withFormat: \"\(format)\") })" + case .double: return "let \(propertyName) = (json[\"\(attributeName)\"] as? [Any]).map({ $0.toOptionalDoubleArray() })" + case .url: return "let \(propertyName) = (json[\"\(attributeName)\"] as? [Any]).map({ $0.toOptionalURLArray() })" + } + } + + private static func letStatementForArrayOfRequiredPrimitiveValues( + _ attributeName: String, + _ propertyName: String, + _ elementType: SwiftPrimitiveValueType + ) + -> LineOfCode { + switch elementType { + case .any: return "let \(propertyName) = json[\"\(attributeName)\"] as? [Any?]" // Any is treated as optional. + case .emptyArray: return "let \(propertyName) = json[\"\(attributeName)\"] as? [[Any?]]" + case .bool, .int, .string: return "let \(propertyName) = json[\"\(attributeName)\"] as? [\(elementType.name)]" + case let .date(format): return "let \(propertyName) = (json[\"\(attributeName)\"] as? [String]).flatMap({ $0.toDateArray(withFormat: \"\(format)\") })" + case .double: return "let \(propertyName) = (json[\"\(attributeName)\"] as? [NSNumber]).map({ $0.toDoubleArray() })" + case .url: return "let \(propertyName) = (json[\"\(attributeName)\"] as? [String]).flatMap({ $0.toURLArray() })" + } + } +} + +extension SwiftPrimitiveValueType { + fileprivate var name: String { + switch self { + case .any: return "Any" + case .bool: return "Bool" + case .date: return "Date" + case .double: return "Double" + case .emptyArray: return "[Any?]" + case .int: return "Int" + case .string: return "String" + case .url: return "URL" + } + } +} diff --git a/json2swift/swift-code-templates.swift b/Sources/libJson2Swift/swift-code-templates.swift similarity index 92% rename from json2swift/swift-code-templates.swift rename to Sources/libJson2Swift/swift-code-templates.swift index 943aae0..979040f 100644 --- a/json2swift/swift-code-templates.swift +++ b/Sources/libJson2Swift/swift-code-templates.swift @@ -19,8 +19,8 @@ let jsonUtilitiesTemplate = [ newline, codeTemplateForStringBasedValueArray, newline, - codeTemplateForOptionalValueArray - ].flatMap({$0}).joined(separator: "\n") + codeTemplateForOptionalValueArray, +].flatMap { $0 }.joined(separator: "\n") private let newline = [""] @@ -75,7 +75,8 @@ private let codeTemplateForCreatableWithJSON = [ " }", " }", " }", - "}"] + "}", +] private let codeTemplateForDateParsing = [ "extension Date {", @@ -104,7 +105,8 @@ private let codeTemplateForDateParsing = [ " guard let date = Date.parse(string: string, format: format) else { return nil }", " self.init(timeIntervalSinceReferenceDate: date.timeIntervalSinceReferenceDate)", " }", - "}"] + "}", +] private let codeTemplateForURL = [ "extension URL {", @@ -112,17 +114,19 @@ private let codeTemplateForURL = [ " guard let string = json[key] as? String else { return nil }", " self.init(string: string)", " }", - "}"] + "}", +] private let codeTemplateForDouble = [ -"extension Double {", -" init?(json: [String: Any], key: String) {", -" // Explicitly unboxing the number allows an integer to be converted to a double,", -" // which is needed when a JSON attribute value can have either representation.", -" guard let nsNumber = json[key] as? NSNumber else { return nil }", -" self.init(_: nsNumber.doubleValue)", -" }", -"}"] + "extension Double {", + " init?(json: [String: Any], key: String) {", + " // Explicitly unboxing the number allows an integer to be converted to a double,", + " // which is needed when a JSON attribute value can have either representation.", + " guard let nsNumber = json[key] as? NSNumber else { return nil }", + " self.init(_: nsNumber.doubleValue)", + " }", + "}", +] private let codeTemplateForDoubleArray = [ "extension Array where Element: NSNumber {", @@ -131,7 +135,8 @@ private let codeTemplateForDoubleArray = [ " func toDoubleArray() -> [Double] {", " return map { $0.doubleValue }", " }", - "}"] + "}", +] private let codeTemplateForStringBasedValueArray = [ "extension Array where Element: CustomStringConvertible {", @@ -152,7 +157,8 @@ private let codeTemplateForStringBasedValueArray = [ " }", " return urlArray", " }", - "}"] + "}", +] private let codeTemplateForOptionalValueArray = [ "extension Array where Element: Any {", @@ -180,4 +186,5 @@ private let codeTemplateForOptionalValueArray = [ " return URL(string: string)", " }", " }", - "}"] + "}", +] diff --git a/json2swift/swift-data-model.swift b/Sources/libJson2Swift/swift-data-model.swift similarity index 56% rename from json2swift/swift-data-model.swift rename to Sources/libJson2Swift/swift-data-model.swift index 1f846ce..5067814 100644 --- a/json2swift/swift-data-model.swift +++ b/Sources/libJson2Swift/swift-data-model.swift @@ -8,6 +8,8 @@ import Foundation +// MARK: - SwiftStruct + struct SwiftStruct { let name: String let properties: [SwiftProperty] @@ -16,37 +18,61 @@ struct SwiftStruct { let nestedStructs: [SwiftStruct] } +// MARK: - SwiftProperty + struct SwiftProperty { let name: String let type: SwiftType } +// MARK: - SwiftType + struct SwiftType { let name: String let isOptional: Bool } +// MARK: - SwiftInitializer + struct SwiftInitializer { let parameters: [SwiftParameter] } +// MARK: - SwiftParameter + struct SwiftParameter { let name: String let type: SwiftType } +// MARK: - SwiftFailableInitializer + struct SwiftFailableInitializer { let requiredTransformations: [TransformationFromJSON] let optionalTransformations: [TransformationFromJSON] } +// MARK: - TransformationFromJSON + enum TransformationFromJSON { - case toCustomStruct( attributeName: String, propertyName: String, type: SwiftStruct) - case toPrimitiveValue( attributeName: String, propertyName: String, type: SwiftPrimitiveValueType) - case toCustomStructArray( attributeName: String, propertyName: String, elementType: SwiftStruct, hasOptionalElements: Bool) - case toPrimitiveValueArray(attributeName: String, propertyName: String, elementType: SwiftPrimitiveValueType, hasOptionalElements: Bool) + case toCustomStruct(attributeName: String, propertyName: String, type: SwiftStruct) + case toPrimitiveValue(attributeName: String, propertyName: String, type: SwiftPrimitiveValueType) + case toCustomStructArray( + attributeName: String, + propertyName: String, + elementType: SwiftStruct, + hasOptionalElements: Bool + ) + case toPrimitiveValueArray( + attributeName: String, + propertyName: String, + elementType: SwiftPrimitiveValueType, + hasOptionalElements: Bool + ) } +// MARK: - SwiftPrimitiveValueType + enum SwiftPrimitiveValueType { case int case double diff --git a/unit_tests/json-attribute-merging-boolean-tests.swift b/Tests/json2swiftTests/json-attribute-merging-boolean-tests.swift similarity index 92% rename from unit_tests/json-attribute-merging-boolean-tests.swift rename to Tests/json2swiftTests/json-attribute-merging-boolean-tests.swift index 036b83e..502587c 100644 --- a/unit_tests/json-attribute-merging-boolean-tests.swift +++ b/Tests/json2swiftTests/json-attribute-merging-boolean-tests.swift @@ -6,11 +6,14 @@ // Copyright © 2016 iJoshSmith. All rights reserved. // +@testable import libJson2Swift import XCTest private let rb = JSONType.bool(isRequired: true) private let ob = JSONType.bool(isRequired: false) +// MARK: - json_attribute_merging_boolean_tests + class json_attribute_merging_boolean_tests: XCTestCase { /* Method Naming Convention @@ -18,14 +21,16 @@ class json_attribute_merging_boolean_tests: XCTestCase { o = Optional b = Boolean */ - + // MARK: - Bool and Bool + func test_rb_and_rb_yields_rb() { XCTAssertEqual(rb + rb, rb) } func test_ob_and_ob_yields_ob() { XCTAssertEqual(ob + ob, ob) } func test_ob_and_rb_yields_ob() { XCTAssertEqual(ob + rb, ob) } func test_rb_and_ob_yields_ob() { XCTAssertEqual(rb + ob, ob) } - + // MARK: - Bool and Nullable + func test_rb_and_nullable_yields_ob() { XCTAssertEqual(rb + .nullable, ob) } func test_ob_and_nullable_yields_ob() { XCTAssertEqual(ob + .nullable, ob) } func test_nullable_and_rb_yields_ob() { XCTAssertEqual(.nullable + rb, ob) } diff --git a/unit_tests/json-attribute-merging-element-array-tests.swift b/Tests/json2swiftTests/json-attribute-merging-element-array-tests.swift similarity index 75% rename from unit_tests/json-attribute-merging-element-array-tests.swift rename to Tests/json2swiftTests/json-attribute-merging-element-array-tests.swift index 3f88b88..8149703 100644 --- a/unit_tests/json-attribute-merging-element-array-tests.swift +++ b/Tests/json2swiftTests/json-attribute-merging-element-array-tests.swift @@ -6,36 +6,39 @@ // Copyright © 2016 iJoshSmith. All rights reserved. // +@testable import libJson2Swift import XCTest private let stringAttributeName = "string-attribute" -private let boolAttributeName = "bool-attribute" -private let dateAttributeName = "date-attribute" +private let boolAttributeName = "bool-attribute" +private let dateAttributeName = "date-attribute" private let schemaX = JSONElementSchema(name: "schema", attributes: [ stringAttributeName: .string(isRequired: false), - boolAttributeName: .bool( isRequired: true), - dateAttributeName: .date( isRequired: true, format: "MM/dd/yyyy") - ]) + boolAttributeName: .bool(isRequired: true), + dateAttributeName: .date(isRequired: true, format: "MM/dd/yyyy"), +]) private let schemaY = JSONElementSchema(name: "schema", attributes: [ stringAttributeName: .string(isRequired: true), - boolAttributeName: .bool( isRequired: false), - dateAttributeName: .date( isRequired: true, format: "MM/dd/yyyy") - ]) + boolAttributeName: .bool(isRequired: false), + dateAttributeName: .date(isRequired: true, format: "MM/dd/yyyy"), +]) private let schemaM = JSONElementSchema(name: "schema", attributes: [ stringAttributeName: .string(isRequired: false), - boolAttributeName: .bool( isRequired: false), - dateAttributeName: .date( isRequired: true, format: "MM/dd/yyyy") - ]) -private let rax = JSONType.elementArray(isRequired: true, elementSchema: schemaX, hasNullableElements: false) + boolAttributeName: .bool(isRequired: false), + dateAttributeName: .date(isRequired: true, format: "MM/dd/yyyy"), +]) +private let rax = JSONType.elementArray(isRequired: true, elementSchema: schemaX, hasNullableElements: false) private let oax = JSONType.elementArray(isRequired: false, elementSchema: schemaX, hasNullableElements: false) -private let ray = JSONType.elementArray(isRequired: true, elementSchema: schemaY, hasNullableElements: false) +private let ray = JSONType.elementArray(isRequired: true, elementSchema: schemaY, hasNullableElements: false) private let oay = JSONType.elementArray(isRequired: false, elementSchema: schemaY, hasNullableElements: false) -private let ram = JSONType.elementArray(isRequired: true, elementSchema: schemaM, hasNullableElements: false) +private let ram = JSONType.elementArray(isRequired: true, elementSchema: schemaM, hasNullableElements: false) private let oam = JSONType.elementArray(isRequired: false, elementSchema: schemaM, hasNullableElements: false) -private let raxn = JSONType.elementArray(isRequired: true, elementSchema: schemaX, hasNullableElements: true) +private let raxn = JSONType.elementArray(isRequired: true, elementSchema: schemaX, hasNullableElements: true) private let oaxn = JSONType.elementArray(isRequired: false, elementSchema: schemaX, hasNullableElements: true) +// MARK: - json_attribute_merging_element_array_tests + class json_attribute_merging_element_array_tests: XCTestCase { /* Method Naming Convention @@ -47,26 +50,30 @@ class json_attribute_merging_element_array_tests: XCTestCase { y = Schema Y m = Merged Schema (X + Y) */ - + // MARK: - Array of Non-Nullable Elements and Nullable + func test_rax_and_nullable_yields_oax() { XCTAssertEqual(rax + .nullable, oax) } func test_oax_and_nullable_yields_oax() { XCTAssertEqual(oax + .nullable, oax) } func test_nullable_and_rax_yields_oax() { XCTAssertEqual(.nullable + rax, oax) } func test_nullable_and_oax_yields_oax() { XCTAssertEqual(.nullable + oax, oax) } - + // MARK: - Array of Nullable Elements and Nullable + func test_raxn_and_nullable_yields_oaxn() { XCTAssertEqual(raxn + .nullable, oaxn) } func test_oaxn_and_nullable_yields_oaxn() { XCTAssertEqual(oaxn + .nullable, oaxn) } func test_nullable_and_raxn_yields_oaxn() { XCTAssertEqual(.nullable + raxn, oaxn) } func test_nullable_and_oaxn_yields_oaxn() { XCTAssertEqual(.nullable + oaxn, oaxn) } - + // MARK: - Arrays with same schema + func test_ra_and_ra_yields_ra() { XCTAssertEqual(rax + rax, rax) } func test_ra_and_oa_yields_oa() { XCTAssertEqual(rax + oax, oax) } func test_oa_and_ra_yields_oa() { XCTAssertEqual(oax + rax, oax) } func test_oa_and_oa_yields_oa() { XCTAssertEqual(oax + oax, oax) } - + // MARK: - Arrays with different schemas + func test_rax_and_ray_yields_ram() { XCTAssertEqual(rax + ray, ram) } func test_ray_and_rax_yields_ram() { XCTAssertEqual(ray + rax, ram) } func test_rax_and_oay_yields_oam() { XCTAssertEqual(rax + oay, oam) } diff --git a/unit_tests/json-attribute-merging-element-tests.swift b/Tests/json2swiftTests/json-attribute-merging-element-tests.swift similarity index 75% rename from unit_tests/json-attribute-merging-element-tests.swift rename to Tests/json2swiftTests/json-attribute-merging-element-tests.swift index 820b4e3..03c4314 100644 --- a/unit_tests/json-attribute-merging-element-tests.swift +++ b/Tests/json2swiftTests/json-attribute-merging-element-tests.swift @@ -6,33 +6,36 @@ // Copyright © 2016 iJoshSmith. All rights reserved. // +@testable import libJson2Swift import XCTest private let stringAttributeName = "string-attribute" -private let boolAttributeName = "bool-attribute" -private let dateAttributeName = "date-attribute" +private let boolAttributeName = "bool-attribute" +private let dateAttributeName = "date-attribute" private let schemaX = JSONElementSchema(name: "schema", attributes: [ stringAttributeName: .string(isRequired: false), - boolAttributeName: .bool( isRequired: true), - dateAttributeName: .date( isRequired: true, format: "MM/dd/yyyy") - ]) + boolAttributeName: .bool(isRequired: true), + dateAttributeName: .date(isRequired: true, format: "MM/dd/yyyy"), +]) private let schemaY = JSONElementSchema(name: "schema", attributes: [ stringAttributeName: .string(isRequired: true), - boolAttributeName: .bool( isRequired: false), - dateAttributeName: .date( isRequired: true, format: "MM/dd/yyyy") - ]) + boolAttributeName: .bool(isRequired: false), + dateAttributeName: .date(isRequired: true, format: "MM/dd/yyyy"), +]) private let schemaM = JSONElementSchema(name: "schema", attributes: [ stringAttributeName: .string(isRequired: false), - boolAttributeName: .bool( isRequired: false), - dateAttributeName: .date( isRequired: true, format: "MM/dd/yyyy") - ]) -private let rex = JSONType.element(isRequired: true, schema: schemaX) + boolAttributeName: .bool(isRequired: false), + dateAttributeName: .date(isRequired: true, format: "MM/dd/yyyy"), +]) +private let rex = JSONType.element(isRequired: true, schema: schemaX) private let oex = JSONType.element(isRequired: false, schema: schemaX) -private let rey = JSONType.element(isRequired: true, schema: schemaY) +private let rey = JSONType.element(isRequired: true, schema: schemaY) private let oey = JSONType.element(isRequired: false, schema: schemaY) -private let rem = JSONType.element(isRequired: true, schema: schemaM) +private let rem = JSONType.element(isRequired: true, schema: schemaM) private let oem = JSONType.element(isRequired: false, schema: schemaM) +// MARK: - json_attribute_merging_element_tests + class json_attribute_merging_element_tests: XCTestCase { /* Method Naming Convention @@ -43,20 +46,23 @@ class json_attribute_merging_element_tests: XCTestCase { y = Schema Y m = Merged Schema (X + Y) */ - + // MARK: - Element and Nullable + func test_re_and_nullable_yields_oe() { XCTAssertEqual(rex + .nullable, oex) } func test_oe_and_nullable_yields_oe() { XCTAssertEqual(oex + .nullable, oex) } func test_nullable_and_re_yields_oe() { XCTAssertEqual(.nullable + rex, oex) } func test_nullable_and_oe_yields_oe() { XCTAssertEqual(.nullable + oex, oex) } - + // MARK: - Elements with same schema + func test_re_and_re_yields_re() { XCTAssertEqual(rex + rex, rex) } func test_re_and_oe_yields_oe() { XCTAssertEqual(rex + oex, oex) } func test_oe_and_re_yields_oe() { XCTAssertEqual(oex + rex, oex) } func test_oe_and_oe_yields_oe() { XCTAssertEqual(oex + oex, oex) } - + // MARK: - Elements with different schemas + func test_rex_and_rey_yields_rem() { XCTAssertEqual(rex + rey, rem) } func test_rey_and_rex_yields_rem() { XCTAssertEqual(rey + rex, rem) } func test_rex_and_oey_yields_oem() { XCTAssertEqual(rex + oey, oem) } diff --git a/unit_tests/json-attribute-merging-empty-array-tests.swift b/Tests/json2swiftTests/json-attribute-merging-empty-array-tests.swift similarity index 84% rename from unit_tests/json-attribute-merging-empty-array-tests.swift rename to Tests/json2swiftTests/json-attribute-merging-empty-array-tests.swift index 6094624..70f1463 100644 --- a/unit_tests/json-attribute-merging-empty-array-tests.swift +++ b/Tests/json2swiftTests/json-attribute-merging-empty-array-tests.swift @@ -6,16 +6,19 @@ // Copyright © 2016 iJoshSmith. All rights reserved. // +@testable import libJson2Swift import XCTest private let schemaX = JSONElementSchema(name: "schema", attributes: [:]) -private let rax = JSONType.elementArray(isRequired: true, elementSchema: schemaX, hasNullableElements: false) +private let rax = JSONType.elementArray(isRequired: true, elementSchema: schemaX, hasNullableElements: false) private let oax = JSONType.elementArray(isRequired: false, elementSchema: schemaX, hasNullableElements: false) -private let ras = JSONType.valueArray(isRequired: true, valueType: .string(isRequired: true)) +private let ras = JSONType.valueArray(isRequired: true, valueType: .string(isRequired: true)) private let oas = JSONType.valueArray(isRequired: false, valueType: .string(isRequired: true)) +// MARK: - json_attribute_merging_empty_array_tests + class json_attribute_merging_empty_array_tests: XCTestCase { /* Method Naming Convention @@ -25,17 +28,20 @@ class json_attribute_merging_empty_array_tests: XCTestCase { x = Schema X s = String */ - + // MARK: - Empty Array and Empty Array + func test_emptyArray_and_emptyArray_yields_emptyArray() { XCTAssertEqual(.emptyArray + .emptyArray, .emptyArray) } - + // MARK: - Element Array and Empty Array + func test_rax_and_emptyArray_yields_rax() { XCTAssertEqual(rax + .emptyArray, rax) } func test_oax_and_emptyArray_yields_oax() { XCTAssertEqual(oax + .emptyArray, oax) } func test_emptyArray_and_rax_yields_rax() { XCTAssertEqual(.emptyArray + rax, rax) } func test_emptyArray_and_oax_yields_oax() { XCTAssertEqual(.emptyArray + oax, oax) } - + // MARK: - Value Array and Empty Array + func test_ras_and_emptyArray_yields_ras() { XCTAssertEqual(ras + .emptyArray, ras) } func test_oas_and_emptyArray_yields_oas() { XCTAssertEqual(oas + .emptyArray, oas) } func test_emptyArray_and_ras_yields_ras() { XCTAssertEqual(.emptyArray + ras, ras) } diff --git a/unit_tests/json-attribute-merging-numeric-tests.swift b/Tests/json2swiftTests/json-attribute-merging-numeric-tests.swift similarity index 90% rename from unit_tests/json-attribute-merging-numeric-tests.swift rename to Tests/json2swiftTests/json-attribute-merging-numeric-tests.swift index 7851055..c6fe818 100644 --- a/unit_tests/json-attribute-merging-numeric-tests.swift +++ b/Tests/json2swiftTests/json-attribute-merging-numeric-tests.swift @@ -6,13 +6,16 @@ // Copyright © 2016 iJoshSmith. All rights reserved. // +@testable import libJson2Swift import XCTest -private let ri = JSONType.number(isRequired: true, isFloatingPoint: false) +private let ri = JSONType.number(isRequired: true, isFloatingPoint: false) private let oi = JSONType.number(isRequired: false, isFloatingPoint: false) -private let rf = JSONType.number(isRequired: true, isFloatingPoint: true) +private let rf = JSONType.number(isRequired: true, isFloatingPoint: true) private let of = JSONType.number(isRequired: false, isFloatingPoint: true) +// MARK: - json_attribute_merging_numeric_tests + class json_attribute_merging_numeric_tests: XCTestCase { /* Method Naming Convention @@ -21,32 +24,37 @@ class json_attribute_merging_numeric_tests: XCTestCase { i = Integral f = Floating Point */ - + // MARK: - Both are required + func test_ri_and_ri_yields_ri() { XCTAssertEqual(ri + ri, ri) } func test_ri_and_rf_yields_rf() { XCTAssertEqual(ri + rf, rf) } func test_rf_and_ri_yields_rf() { XCTAssertEqual(rf + ri, rf) } func test_rf_and_rf_yields_rf() { XCTAssertEqual(rf + rf, rf) } - + // MARK: - Both are optional + func test_oi_and_oi_yields_oi() { XCTAssertEqual(oi + oi, oi) } func test_oi_and_of_yields_of() { XCTAssertEqual(oi + of, of) } func test_of_and_oi_yields_of() { XCTAssertEqual(of + oi, of) } func test_of_and_of_yields_of() { XCTAssertEqual(of + of, of) } - + // MARK: - One optional, one required + func test_oi_and_ri_yields_oi() { XCTAssertEqual(oi + ri, oi) } func test_of_and_rf_yields_of() { XCTAssertEqual(of + rf, of) } func test_ri_and_oi_yields_oi() { XCTAssertEqual(ri + oi, oi) } func test_rf_and_of_yields_of() { XCTAssertEqual(rf + of, of) } - + // MARK: - One is a required number, the other is nullable + func test_ri_and_nullable_yields_oi() { XCTAssertEqual(ri + .nullable, oi) } func test_rf_and_nullable_yields_of() { XCTAssertEqual(rf + .nullable, of) } func test_nullable_and_ri_yields_oi() { XCTAssertEqual(.nullable + ri, oi) } func test_nullable_and_rf_yields_of() { XCTAssertEqual(.nullable + rf, of) } - + // MARK: - One is an optional number, the other is nullable + func test_oi_and_nullable_yields_oi() { XCTAssertEqual(oi + .nullable, oi) } func test_of_and_nullable_yields_of() { XCTAssertEqual(of + .nullable, of) } func test_nullable_and_oi_yields_oi() { XCTAssertEqual(.nullable + oi, oi) } diff --git a/unit_tests/json-attribute-merging-text-tests.swift b/Tests/json2swiftTests/json-attribute-merging-text-tests.swift similarity index 96% rename from unit_tests/json-attribute-merging-text-tests.swift rename to Tests/json2swiftTests/json-attribute-merging-text-tests.swift index 6584580..6d62523 100644 --- a/unit_tests/json-attribute-merging-text-tests.swift +++ b/Tests/json2swiftTests/json-attribute-merging-text-tests.swift @@ -6,15 +6,18 @@ // Copyright © 2016 iJoshSmith. All rights reserved. // +@testable import libJson2Swift import XCTest -private let rd = JSONType.date(isRequired: true, format: "M/d/yyyy") +private let rd = JSONType.date(isRequired: true, format: "M/d/yyyy") private let od = JSONType.date(isRequired: false, format: "M/d/yyyy") private let ru = JSONType.url(isRequired: true) private let ou = JSONType.url(isRequired: false) private let rs = JSONType.string(isRequired: true) private let os = JSONType.string(isRequired: false) +// MARK: - json_attribute_merging_text_tests + class json_attribute_merging_text_tests: XCTestCase { /* Method Naming Convention @@ -24,63 +27,73 @@ class json_attribute_merging_text_tests: XCTestCase { u = URL s = String */ - + // MARK: - String and String yields String + func test_rs_and_rs_yields_rs() { XCTAssertEqual(rs + rs, rs) } func test_rs_and_os_yields_os() { XCTAssertEqual(rs + os, os) } func test_os_and_rs_yields_os() { XCTAssertEqual(os + rs, os) } func test_os_and_os_yields_os() { XCTAssertEqual(os + os, os) } - + // MARK: - String and Nullable yields Optional String + func test_rs_and_nullable_yields_os() { XCTAssertEqual(rs + .nullable, os) } func test_os_and_nullable_yields_os() { XCTAssertEqual(os + .nullable, os) } func test_nullable_and_rs_yields_os() { XCTAssertEqual(.nullable + rs, os) } func test_nullable_and_os_yields_os() { XCTAssertEqual(.nullable + os, os) } - + // MARK: - URL and URL yields URL + func test_ru_and_ru_yields_ru() { XCTAssertEqual(ru + ru, ru) } func test_ru_and_ou_yields_ou() { XCTAssertEqual(ru + ou, ou) } func test_ou_and_ru_yields_ou() { XCTAssertEqual(ou + ru, ou) } func test_ou_and_ou_yields_ou() { XCTAssertEqual(ou + ou, ou) } - + // MARK: - URL and Nullable yields Optional URL + func test_ru_and_nullable_yields_ou() { XCTAssertEqual(ru + .nullable, ou) } func test_ou_and_nullable_yields_ou() { XCTAssertEqual(ou + .nullable, ou) } func test_nullable_and_ru_yields_ou() { XCTAssertEqual(.nullable + ru, ou) } func test_nullable_and_ou_yields_ou() { XCTAssertEqual(.nullable + ou, ou) } - + // MARK: - URL and String yields String + func test_ru_and_rs_yields_rs() { XCTAssertEqual(ru + rs, rs) } func test_ru_and_os_yields_os() { XCTAssertEqual(ru + os, os) } func test_ou_and_rs_yields_os() { XCTAssertEqual(ou + rs, os) } func test_ou_and_os_yields_os() { XCTAssertEqual(ou + os, os) } - + // MARK: - Date and Date yields Date + func test_rd_and_rd_yields_rd() { XCTAssertEqual(rd + rd, rd) } func test_rd_and_od_yields_od() { XCTAssertEqual(rd + od, od) } func test_od_and_rd_yields_od() { XCTAssertEqual(od + rd, od) } func test_od_and_od_yields_od() { XCTAssertEqual(od + od, od) } - + // MARK: - Different date formats + func test_different_date_formats_yields_first_format() { let dateTypeA = JSONType.date(isRequired: true, format: "first-format") let dateTypeB = JSONType.date(isRequired: true, format: "second-format") XCTAssertEqual(dateTypeA + dateTypeB, dateTypeA) } - + // MARK: - Date and Nullable yields Optional Date + func test_rd_and_nullable_yields_od() { XCTAssertEqual(rd + .nullable, od) } func test_od_and_nullable_yields_od() { XCTAssertEqual(od + .nullable, od) } func test_nullable_and_rd_yields_od() { XCTAssertEqual(.nullable + rd, od) } func test_nullable_and_od_yields_od() { XCTAssertEqual(.nullable + od, od) } // MARK: - Date and URL yields String + func test_rd_and_ru_yields_rs() { XCTAssertEqual(rd + ru, rs) } func test_rd_and_ou_yields_os() { XCTAssertEqual(rd + ou, os) } func test_od_and_ru_yields_os() { XCTAssertEqual(od + ru, os) } func test_od_and_ou_yields_os() { XCTAssertEqual(od + ou, os) } - + // MARK: - Date and String yields Date (to allow for only one attribute to need a DATE_FORMAT) + func test_rd_and_rs_yields_rd() { XCTAssertEqual(rd + rs, rd) } func test_rd_and_os_yields_od() { XCTAssertEqual(rd + os, od) } func test_od_and_rs_yields_od() { XCTAssertEqual(od + rs, od) } diff --git a/unit_tests/json-attribute-merging-value-array-tests.swift b/Tests/json2swiftTests/json-attribute-merging-value-array-tests.swift similarity index 78% rename from unit_tests/json-attribute-merging-value-array-tests.swift rename to Tests/json2swiftTests/json-attribute-merging-value-array-tests.swift index b2223f8..634fc11 100644 --- a/unit_tests/json-attribute-merging-value-array-tests.swift +++ b/Tests/json2swiftTests/json-attribute-merging-value-array-tests.swift @@ -6,12 +6,15 @@ // Copyright © 2016 iJoshSmith. All rights reserved. // +@testable import libJson2Swift import XCTest -private let rars = JSONType.valueArray(isRequired: true, valueType: .string(isRequired: true)) -private let raos = JSONType.valueArray(isRequired: true, valueType: .string(isRequired: false)) -private let oars = JSONType.valueArray(isRequired: false, valueType: .string(isRequired: true)) -private let oaos = JSONType.valueArray(isRequired: false, valueType: .string(isRequired: false)) +private let rars = JSONType.valueArray(isRequired: true, valueType: .string(isRequired: true)) +private let raos = JSONType.valueArray(isRequired: true, valueType: .string(isRequired: false)) +private let oars = JSONType.valueArray(isRequired: false, valueType: .string(isRequired: true)) +private let oaos = JSONType.valueArray(isRequired: false, valueType: .string(isRequired: false)) + +// MARK: - json_attribute_merging_value_array_tests class json_attribute_merging_value_array_tests: XCTestCase { /* @@ -21,8 +24,9 @@ class json_attribute_merging_value_array_tests: XCTestCase { a = Array s = String */ - + // MARK: - Arrays with same value type + func test_rars_and_rars_yields_rars() { XCTAssertEqual(rars + rars, rars) } func test_rars_and_raos_yields_raos() { XCTAssertEqual(rars + raos, raos) } func test_raos_and_rars_yields_raos() { XCTAssertEqual(raos + rars, raos) } diff --git a/unit_tests/json-schema-inference-element-array-tests.swift b/Tests/json2swiftTests/json-schema-inference-element-array-tests.swift similarity index 78% rename from unit_tests/json-schema-inference-element-array-tests.swift rename to Tests/json2swiftTests/json-schema-inference-element-array-tests.swift index 35d4b7c..b5dc974 100644 --- a/unit_tests/json-schema-inference-element-array-tests.swift +++ b/Tests/json2swiftTests/json-schema-inference-element-array-tests.swift @@ -6,10 +6,13 @@ // Copyright © 2016 iJoshSmith. All rights reserved. // +@testable import libJson2Swift import XCTest private let dummyName = "fake-array" +// MARK: - json_schema_inference_element_array_tests + class json_schema_inference_element_array_tests: XCTestCase { func test_empty_array() { let elementArray = [JSONElement]() @@ -17,108 +20,101 @@ class json_schema_inference_element_array_tests: XCTestCase { XCTAssertEqual(schema.name, dummyName) XCTAssertTrue(schema.attributes.isEmpty) } - + func test_one_element_array() { let elementArray = [ [ - "numeric-attribute": 42 - ] + "numeric-attribute": 42, + ], ] let schema = JSONElementSchema.inferred(from: elementArray, named: dummyName) if let attribute = schema.attributes["numeric-attribute"] { XCTAssertEqual(attribute, .number(isRequired: true, isFloatingPoint: false)) - } - else { XCTFail() } + } else { XCTFail() } } - + func test_two_element_array() { let elementArray = [ [ - "numeric-attribute": 42 + "numeric-attribute": 42, ], [ - "numeric-attribute": 3.14 - ] + "numeric-attribute": 3.14, + ], ] let schema = JSONElementSchema.inferred(from: elementArray, named: dummyName) if let attribute = schema.attributes["numeric-attribute"] { XCTAssertEqual(attribute, .number(isRequired: true, isFloatingPoint: true)) - } - else { XCTFail() } + } else { XCTFail() } } - + func test_three_element_array() { let elementArray = [ [ - "url-attribute": "http://abc.com/kitty.png" + "url-attribute": "http://abc.com/kitty.png", ], [ - "url-attribute": NSNull() + "url-attribute": NSNull(), ], [ - "url-attribute": "http://xyz.org/foo?q=bar" - ] + "url-attribute": "http://xyz.org/foo?q=bar", + ], ] let schema = JSONElementSchema.inferred(from: elementArray, named: dummyName) if let attribute = schema.attributes["url-attribute"] { XCTAssertEqual(attribute, .url(isRequired: false)) - } - else { XCTFail() } + } else { XCTFail() } } - + func test_three_element_array_with_two_attributes_per_element() { let elementArray = [ [ "string-attribute": "apple", - "bool-attribute": true + "bool-attribute": true, ], [ "string-attribute": "banana", - "bool-attribute": false + "bool-attribute": false, ], [ - "string-attribute": "cherry" + "string-attribute": "cherry", // Omitting bool-attribute makes it optional. - ] + ], ] let schema = JSONElementSchema.inferred(from: elementArray, named: dummyName) XCTAssertEqual(schema.attributes.count, 2) - + if let stringAttribute = schema.attributes["string-attribute"] { XCTAssertEqual(stringAttribute, .string(isRequired: true)) - } - else { XCTFail() } - + } else { XCTFail() } + if let boolAttribute = schema.attributes["bool-attribute"] { XCTAssertEqual(boolAttribute, .bool(isRequired: false)) - } - else { XCTFail() } + } else { XCTFail() } } - + func test_element_array_with_null_value() { let elementArray: Any = [ [ "string-attribute": "apple", - "bool-attribute": true + "bool-attribute": true, ], NSNull(), [ "string-attribute": "banana", - "bool-attribute": false - ] + "bool-attribute": false, + ], ] let schema = JSONElementSchema.inferred(from: ["array-attribute": elementArray], named: "foo") XCTAssertEqual(schema.attributes.count, 1) - + if let arrayAttribute = schema.attributes["array-attribute"] { if case let .elementArray(isRequired, elementSchema, hasNullableElements) = arrayAttribute { XCTAssertTrue(isRequired) XCTAssertEqual(elementSchema.name, "array-attribute") XCTAssertEqual(elementSchema.attributes.count, 2) XCTAssertTrue(hasNullableElements) - } - else { XCTFail() } - } - else { XCTFail() } + } else { XCTFail() } + } else { XCTFail() } } } diff --git a/unit_tests/json-schema-inference-element-tests.swift b/Tests/json2swiftTests/json-schema-inference-element-tests.swift similarity index 82% rename from unit_tests/json-schema-inference-element-tests.swift rename to Tests/json2swiftTests/json-schema-inference-element-tests.swift index 694e342..f6ceabf 100644 --- a/unit_tests/json-schema-inference-element-tests.swift +++ b/Tests/json2swiftTests/json-schema-inference-element-tests.swift @@ -6,105 +6,104 @@ // Copyright © 2016 iJoshSmith. All rights reserved. // +@testable import libJson2Swift import XCTest private let dummyName = "fake-element" +// MARK: - json_schema_inference_element_tests + class json_schema_inference_element_tests: XCTestCase { func test_null() { let jsonElement = ["null-attribute": NSNull()] let schema = JSONElementSchema.inferred(from: jsonElement, named: dummyName) if let type = schema.attributes["null-attribute"] { XCTAssertEqual(type, .nullable) - } - else { XCTFail() } + } else { XCTFail() } } - + func test_bool() { let jsonElement = ["bool-attribute": true] let schema = JSONElementSchema.inferred(from: jsonElement, named: dummyName) if let type = schema.attributes["bool-attribute"] { XCTAssertEqual(type, .bool(isRequired: true)) - } - else { XCTFail() } + } else { XCTFail() } } - + func test_integer() { let jsonElement = ["int-attribute": 42] let schema = JSONElementSchema.inferred(from: jsonElement, named: dummyName) if let type = schema.attributes["int-attribute"] { XCTAssertEqual(type, .number(isRequired: true, isFloatingPoint: false)) - } - else { XCTFail() } + } else { XCTFail() } } - + func test_floating_point() { let jsonElement = ["floating-point-attribute": 3.14] let schema = JSONElementSchema.inferred(from: jsonElement, named: dummyName) if let type = schema.attributes["floating-point-attribute"] { XCTAssertEqual(type, .number(isRequired: true, isFloatingPoint: true)) - } - else { XCTFail() } + } else { XCTFail() } } - + func test_string() { let jsonElement = ["string-attribute": "some text"] let schema = JSONElementSchema.inferred(from: jsonElement, named: dummyName) if let type = schema.attributes["string-attribute"] { XCTAssertEqual(type, .string(isRequired: true)) - } - else { XCTFail() } + } else { XCTFail() } } - + func test_url() { let jsonElement = ["url-attribute": "http://ijoshsmith.com"] let schema = JSONElementSchema.inferred(from: jsonElement, named: dummyName) if let type = schema.attributes["url-attribute"] { XCTAssertEqual(type, .url(isRequired: true)) - } - else { XCTFail() } + } else { XCTFail() } } - + func test_date() { let jsonElement = ["date-attribute": "DATE_FORMAT=MM/dd/yyyy"] let schema = JSONElementSchema.inferred(from: jsonElement, named: dummyName) if let type = schema.attributes["date-attribute"] { XCTAssertEqual(type, .date(isRequired: true, format: "MM/dd/yyyy")) - } - else { XCTFail() } + } else { XCTFail() } } - + func test_element() { let jsonElement = [ "element-attribute": [ - "string-attribute": "some text" - ] + "string-attribute": "some text", + ], ] let schema = JSONElementSchema.inferred(from: jsonElement, named: dummyName) if let type = schema.attributes["element-attribute"] { let expectedSchema = JSONElementSchema( name: "element-attribute", - attributes: ["string-attribute": .string(isRequired: true)]) + attributes: ["string-attribute": .string(isRequired: true)] + ) XCTAssertEqual(type, .element(isRequired: true, schema: expectedSchema)) - } - else { XCTFail() } + } else { XCTFail() } } - + func test_element_array() { let jsonElementArray = [ "array-attribute": [ [ - "string-attribute": "some text" - ] - ] + "string-attribute": "some text", + ], + ], ] let schema = JSONElementSchema.inferred(from: jsonElementArray, named: dummyName) if let type = schema.attributes["array-attribute"] { let expectedSchema = JSONElementSchema( name: "array-attribute", - attributes: ["string-attribute": .string(isRequired: true)]) - XCTAssertEqual(type, .elementArray(isRequired: true, elementSchema: expectedSchema, hasNullableElements: false)) - } - else { XCTFail() } + attributes: ["string-attribute": .string(isRequired: true)] + ) + XCTAssertEqual( + type, + .elementArray(isRequired: true, elementSchema: expectedSchema, hasNullableElements: false) + ) + } else { XCTFail() } } } diff --git a/unit_tests/json-schema-inference-value-array-tests.swift b/Tests/json2swiftTests/json-schema-inference-value-array-tests.swift similarity index 71% rename from unit_tests/json-schema-inference-value-array-tests.swift rename to Tests/json2swiftTests/json-schema-inference-value-array-tests.swift index e6cc9ab..bd9e805 100644 --- a/unit_tests/json-schema-inference-value-array-tests.swift +++ b/Tests/json2swiftTests/json-schema-inference-value-array-tests.swift @@ -6,105 +6,124 @@ // Copyright © 2016 iJoshSmith. All rights reserved. // +@testable import libJson2Swift import XCTest private let dummyName = "fake-element" +// MARK: - json_schema_inference_value_array_tests + class json_schema_inference_value_array_tests: XCTestCase { func test_array_of_integer() { let valuesArray = [ "integers-attribute": [ - 1, 2, 3 - ] + 1, 2, 3, + ], ] let schema = JSONElementSchema.inferred(from: valuesArray, named: dummyName) if let type = schema.attributes["integers-attribute"] { let arrayOfInteger: JSONType = - .valueArray(isRequired: true, valueType: - .number(isRequired: true, isFloatingPoint: false)) + .valueArray( + isRequired: true, + valueType: + .number(isRequired: true, isFloatingPoint: false) + ) XCTAssertEqual(type, arrayOfInteger) - } - else { XCTFail() } + } else { XCTFail() } } - + func test_array_of_array_of_integer() { let valuesArray = [ "integer-arrays-attribute": [ [1, 2, 3], [4, 5], [6], - ] + ], ] let schema = JSONElementSchema.inferred(from: valuesArray, named: dummyName) if let type = schema.attributes["integer-arrays-attribute"] { let arrayOfArrayOfInteger: JSONType = - .valueArray(isRequired: true, valueType: - .valueArray(isRequired: true, valueType: - .number(isRequired: true, isFloatingPoint: false))) + .valueArray( + isRequired: true, + valueType: + .valueArray( + isRequired: true, + valueType: + .number(isRequired: true, isFloatingPoint: false) + ) + ) XCTAssertEqual(type, arrayOfArrayOfInteger) - } - else { XCTFail() } + } else { XCTFail() } } - + func test_array_of_array_of_optional_bool() { let valuesArray = [ "boolean-arrays-attribute": [ - [true, false, true], + [true, false, true], [false, NSNull(), false], - [false, false, true], - ] + [false, false, true], + ], ] let schema = JSONElementSchema.inferred(from: valuesArray, named: dummyName) if let type = schema.attributes["boolean-arrays-attribute"] { let arrayOfArrayOfOptionalBoolean: JSONType = - .valueArray(isRequired: true, valueType: - .valueArray(isRequired: true, valueType: - .bool(isRequired: false))) + .valueArray( + isRequired: true, + valueType: + .valueArray( + isRequired: true, + valueType: + .bool(isRequired: false) + ) + ) XCTAssertEqual(type, arrayOfArrayOfOptionalBoolean) - } - else { XCTFail() } + } else { XCTFail() } } - + func test_array_of_optional_array_of_optional_string() { let valuesArray = [ "string-arrays-attribute": [ NSNull(), ["A", "B"], ["C", "D", NSNull()], - ] + ], ] let schema = JSONElementSchema.inferred(from: valuesArray, named: dummyName) if let type = schema.attributes["string-arrays-attribute"] { let arrayOfOptionalArrayOfOptionalString: JSONType = - .valueArray(isRequired: true, valueType: - .valueArray(isRequired: false, valueType: - .string(isRequired: false))) + .valueArray( + isRequired: true, + valueType: + .valueArray( + isRequired: false, + valueType: + .string(isRequired: false) + ) + ) XCTAssertEqual(type, arrayOfOptionalArrayOfOptionalString) - } - else { XCTFail() } + } else { XCTFail() } } - + func test_url_array_and_empty_array() { let jsonArray = [ [ "url-array-attribute": [ - "http://fake-url.com" - ] + "http://fake-url.com", + ], ], [ "url-array-attribute": [ // Empty array - ] - ] + ], + ], ] let schema = JSONElementSchema.inferred(from: jsonArray, named: dummyName) if let type = schema.attributes["url-array-attribute"] { let arrayOfRequiredURL: JSONType = .valueArray(isRequired: true, valueType: .url(isRequired: true)) XCTAssertEqual(type, arrayOfRequiredURL) - } - else { XCTFail() } + } else { XCTFail() } } - + func test_mixed_array() { let jsonArray = [ "mixed-array-attribute": [ @@ -113,17 +132,16 @@ class json_schema_inference_value_array_tests: XCTestCase { 42, NSNull(), 3.14, - true - ] + true, + ], ] let schema = JSONElementSchema.inferred(from: jsonArray, named: dummyName) if let type = schema.attributes["mixed-array-attribute"] { let arrayOfAnything: JSONType = .valueArray(isRequired: true, valueType: .anything) XCTAssertEqual(type, arrayOfAnything) - } - else { XCTFail() } + } else { XCTFail() } } - + func test_mixed_array_and_empty_array() { let jsonArray = [ [ @@ -133,20 +151,19 @@ class json_schema_inference_value_array_tests: XCTestCase { 42, NSNull(), 3.14, - true - ] + true, + ], ], [ "mixed-array-attribute": [ // Empty array - ] + ], ], ] let schema = JSONElementSchema.inferred(from: jsonArray, named: dummyName) if let type = schema.attributes["mixed-array-attribute"] { let arrayOfAnything: JSONType = .valueArray(isRequired: true, valueType: .anything) XCTAssertEqual(type, arrayOfAnything) - } - else { XCTFail() } + } else { XCTFail() } } } diff --git a/unit_tests/name-translation-tests.swift b/Tests/json2swiftTests/name-translation-tests.swift similarity index 97% rename from unit_tests/name-translation-tests.swift rename to Tests/json2swiftTests/name-translation-tests.swift index 3368874..2ca1957 100644 --- a/unit_tests/name-translation-tests.swift +++ b/Tests/json2swiftTests/name-translation-tests.swift @@ -6,69 +6,69 @@ // Copyright © 2016 iJoshSmith. All rights reserved. // +@testable import libJson2Swift import XCTest class name_translation_tests: XCTestCase { - // MARK: - Swift names from JSON names - + func test_to_swift_struct_name_unchanged_if_valid() { let jsonName = "MyStruct" XCTAssertEqual(jsonName.toSwiftStructName(), "MyStruct") } - + func test_to_swift_property_name_unchanged_if_valid() { let jsonName = "someProperty" XCTAssertEqual(jsonName.toSwiftPropertyName(), "someProperty") } - + func test_to_swift_struct_name_removes_hyphen() { let jsonName = "fake-name" XCTAssertEqual(jsonName.toSwiftStructName(), "FakeName") } - + func test_to_swift_struct_name_removes_underscore() { let jsonName = "fake_name" XCTAssertEqual(jsonName.toSwiftStructName(), "FakeName") } - + func test_to_swift_property_name_removes_hyphen() { let jsonName = "fake-name" XCTAssertEqual(jsonName.toSwiftPropertyName(), "fakeName") } - + func test_to_swift_property_name_removes_leading_underscore() { let jsonName = "_foo" XCTAssertEqual(jsonName.toSwiftPropertyName(), "foo") } - + func test_to_swift_property_name_removes_trailing_underscore() { let jsonName = "foo_" XCTAssertEqual(jsonName.toSwiftPropertyName(), "foo") } - + func test_to_swift_property_name_removes_separator_underscore() { let jsonName = "foo_bar" XCTAssertEqual(jsonName.toSwiftPropertyName(), "fooBar") } - + func test_to_swift_property_name_removes_two_consecutive_separator_underscores() { let jsonName = "foo__bar" XCTAssertEqual(jsonName.toSwiftPropertyName(), "fooBar") } - + func test_to_swift_property_name_preserves_trailing_caps() { let jsonName = "SomeHTML" XCTAssertEqual(jsonName.toSwiftPropertyName(), "someHTML") } - + func test_to_swift_property_name_adds_underscore_before_initial_number() { let jsonName = "4th_item" XCTAssertEqual(jsonName.toSwiftPropertyName(), "_4thItem") } - + // MARK: - Avoiding Swift keywords - + func test_to_swift_property_name_keyword_has_underscore_prefix() { let jsonName = "private" XCTAssertEqual(jsonName.toSwiftPropertyName(), "_private") diff --git a/unit_tests/swift-code-generation-custom-struct-tests.swift b/Tests/json2swiftTests/swift-code-generation-custom-struct-tests.swift similarity index 51% rename from unit_tests/swift-code-generation-custom-struct-tests.swift rename to Tests/json2swiftTests/swift-code-generation-custom-struct-tests.swift index f1f6677..1a16c84 100644 --- a/unit_tests/swift-code-generation-custom-struct-tests.swift +++ b/Tests/json2swiftTests/swift-code-generation-custom-struct-tests.swift @@ -6,34 +6,61 @@ // Copyright © 2016 iJoshSmith. All rights reserved. // +@testable import libJson2Swift import XCTest class swift_code_generation_custom_struct_tests: XCTestCase { + // MARK: Internal + func test_custom_struct() { let customStruct = createStruct(named: "SomeStruct") - let transformation = TransformationFromJSON.toCustomStruct(attributeName: "a", propertyName: "p", type: customStruct) + let transformation = TransformationFromJSON.toCustomStruct( + attributeName: "a", + propertyName: "p", + type: customStruct + ) XCTAssertEqual(transformation.letStatement, "let p = SomeStruct(json: json, key: \"a\")") } - + func test_array_of_required_struct() { let someStruct = createStruct(named: "SomeStruct") - let transformation = TransformationFromJSON.toCustomStructArray(attributeName: "a", propertyName: "p", elementType: someStruct, hasOptionalElements: false) - XCTAssertEqual(transformation.letStatement, "let p = SomeStruct.createRequiredInstances(from: json, arrayKey: \"a\")") + let transformation = TransformationFromJSON.toCustomStructArray( + attributeName: "a", + propertyName: "p", + elementType: someStruct, + hasOptionalElements: false + ) + XCTAssertEqual( + transformation.letStatement, + "let p = SomeStruct.createRequiredInstances(from: json, arrayKey: \"a\")" + ) } - + func test_array_of_optional_struct() { let someStruct = createStruct(named: "SomeStruct") - let transformation = TransformationFromJSON.toCustomStructArray(attributeName: "a", propertyName: "p", elementType: someStruct, hasOptionalElements: true) - XCTAssertEqual(transformation.letStatement, "let p = SomeStruct.createOptionalInstances(from: json, arrayKey: \"a\")") + let transformation = TransformationFromJSON.toCustomStructArray( + attributeName: "a", + propertyName: "p", + elementType: someStruct, + hasOptionalElements: true + ) + XCTAssertEqual( + transformation.letStatement, + "let p = SomeStruct.createOptionalInstances(from: json, arrayKey: \"a\")" + ) } - + + // MARK: Private + private func createStruct(named name: String) -> SwiftStruct { let initializer = SwiftInitializer(parameters: []) let failableInitializer = SwiftFailableInitializer(requiredTransformations: [], optionalTransformations: []) - return SwiftStruct(name: name, - properties: [], - initializer: initializer, - failableInitializer: failableInitializer, - nestedStructs: []) + return SwiftStruct( + name: name, + properties: [], + initializer: initializer, + failableInitializer: failableInitializer, + nestedStructs: [] + ) } } diff --git a/Tests/json2swiftTests/swift-code-generation-primitive-array-tests.swift b/Tests/json2swiftTests/swift-code-generation-primitive-array-tests.swift new file mode 100644 index 0000000..e42e910 --- /dev/null +++ b/Tests/json2swiftTests/swift-code-generation-primitive-array-tests.swift @@ -0,0 +1,200 @@ +// +// swift-code-generation-primitive-array-tests.swift +// json2swift +// +// Created by Joshua Smith on 10/21/16. +// Copyright © 2016 iJoshSmith. All rights reserved. +// + +@testable import libJson2Swift +import XCTest + +class swift_code_generation_primitive_array_tests: XCTestCase { + // MARK: - Array with required primitive values + + func test_array_of_required_any() { + let transformation = TransformationFromJSON.toPrimitiveValueArray( + attributeName: "a", + propertyName: "p", + elementType: .any, + hasOptionalElements: false + ) + XCTAssertEqual(transformation.letStatement, "let p = json[\"a\"] as? [Any?]") // Any is treated as optional. + } + + func test_array_of_required_bool() { + let transformation = TransformationFromJSON.toPrimitiveValueArray( + attributeName: "a", + propertyName: "p", + elementType: .bool, + hasOptionalElements: false + ) + XCTAssertEqual(transformation.letStatement, "let p = json[\"a\"] as? [Bool]") + } + + func test_array_of_required_date() { + let transformation = TransformationFromJSON.toPrimitiveValueArray( + attributeName: "a", + propertyName: "p", + elementType: .date(format: "M/d/yyyy"), + hasOptionalElements: false + ) + XCTAssertEqual( + transformation.letStatement, + "let p = (json[\"a\"] as? [String]).flatMap({ $0.toDateArray(withFormat: \"M/d/yyyy\") })" + ) + } + + func test_array_of_required_double() { + let transformation = TransformationFromJSON.toPrimitiveValueArray( + attributeName: "a", + propertyName: "p", + elementType: .double, + hasOptionalElements: false + ) + XCTAssertEqual(transformation.letStatement, "let p = (json[\"a\"] as? [NSNumber]).map({ $0.toDoubleArray() })") + } + + func test_array_of_required_emptyArray() { + let transformation = TransformationFromJSON.toPrimitiveValueArray( + attributeName: "a", + propertyName: "p", + elementType: .emptyArray, + hasOptionalElements: false + ) + XCTAssertEqual(transformation.letStatement, "let p = json[\"a\"] as? [[Any?]]") + } + + func test_array_of_required_int() { + let transformation = TransformationFromJSON.toPrimitiveValueArray( + attributeName: "a", + propertyName: "p", + elementType: .int, + hasOptionalElements: false + ) + XCTAssertEqual(transformation.letStatement, "let p = json[\"a\"] as? [Int]") + } + + func test_array_of_required_string() { + let transformation = TransformationFromJSON.toPrimitiveValueArray( + attributeName: "a", + propertyName: "p", + elementType: .string, + hasOptionalElements: false + ) + XCTAssertEqual(transformation.letStatement, "let p = json[\"a\"] as? [String]") + } + + func test_array_of_required_url() { + let transformation = TransformationFromJSON.toPrimitiveValueArray( + attributeName: "a", + propertyName: "p", + elementType: .url, + hasOptionalElements: false + ) + XCTAssertEqual(transformation.letStatement, "let p = (json[\"a\"] as? [String]).flatMap({ $0.toURLArray() })") + } + + // MARK: - Array with optional primitive values + + func test_array_of_optional_any() { + let transformation = TransformationFromJSON.toPrimitiveValueArray( + attributeName: "a", + propertyName: "p", + elementType: .any, + hasOptionalElements: true + ) + XCTAssertEqual( + transformation.letStatement, + "let p = (json[\"a\"] as? [Any]).map({ $0.toOptionalValueArray() as [Any?] })" + ) + } + + func test_array_of_optional_bool() { + let transformation = TransformationFromJSON.toPrimitiveValueArray( + attributeName: "a", + propertyName: "p", + elementType: .bool, + hasOptionalElements: true + ) + XCTAssertEqual( + transformation.letStatement, + "let p = (json[\"a\"] as? [Any]).map({ $0.toOptionalValueArray() as [Bool?] })" + ) + } + + func test_array_of_optional_date() { + let transformation = TransformationFromJSON.toPrimitiveValueArray( + attributeName: "a", + propertyName: "p", + elementType: .date(format: "M/d/yyyy"), + hasOptionalElements: true + ) + XCTAssertEqual( + transformation.letStatement, + "let p = (json[\"a\"] as? [Any]).map({ $0.toOptionalDateArray(withFormat: \"M/d/yyyy\") })" + ) + } + + func test_array_of_optional_double() { + let transformation = TransformationFromJSON.toPrimitiveValueArray( + attributeName: "a", + propertyName: "p", + elementType: .double, + hasOptionalElements: true + ) + XCTAssertEqual( + transformation.letStatement, + "let p = (json[\"a\"] as? [Any]).map({ $0.toOptionalDoubleArray() })" + ) + } + + func test_array_of_optional_emptyArray() { + let transformation = TransformationFromJSON.toPrimitiveValueArray( + attributeName: "a", + propertyName: "p", + elementType: .emptyArray, + hasOptionalElements: true + ) + XCTAssertEqual( + transformation.letStatement, + "let p = (json[\"a\"] as? [Any]).map({ $0.toOptionalValueArray() as [[Any?]?] })" + ) + } + + func test_array_of_optional_int() { + let transformation = TransformationFromJSON.toPrimitiveValueArray( + attributeName: "a", + propertyName: "p", + elementType: .int, + hasOptionalElements: true + ) + XCTAssertEqual( + transformation.letStatement, + "let p = (json[\"a\"] as? [Any]).map({ $0.toOptionalValueArray() as [Int?] })" + ) + } + + func test_array_of_optional_string() { + let transformation = TransformationFromJSON.toPrimitiveValueArray( + attributeName: "a", + propertyName: "p", + elementType: .string, + hasOptionalElements: true + ) + XCTAssertEqual( + transformation.letStatement, + "let p = (json[\"a\"] as? [Any]).map({ $0.toOptionalValueArray() as [String?] })" + ) + } + + func test_array_of_optional_url() { + let transformation = TransformationFromJSON.toPrimitiveValueArray( + attributeName: "a", + propertyName: "p", + elementType: .url, + hasOptionalElements: true + ) + XCTAssertEqual(transformation.letStatement, "let p = (json[\"a\"] as? [Any]).map({ $0.toOptionalURLArray() })") + } +} diff --git a/unit_tests/swift-code-generation-primitive-value-tests.swift b/Tests/json2swiftTests/swift-code-generation-primitive-value-tests.swift similarity index 80% rename from unit_tests/swift-code-generation-primitive-value-tests.swift rename to Tests/json2swiftTests/swift-code-generation-primitive-value-tests.swift index 6de69b0..f8d8ad7 100644 --- a/unit_tests/swift-code-generation-primitive-value-tests.swift +++ b/Tests/json2swiftTests/swift-code-generation-primitive-value-tests.swift @@ -6,6 +6,7 @@ // Copyright © 2016 iJoshSmith. All rights reserved. // +@testable import libJson2Swift import XCTest class swift_code_generation_primitive_value_tests: XCTestCase { @@ -13,37 +14,53 @@ class swift_code_generation_primitive_value_tests: XCTestCase { let transformation = TransformationFromJSON.toPrimitiveValue(attributeName: "a", propertyName: "p", type: .any) XCTAssertEqual(transformation.letStatement, "let p = json[\"a\"] as? Any") } - + func test_emptyArray() { - let transformation = TransformationFromJSON.toPrimitiveValue(attributeName: "a", propertyName: "p", type: .emptyArray) + let transformation = TransformationFromJSON.toPrimitiveValue( + attributeName: "a", + propertyName: "p", + type: .emptyArray + ) XCTAssertEqual(transformation.letStatement, "let p = json[\"a\"] as? [Any?]") } - + func test_bool() { let transformation = TransformationFromJSON.toPrimitiveValue(attributeName: "a", propertyName: "p", type: .bool) XCTAssertEqual(transformation.letStatement, "let p = json[\"a\"] as? Bool") } - + func test_date() { - let transformation = TransformationFromJSON.toPrimitiveValue(attributeName: "a", propertyName: "p", type: .date(format: "M/d/yyyy")) + let transformation = TransformationFromJSON.toPrimitiveValue( + attributeName: "a", + propertyName: "p", + type: .date(format: "M/d/yyyy") + ) XCTAssertEqual(transformation.letStatement, "let p = Date(json: json, key: \"a\", format: \"M/d/yyyy\")") } - + func test_double() { - let transformation = TransformationFromJSON.toPrimitiveValue(attributeName: "a", propertyName: "p", type: .double) + let transformation = TransformationFromJSON.toPrimitiveValue( + attributeName: "a", + propertyName: "p", + type: .double + ) XCTAssertEqual(transformation.letStatement, "let p = Double(json: json, key: \"a\")") } - + func test_int() { let transformation = TransformationFromJSON.toPrimitiveValue(attributeName: "a", propertyName: "p", type: .int) XCTAssertEqual(transformation.letStatement, "let p = json[\"a\"] as? Int") } - + func test_string() { - let transformation = TransformationFromJSON.toPrimitiveValue(attributeName: "a", propertyName: "p", type: .string) + let transformation = TransformationFromJSON.toPrimitiveValue( + attributeName: "a", + propertyName: "p", + type: .string + ) XCTAssertEqual(transformation.letStatement, "let p = json[\"a\"] as? String") } - + func test_url() { let transformation = TransformationFromJSON.toPrimitiveValue(attributeName: "a", propertyName: "p", type: .url) XCTAssertEqual(transformation.letStatement, "let p = URL(json: json, key: \"a\")") diff --git a/json2swift.xcodeproj/project.pbxproj b/json2swift.xcodeproj/project.pbxproj deleted file mode 100644 index f2889fc..0000000 --- a/json2swift.xcodeproj/project.pbxproj +++ /dev/null @@ -1,528 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 46; - objects = { - -/* Begin PBXBuildFile section */ - 032199E01DBC1B2200BF8272 /* schema-to-struct-translation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 032199DF1DBC1B2200BF8272 /* schema-to-struct-translation.swift */; }; - 032199E11DBC1B2200BF8272 /* schema-to-struct-translation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 032199DF1DBC1B2200BF8272 /* schema-to-struct-translation.swift */; }; - 032199E31DBC266900BF8272 /* name-translation-tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 032199E21DBC266900BF8272 /* name-translation-tests.swift */; }; - 03237E031DC58A7B00ECCEF9 /* json-attribute-merging-empty-array-tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03237E021DC58A7B00ECCEF9 /* json-attribute-merging-empty-array-tests.swift */; }; - 03524D651DC27A5A0002E0CD /* name-translation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03524D641DC27A5A0002E0CD /* name-translation.swift */; }; - 03524D661DC27A5A0002E0CD /* name-translation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03524D641DC27A5A0002E0CD /* name-translation.swift */; }; - 036BF2D31DB14C630004C4FC /* swift-code-generation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 036BF2D21DB14C630004C4FC /* swift-code-generation.swift */; }; - 036BF2D41DB14C630004C4FC /* swift-code-generation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 036BF2D21DB14C630004C4FC /* swift-code-generation.swift */; }; - 036BF2D61DB14CA50004C4FC /* swift-data-model.swift in Sources */ = {isa = PBXBuildFile; fileRef = 036BF2D51DB14CA50004C4FC /* swift-data-model.swift */; }; - 036BF2D71DB14CA50004C4FC /* swift-data-model.swift in Sources */ = {isa = PBXBuildFile; fileRef = 036BF2D51DB14CA50004C4FC /* swift-data-model.swift */; }; - 03720E461DB177D600BCFE72 /* json-attribute-merging-value-array-tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03720E451DB177D600BCFE72 /* json-attribute-merging-value-array-tests.swift */; }; - 03720E481DB17FE400BCFE72 /* JSONType+JSONType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03720E471DB17FE400BCFE72 /* JSONType+JSONType.swift */; }; - 03720E4A1DB18E3D00BCFE72 /* json-schema-inference-value-array-tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03720E491DB18E3D00BCFE72 /* json-schema-inference-value-array-tests.swift */; }; - 0392CC791DBA7AFB00289A67 /* swift-code-generation-primitive-value-tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0392CC781DBA7AFB00289A67 /* swift-code-generation-primitive-value-tests.swift */; }; - 0392CC7B1DBA7E7D00289A67 /* swift-code-generation-custom-struct-tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0392CC7A1DBA7E7D00289A67 /* swift-code-generation-custom-struct-tests.swift */; }; - 03A195831DAFEA7A00481B1F /* json-schema-inference-element-array-tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03A195821DAFEA7A00481B1F /* json-schema-inference-element-array-tests.swift */; }; - 03B0D0C51DAFCD7C00D20E31 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03B0D0C41DAFCD7C00D20E31 /* main.swift */; }; - 03B0D0DA1DAFCDD000D20E31 /* json-attribute-merging.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03B0D0D71DAFCDD000D20E31 /* json-attribute-merging.swift */; }; - 03B0D0DB1DAFCDD000D20E31 /* json-data-model.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03B0D0D81DAFCDD000D20E31 /* json-data-model.swift */; }; - 03B0D0DC1DAFCDD000D20E31 /* json-schema-inference.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03B0D0D91DAFCDD000D20E31 /* json-schema-inference.swift */; }; - 03B0D0DD1DAFCDD900D20E31 /* json-attribute-merging.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03B0D0D71DAFCDD000D20E31 /* json-attribute-merging.swift */; }; - 03B0D0DE1DAFCDD900D20E31 /* json-data-model.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03B0D0D81DAFCDD000D20E31 /* json-data-model.swift */; }; - 03B0D0DF1DAFCDD900D20E31 /* json-schema-inference.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03B0D0D91DAFCDD000D20E31 /* json-schema-inference.swift */; }; - 03B0D0E61DAFCDF700D20E31 /* json-attribute-merging-element-array-tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03B0D0E01DAFCDF700D20E31 /* json-attribute-merging-element-array-tests.swift */; }; - 03B0D0E71DAFCDF700D20E31 /* json-attribute-merging-boolean-tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03B0D0E11DAFCDF700D20E31 /* json-attribute-merging-boolean-tests.swift */; }; - 03B0D0E81DAFCDF700D20E31 /* json-attribute-merging-element-tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03B0D0E21DAFCDF700D20E31 /* json-attribute-merging-element-tests.swift */; }; - 03B0D0E91DAFCDF700D20E31 /* json-attribute-merging-numeric-tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03B0D0E31DAFCDF700D20E31 /* json-attribute-merging-numeric-tests.swift */; }; - 03B0D0EA1DAFCDF700D20E31 /* json-attribute-merging-text-tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03B0D0E41DAFCDF700D20E31 /* json-attribute-merging-text-tests.swift */; }; - 03B0D0EB1DAFCDF700D20E31 /* json-schema-inference-element-tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03B0D0E51DAFCDF700D20E31 /* json-schema-inference-element-tests.swift */; }; - 03B52FD71DC3FD9D00910D1B /* command-line-interface.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03B52FD61DC3FD9D00910D1B /* command-line-interface.swift */; }; - 03B52FD81DC3FD9D00910D1B /* command-line-interface.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03B52FD61DC3FD9D00910D1B /* command-line-interface.swift */; }; - 03B89CA81DBAC00B007B9776 /* swift-code-generation-primitive-array-tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03B89CA71DBAC00B007B9776 /* swift-code-generation-primitive-array-tests.swift */; }; - 03C8303E1DC3B98500999D21 /* failable-initializer-translation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03C8303D1DC3B98500999D21 /* failable-initializer-translation.swift */; }; - 03C8303F1DC3B98500999D21 /* failable-initializer-translation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03C8303D1DC3B98500999D21 /* failable-initializer-translation.swift */; }; - 03D815671DB83BDA00B88564 /* swift-code-templates.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03D815661DB83BDA00B88564 /* swift-code-templates.swift */; }; - 03D815681DB83BDA00B88564 /* swift-code-templates.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03D815661DB83BDA00B88564 /* swift-code-templates.swift */; }; - 03DCE0AA1DB086C10087787F /* json-helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03DCE0A91DB086C10087787F /* json-helpers.swift */; }; - 03DCE0AB1DB086C10087787F /* json-helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03DCE0A91DB086C10087787F /* json-helpers.swift */; }; -/* End PBXBuildFile section */ - -/* Begin PBXCopyFilesBuildPhase section */ - 03B0D0BF1DAFCD7C00D20E31 /* CopyFiles */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = /usr/share/man/man1/; - dstSubfolderSpec = 0; - files = ( - ); - runOnlyForDeploymentPostprocessing = 1; - }; -/* End PBXCopyFilesBuildPhase section */ - -/* Begin PBXFileReference section */ - 032199DF1DBC1B2200BF8272 /* schema-to-struct-translation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "schema-to-struct-translation.swift"; sourceTree = ""; }; - 032199E21DBC266900BF8272 /* name-translation-tests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "name-translation-tests.swift"; sourceTree = ""; }; - 03237E021DC58A7B00ECCEF9 /* json-attribute-merging-empty-array-tests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "json-attribute-merging-empty-array-tests.swift"; sourceTree = ""; }; - 03524D641DC27A5A0002E0CD /* name-translation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "name-translation.swift"; sourceTree = ""; }; - 036BF2D21DB14C630004C4FC /* swift-code-generation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "swift-code-generation.swift"; sourceTree = ""; }; - 036BF2D51DB14CA50004C4FC /* swift-data-model.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "swift-data-model.swift"; sourceTree = ""; }; - 03720E451DB177D600BCFE72 /* json-attribute-merging-value-array-tests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "json-attribute-merging-value-array-tests.swift"; sourceTree = ""; }; - 03720E471DB17FE400BCFE72 /* JSONType+JSONType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "JSONType+JSONType.swift"; sourceTree = ""; }; - 03720E491DB18E3D00BCFE72 /* json-schema-inference-value-array-tests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "json-schema-inference-value-array-tests.swift"; sourceTree = ""; }; - 0392CC781DBA7AFB00289A67 /* swift-code-generation-primitive-value-tests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "swift-code-generation-primitive-value-tests.swift"; sourceTree = ""; }; - 0392CC7A1DBA7E7D00289A67 /* swift-code-generation-custom-struct-tests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "swift-code-generation-custom-struct-tests.swift"; sourceTree = ""; }; - 03A195821DAFEA7A00481B1F /* json-schema-inference-element-array-tests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = "json-schema-inference-element-array-tests.swift"; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; - 03B0D0C11DAFCD7C00D20E31 /* json2swift */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = json2swift; sourceTree = BUILT_PRODUCTS_DIR; }; - 03B0D0C41DAFCD7C00D20E31 /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = main.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; - 03B0D0CF1DAFCD9C00D20E31 /* unit_tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = unit_tests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - 03B0D0D31DAFCD9C00D20E31 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 03B0D0D71DAFCDD000D20E31 /* json-attribute-merging.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = "json-attribute-merging.swift"; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; - 03B0D0D81DAFCDD000D20E31 /* json-data-model.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = "json-data-model.swift"; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; - 03B0D0D91DAFCDD000D20E31 /* json-schema-inference.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = "json-schema-inference.swift"; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; - 03B0D0E01DAFCDF700D20E31 /* json-attribute-merging-element-array-tests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = "json-attribute-merging-element-array-tests.swift"; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; - 03B0D0E11DAFCDF700D20E31 /* json-attribute-merging-boolean-tests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = "json-attribute-merging-boolean-tests.swift"; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; - 03B0D0E21DAFCDF700D20E31 /* json-attribute-merging-element-tests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = "json-attribute-merging-element-tests.swift"; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; - 03B0D0E31DAFCDF700D20E31 /* json-attribute-merging-numeric-tests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = "json-attribute-merging-numeric-tests.swift"; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; - 03B0D0E41DAFCDF700D20E31 /* json-attribute-merging-text-tests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = "json-attribute-merging-text-tests.swift"; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; - 03B0D0E51DAFCDF700D20E31 /* json-schema-inference-element-tests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = "json-schema-inference-element-tests.swift"; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; - 03B52FD61DC3FD9D00910D1B /* command-line-interface.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "command-line-interface.swift"; sourceTree = ""; }; - 03B89CA71DBAC00B007B9776 /* swift-code-generation-primitive-array-tests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "swift-code-generation-primitive-array-tests.swift"; sourceTree = ""; }; - 03C8303D1DC3B98500999D21 /* failable-initializer-translation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "failable-initializer-translation.swift"; sourceTree = ""; }; - 03D815661DB83BDA00B88564 /* swift-code-templates.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "swift-code-templates.swift"; sourceTree = ""; }; - 03DCE0A91DB086C10087787F /* json-helpers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = "json-helpers.swift"; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 03B0D0BE1DAFCD7C00D20E31 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 03B0D0CC1DAFCD9C00D20E31 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 0328715A1DC53BF6002E4584 /* JSON Data --> JSON Schema */ = { - isa = PBXGroup; - children = ( - 03DCE0A91DB086C10087787F /* json-helpers.swift */, - 03B0D0D81DAFCDD000D20E31 /* json-data-model.swift */, - 03B0D0D71DAFCDD000D20E31 /* json-attribute-merging.swift */, - 03B0D0D91DAFCDD000D20E31 /* json-schema-inference.swift */, - ); - name = "JSON Data --> JSON Schema"; - sourceTree = ""; - }; - 0328715B1DC53C30002E4584 /* Swift Model --> Swift Source Code */ = { - isa = PBXGroup; - children = ( - 036BF2D51DB14CA50004C4FC /* swift-data-model.swift */, - 03D815661DB83BDA00B88564 /* swift-code-templates.swift */, - 036BF2D21DB14C630004C4FC /* swift-code-generation.swift */, - ); - name = "Swift Model --> Swift Source Code"; - sourceTree = ""; - }; - 0328715C1DC53C3F002E4584 /* JSON Schema --> Swift Model */ = { - isa = PBXGroup; - children = ( - 03524D641DC27A5A0002E0CD /* name-translation.swift */, - 03C8303D1DC3B98500999D21 /* failable-initializer-translation.swift */, - 032199DF1DBC1B2200BF8272 /* schema-to-struct-translation.swift */, - ); - name = "JSON Schema --> Swift Model"; - sourceTree = ""; - }; - 03B0D0B81DAFCD7C00D20E31 = { - isa = PBXGroup; - children = ( - 03B0D0C31DAFCD7C00D20E31 /* json2swift */, - 03B0D0D01DAFCD9C00D20E31 /* unit_tests */, - 03B0D0C21DAFCD7C00D20E31 /* products */, - ); - sourceTree = ""; - }; - 03B0D0C21DAFCD7C00D20E31 /* products */ = { - isa = PBXGroup; - children = ( - 03B0D0C11DAFCD7C00D20E31 /* json2swift */, - 03B0D0CF1DAFCD9C00D20E31 /* unit_tests.xctest */, - ); - name = products; - sourceTree = ""; - }; - 03B0D0C31DAFCD7C00D20E31 /* json2swift */ = { - isa = PBXGroup; - children = ( - 0328715A1DC53BF6002E4584 /* JSON Data --> JSON Schema */, - 0328715C1DC53C3F002E4584 /* JSON Schema --> Swift Model */, - 0328715B1DC53C30002E4584 /* Swift Model --> Swift Source Code */, - 03B52FD61DC3FD9D00910D1B /* command-line-interface.swift */, - 03B0D0C41DAFCD7C00D20E31 /* main.swift */, - ); - path = json2swift; - sourceTree = ""; - }; - 03B0D0D01DAFCD9C00D20E31 /* unit_tests */ = { - isa = PBXGroup; - children = ( - 03B0D0D31DAFCD9C00D20E31 /* Info.plist */, - 03B0D0E11DAFCDF700D20E31 /* json-attribute-merging-boolean-tests.swift */, - 03B0D0E01DAFCDF700D20E31 /* json-attribute-merging-element-array-tests.swift */, - 03B0D0E21DAFCDF700D20E31 /* json-attribute-merging-element-tests.swift */, - 03237E021DC58A7B00ECCEF9 /* json-attribute-merging-empty-array-tests.swift */, - 03B0D0E31DAFCDF700D20E31 /* json-attribute-merging-numeric-tests.swift */, - 03B0D0E41DAFCDF700D20E31 /* json-attribute-merging-text-tests.swift */, - 03720E451DB177D600BCFE72 /* json-attribute-merging-value-array-tests.swift */, - 03A195821DAFEA7A00481B1F /* json-schema-inference-element-array-tests.swift */, - 03B0D0E51DAFCDF700D20E31 /* json-schema-inference-element-tests.swift */, - 03720E491DB18E3D00BCFE72 /* json-schema-inference-value-array-tests.swift */, - 03720E471DB17FE400BCFE72 /* JSONType+JSONType.swift */, - 032199E21DBC266900BF8272 /* name-translation-tests.swift */, - 0392CC7A1DBA7E7D00289A67 /* swift-code-generation-custom-struct-tests.swift */, - 03B89CA71DBAC00B007B9776 /* swift-code-generation-primitive-array-tests.swift */, - 0392CC781DBA7AFB00289A67 /* swift-code-generation-primitive-value-tests.swift */, - ); - path = unit_tests; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - 03B0D0C01DAFCD7C00D20E31 /* json2swift */ = { - isa = PBXNativeTarget; - buildConfigurationList = 03B0D0C81DAFCD7C00D20E31 /* Build configuration list for PBXNativeTarget "json2swift" */; - buildPhases = ( - 03B0D0BD1DAFCD7C00D20E31 /* Sources */, - 03B0D0BE1DAFCD7C00D20E31 /* Frameworks */, - 03B0D0BF1DAFCD7C00D20E31 /* CopyFiles */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = json2swift; - productName = json2swift; - productReference = 03B0D0C11DAFCD7C00D20E31 /* json2swift */; - productType = "com.apple.product-type.tool"; - }; - 03B0D0CE1DAFCD9C00D20E31 /* unit_tests */ = { - isa = PBXNativeTarget; - buildConfigurationList = 03B0D0D41DAFCD9C00D20E31 /* Build configuration list for PBXNativeTarget "unit_tests" */; - buildPhases = ( - 03B0D0CB1DAFCD9C00D20E31 /* Sources */, - 03B0D0CC1DAFCD9C00D20E31 /* Frameworks */, - 03B0D0CD1DAFCD9C00D20E31 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = unit_tests; - productName = unit_tests; - productReference = 03B0D0CF1DAFCD9C00D20E31 /* unit_tests.xctest */; - productType = "com.apple.product-type.bundle.unit-test"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 03B0D0B91DAFCD7C00D20E31 /* Project object */ = { - isa = PBXProject; - attributes = { - LastSwiftUpdateCheck = 0800; - LastUpgradeCheck = 1000; - ORGANIZATIONNAME = iJoshSmith; - TargetAttributes = { - 03B0D0C01DAFCD7C00D20E31 = { - CreatedOnToolsVersion = 8.0; - LastSwiftMigration = 1000; - ProvisioningStyle = Automatic; - }; - 03B0D0CE1DAFCD9C00D20E31 = { - CreatedOnToolsVersion = 8.0; - LastSwiftMigration = 1000; - ProvisioningStyle = Automatic; - }; - }; - }; - buildConfigurationList = 03B0D0BC1DAFCD7C00D20E31 /* Build configuration list for PBXProject "json2swift" */; - compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; - hasScannedForEncodings = 0; - knownRegions = ( - en, - ); - mainGroup = 03B0D0B81DAFCD7C00D20E31; - productRefGroup = 03B0D0C21DAFCD7C00D20E31 /* products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - 03B0D0C01DAFCD7C00D20E31 /* json2swift */, - 03B0D0CE1DAFCD9C00D20E31 /* unit_tests */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - 03B0D0CD1DAFCD9C00D20E31 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 03B0D0BD1DAFCD7C00D20E31 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 036BF2D61DB14CA50004C4FC /* swift-data-model.swift in Sources */, - 03B52FD71DC3FD9D00910D1B /* command-line-interface.swift in Sources */, - 03B0D0DA1DAFCDD000D20E31 /* json-attribute-merging.swift in Sources */, - 03DCE0AA1DB086C10087787F /* json-helpers.swift in Sources */, - 03B0D0C51DAFCD7C00D20E31 /* main.swift in Sources */, - 03B0D0DC1DAFCDD000D20E31 /* json-schema-inference.swift in Sources */, - 036BF2D31DB14C630004C4FC /* swift-code-generation.swift in Sources */, - 03D815671DB83BDA00B88564 /* swift-code-templates.swift in Sources */, - 03B0D0DB1DAFCDD000D20E31 /* json-data-model.swift in Sources */, - 03524D651DC27A5A0002E0CD /* name-translation.swift in Sources */, - 03C8303E1DC3B98500999D21 /* failable-initializer-translation.swift in Sources */, - 032199E01DBC1B2200BF8272 /* schema-to-struct-translation.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 03B0D0CB1DAFCD9C00D20E31 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 03B89CA81DBAC00B007B9776 /* swift-code-generation-primitive-array-tests.swift in Sources */, - 03B0D0E61DAFCDF700D20E31 /* json-attribute-merging-element-array-tests.swift in Sources */, - 03B0D0EB1DAFCDF700D20E31 /* json-schema-inference-element-tests.swift in Sources */, - 03720E4A1DB18E3D00BCFE72 /* json-schema-inference-value-array-tests.swift in Sources */, - 03B0D0DD1DAFCDD900D20E31 /* json-attribute-merging.swift in Sources */, - 03720E481DB17FE400BCFE72 /* JSONType+JSONType.swift in Sources */, - 036BF2D71DB14CA50004C4FC /* swift-data-model.swift in Sources */, - 03B0D0DF1DAFCDD900D20E31 /* json-schema-inference.swift in Sources */, - 03B52FD81DC3FD9D00910D1B /* command-line-interface.swift in Sources */, - 032199E11DBC1B2200BF8272 /* schema-to-struct-translation.swift in Sources */, - 036BF2D41DB14C630004C4FC /* swift-code-generation.swift in Sources */, - 03B0D0E91DAFCDF700D20E31 /* json-attribute-merging-numeric-tests.swift in Sources */, - 032199E31DBC266900BF8272 /* name-translation-tests.swift in Sources */, - 03D815681DB83BDA00B88564 /* swift-code-templates.swift in Sources */, - 03DCE0AB1DB086C10087787F /* json-helpers.swift in Sources */, - 03B0D0E71DAFCDF700D20E31 /* json-attribute-merging-boolean-tests.swift in Sources */, - 03524D661DC27A5A0002E0CD /* name-translation.swift in Sources */, - 0392CC791DBA7AFB00289A67 /* swift-code-generation-primitive-value-tests.swift in Sources */, - 03B0D0EA1DAFCDF700D20E31 /* json-attribute-merging-text-tests.swift in Sources */, - 03C8303F1DC3B98500999D21 /* failable-initializer-translation.swift in Sources */, - 03B0D0DE1DAFCDD900D20E31 /* json-data-model.swift in Sources */, - 03A195831DAFEA7A00481B1F /* json-schema-inference-element-array-tests.swift in Sources */, - 03720E461DB177D600BCFE72 /* json-attribute-merging-value-array-tests.swift in Sources */, - 03B0D0E81DAFCDF700D20E31 /* json-attribute-merging-element-tests.swift in Sources */, - 03237E031DC58A7B00ECCEF9 /* json-attribute-merging-empty-array-tests.swift in Sources */, - 0392CC7B1DBA7E7D00289A67 /* swift-code-generation-custom-struct-tests.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin XCBuildConfiguration section */ - 03B0D0C61DAFCD7C00D20E31 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_SUSPICIOUS_MOVES = YES; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - CODE_SIGN_IDENTITY = "-"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = dwarf; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.11; - MTL_ENABLE_DEBUG_INFO = YES; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = macosx; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - }; - name = Debug; - }; - 03B0D0C71DAFCD7C00D20E31 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_SUSPICIOUS_MOVES = YES; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - CODE_SIGN_IDENTITY = "-"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.11; - MTL_ENABLE_DEBUG_INFO = NO; - ONLY_ACTIVE_ARCH = NO; - SDKROOT = macosx; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; - }; - name = Release; - }; - 03B0D0C91DAFCD7C00D20E31 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)"; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_SWIFT3_OBJC_INFERENCE = Default; - SWIFT_VERSION = 4.2; - }; - name = Debug; - }; - 03B0D0CA1DAFCD7C00D20E31 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_SWIFT3_OBJC_INFERENCE = Default; - SWIFT_VERSION = 4.2; - }; - name = Release; - }; - 03B0D0D51DAFCD9C00D20E31 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - COMBINE_HIDPI_IMAGES = YES; - INFOPLIST_FILE = unit_tests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = "iJoshSmith.unit-tests"; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; - SWIFT_SWIFT3_OBJC_INFERENCE = Default; - SWIFT_VERSION = 4.2; - }; - name = Debug; - }; - 03B0D0D61DAFCD9C00D20E31 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - COMBINE_HIDPI_IMAGES = YES; - INFOPLIST_FILE = unit_tests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = "iJoshSmith.unit-tests"; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; - SWIFT_SWIFT3_OBJC_INFERENCE = Default; - SWIFT_VERSION = 4.2; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 03B0D0BC1DAFCD7C00D20E31 /* Build configuration list for PBXProject "json2swift" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 03B0D0C61DAFCD7C00D20E31 /* Debug */, - 03B0D0C71DAFCD7C00D20E31 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 03B0D0C81DAFCD7C00D20E31 /* Build configuration list for PBXNativeTarget "json2swift" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 03B0D0C91DAFCD7C00D20E31 /* Debug */, - 03B0D0CA1DAFCD7C00D20E31 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 03B0D0D41DAFCD9C00D20E31 /* Build configuration list for PBXNativeTarget "unit_tests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 03B0D0D51DAFCD9C00D20E31 /* Debug */, - 03B0D0D61DAFCD9C00D20E31 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = 03B0D0B91DAFCD7C00D20E31 /* Project object */; -} diff --git a/json2swift.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/json2swift.xcodeproj/project.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 4ad0e92..0000000 --- a/json2swift.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,7 +0,0 @@ - - - - - diff --git a/json2swift.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/json2swift.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist deleted file mode 100644 index 18d9810..0000000 --- a/json2swift.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEDidComputeMac32BitWarning - - - diff --git a/json2swift/failable-initializer-translation.swift b/json2swift/failable-initializer-translation.swift deleted file mode 100644 index 423cd94..0000000 --- a/json2swift/failable-initializer-translation.swift +++ /dev/null @@ -1,80 +0,0 @@ -// -// failable-initializer-translation.swift -// json2swift -// -// Created by Joshua Smith on 10/28/16. -// Copyright © 2016 iJoshSmith. All rights reserved. -// - -// MARK: - JSONElementSchema --> SwiftFailableInitializer -internal extension SwiftFailableInitializer { - static func create(forStructBasedOn jsonElementSchema: JSONElementSchema) -> SwiftFailableInitializer { - let attributeMap = jsonElementSchema.attributes - let allAttributeNames = Set(attributeMap.keys) - let requiredAttributeNames = allAttributeNames.filter { attributeMap[$0]!.isRequired } - let optionalAttributeNames = allAttributeNames.subtracting(requiredAttributeNames) - let requiredTransformations: [TransformationFromJSON] = requiredAttributeNames.map { - TransformationFromJSON.create(forAttributeNamed: $0, inAttributeMap: attributeMap) - } - let optionalTransformations: [TransformationFromJSON] = optionalAttributeNames.map { - TransformationFromJSON.create(forAttributeNamed: $0, inAttributeMap: attributeMap) - } - return SwiftFailableInitializer(requiredTransformations: requiredTransformations, - optionalTransformations: optionalTransformations) - } -} - -// MARK: - JSON attribute --> TransformationFromJSON -fileprivate extension TransformationFromJSON { - static func create(forAttributeNamed attributeName: String, inAttributeMap attributeMap: JSONAttributeMap) -> TransformationFromJSON { - let propertyName = attributeName.toSwiftPropertyName() - let jsonType = attributeMap[attributeName]! - switch jsonType { - case let .element(_, schema): - let swiftStruct = SwiftStruct.create(from: schema) - return TransformationFromJSON.toCustomStruct(attributeName: attributeName, - propertyName: propertyName, - type: swiftStruct) - - case let .elementArray(_, elementSchema, hasNullableElements): - let swiftStruct = SwiftStruct.create(from: elementSchema) - return TransformationFromJSON.toCustomStructArray(attributeName: attributeName, - propertyName: propertyName, - elementType: swiftStruct, - hasOptionalElements: hasNullableElements) - - case let .valueArray(_, valueType): - let elementType = SwiftPrimitiveValueType.create(from: valueType) - let hasOptionalElements = valueType.isRequired == false - return TransformationFromJSON.toPrimitiveValueArray(attributeName: attributeName, - propertyName: propertyName, - elementType: elementType, - hasOptionalElements: hasOptionalElements) - - default: - let valueType = SwiftPrimitiveValueType.create(from: jsonType) - return TransformationFromJSON.toPrimitiveValue(attributeName: attributeName, - propertyName: propertyName, - type: valueType) - } - } -} - -// MARK: - JSONType --> SwiftPrimitiveValueType -fileprivate extension SwiftPrimitiveValueType { - static func create(from jsonType: JSONType) -> SwiftPrimitiveValueType { - switch jsonType { - case .anything, - .element, - .elementArray, - .nullable, - .valueArray: return .any - case .emptyArray: return .emptyArray - case .number(_, let isFloatingPoint): return isFloatingPoint ? .double : .int - case .date(_, let format): return .date(format: format) - case .url: return .url - case .string: return .string - case .bool: return .bool - } - } -} diff --git a/json2swift/json-attribute-merging.swift b/json2swift/json-attribute-merging.swift deleted file mode 100644 index 2f37cdd..0000000 --- a/json2swift/json-attribute-merging.swift +++ /dev/null @@ -1,104 +0,0 @@ -// -// json-attribute-merging.swift -// json2swift -// -// Created by Joshua Smith on 10/11/16. -// Copyright © 2016 iJoshSmith. All rights reserved. -// - -// MARK: - JSONElementSchema extension -extension JSONElementSchema { - static func inferredByMergingAttributes(of schemas: [JSONElementSchema], named name: String) -> JSONElementSchema { - switch schemas.count { - case 0: return JSONElementSchema(name: name) - case 1: return schemas[0] - default: return schemas.dropFirst().reduce(schemas[0], merge(schema:withSchema:)) - } - } - - private static func merge(schema: JSONElementSchema, withSchema otherSchema: JSONElementSchema) -> JSONElementSchema { - return schema.merged(with: otherSchema) - } - - fileprivate func merged(with schema: JSONElementSchema) -> JSONElementSchema { - let mergedAttributes = JSONType.merge(attributes: attributes, with: schema.attributes) - return JSONElementSchema(name: name, attributes: mergedAttributes) - } -} - -// MARK: - JSONType extension -extension JSONType { - fileprivate static func merge(attributes: JSONAttributeMap, with otherAttributes: JSONAttributeMap) -> JSONAttributeMap { - let attributeNames = Set(Array(attributes.keys) + Array(otherAttributes.keys)) - let mergedAttributes = attributeNames.map { name -> (String, JSONType) in - let type = attributes[name] ?? .nullable - let otherType = otherAttributes[name] ?? .nullable - let compatibleType = type.findCompatibleType(with: otherType) - return (name, compatibleType) - } - return JSONAttributeMap(entries: mergedAttributes) - } - - internal func findCompatibleType(with type: JSONType) -> JSONType { - return JSONType.compatible(with: self, and: type) - } - - private static func compatible(with type1: JSONType, and type2: JSONType) -> JSONType { - switch (type1, type2) { - // Element - case let (.element(r1, s1), .element(r2, s2)): return .element(isRequired: r1 && r2, schema: s1.merged(with: s2)) - case let (.element(_, s), .nullable): return .element(isRequired: false, schema: s) - case let (.nullable, .element(_, s)): return .element(isRequired: false, schema: s) - - // Element Array - case let (.elementArray(r1, s1, n1), .elementArray(r2, s2, n2)): return .elementArray(isRequired: r1 && r2, elementSchema: s1.merged(with: s2), hasNullableElements: n1 || n2) - case let (.elementArray(_, s, n), .nullable): return .elementArray(isRequired: false, elementSchema: s, hasNullableElements: n) - case let (.nullable, .elementArray(_, s, n)): return .elementArray(isRequired: false, elementSchema: s, hasNullableElements: n) - - // Value Array - case let (.valueArray(r1, t1), .valueArray(r2, t2)): return .valueArray(isRequired: r1 && r2, valueType: t1.findCompatibleType(with: t2)) - case let (.valueArray(_, t), .nullable): return .valueArray(isRequired: false, valueType: t) - case let (.nullable, .valueArray(_, t)): return .valueArray(isRequired: false, valueType: t) - - // Numeric - case let (.number(r1, fp1), .number(r2, fp2)): return .number(isRequired: r1 && r2, isFloatingPoint: fp1 || fp2) - case let (.number(_, fp), .nullable): return .number(isRequired: false, isFloatingPoint: fp) - case let (.nullable, .number(_, fp)): return .number(isRequired: false, isFloatingPoint: fp) - - // Text - case let (.date(r1, f), .date(r2, _)): return .date( isRequired: r1 && r2, format: f) - case let (.date(r1, _), .url(r2)): return .string(isRequired: r1 && r2) - case let (.date(r1, f), .string(r2)): return .date( isRequired: r1 && r2, format: f) - case let (.date(_, f), .nullable): return .date( isRequired: false, format: f) - case let (.nullable, .date(_, f)): return .date( isRequired: false, format: f) - case let (.url(r1), .date(r2, _)): return .string(isRequired: r1 && r2) - case let (.url(r1), .url(r2)): return .url( isRequired: r1 && r2) - case let (.url(r1), .string(r2)): return .string(isRequired: r1 && r2) - case (.url, .nullable): return .url( isRequired: false) - case (.nullable, .url): return .url( isRequired: false) - case let (.string(r1), .date(r2, f)): return .date( isRequired: r1 && r2, format: f) - case let (.string(r1), .url(r2)): return .string(isRequired: r1 && r2) - case let (.string(r1), .string(r2)): return .string(isRequired: r1 && r2) - case (.string, .nullable): return .string(isRequired: false) - case (.nullable, .string): return .string(isRequired: false) - - // Boolean - case let (.bool(r1), .bool(r2)): return .bool(isRequired: r1 && r2) - case (.bool, .nullable): return .bool(isRequired: false) - case (.nullable, .bool): return .bool(isRequired: false) - - // Nullable (e.g. both attribute values were null) - case (.nullable, .nullable): return .nullable - - // Empty array - case (.emptyArray, .emptyArray): return .emptyArray - case (.elementArray, .emptyArray): return type1 - case (.valueArray, .emptyArray): return type1 - case (.emptyArray, .elementArray): return type2 - case (.emptyArray, .valueArray): return type2 - - // Unrelated types - default: return .anything - } - } -} diff --git a/json2swift/json-data-model.swift b/json2swift/json-data-model.swift deleted file mode 100644 index 0a59236..0000000 --- a/json2swift/json-data-model.swift +++ /dev/null @@ -1,91 +0,0 @@ -// -// json-data-model.swift -// json2swift -// -// Created by Joshua Smith on 10/10/16. -// Copyright © 2016 iJoshSmith. All rights reserved. -// - -/// The prefix to use on a sample JSON attribute whose value is a date string. -/// This provides a hint to the JSON analyzer that the corresponding property is a Date. -/// For example, use "DATE_FORMAT=MM/dd/yyyy" for an attribute whose values are like "12/25/2016". -let dateFormatPrefix = "DATE_FORMAT=" - -typealias JSONValue = Any -typealias JSONElement = [String: JSONValue] - -// MARK: - JSONType -indirect enum JSONType { - case element( isRequired: Bool, schema: JSONElementSchema) - case elementArray(isRequired: Bool, elementSchema: JSONElementSchema, hasNullableElements: Bool) - case valueArray( isRequired: Bool, valueType: JSONType) - case number( isRequired: Bool, isFloatingPoint: Bool) - case date( isRequired: Bool, format: String) - case url( isRequired: Bool) - case string( isRequired: Bool) - case bool( isRequired: Bool) - case nullable // For an attribute that is missing or has a null value. - case anything // For an attribute with multiple values of unrelated types. - case emptyArray // For an attribute that contains an empty array. -} - -extension JSONType { - var isRequired: Bool { - switch self { - case let .element (isRequired, _): return isRequired - case let .elementArray(isRequired, _, _): return isRequired - case let .valueArray (isRequired, _): return isRequired - case let .number (isRequired, _): return isRequired - case let .date (isRequired, _): return isRequired - case let .url (isRequired): return isRequired - case let .string (isRequired): return isRequired - case let .bool (isRequired): return isRequired - case .nullable, .anything: return false - case .emptyArray: return true - } - } - - var jsonElementSchema: JSONElementSchema? { - switch self { - case let .element(_, schema): return schema - case let .elementArray(_, elementSchema, _): return elementSchema - default: return nil - } - } -} - -extension JSONType: Equatable { - static func ==(lhs: JSONType, rhs: JSONType) -> Bool { - switch (lhs, rhs) { - case let (.element(r1, a), .element(r2, b)): return r1 == r2 && a == b - case let (.elementArray(r1, a, n1), .elementArray(r2, b, n2)): return r1 == r2 && a == b && n1 == n2 - case let (.valueArray(r1, a), .valueArray(r2, b)): return r1 == r2 && a == b - case let (.number(r1, a), .number(r2, b)): return r1 == r2 && a == b - case let (.date(r1, a), .date(r2, b)): return r1 == r2 && a == b - case let (.url(r1), .url(r2)): return r1 == r2 - case let (.string(r1), .string(r2)): return r1 == r2 - case let (.bool(r1), .bool(r2)): return r1 == r2 - case (.nullable, .nullable): return true - case (.anything, .anything): return true - case (.emptyArray, .emptyArray): return true - default: return false - } - } -} - -// MARK: - JSONElementSchema -typealias JSONAttributeMap = [String: JSONType] -struct JSONElementSchema { - let name: String - let attributes: JSONAttributeMap - - init(name: String, attributes: JSONAttributeMap = [:]) { - self.name = name - self.attributes = attributes - } -} - -extension JSONElementSchema: Equatable {} -func ==(lhs: JSONElementSchema, rhs: JSONElementSchema) -> Bool { - return lhs.name == rhs.name && lhs.attributes == rhs.attributes -} diff --git a/json2swift/schema-to-struct-translation.swift b/json2swift/schema-to-struct-translation.swift deleted file mode 100644 index f6959ed..0000000 --- a/json2swift/schema-to-struct-translation.swift +++ /dev/null @@ -1,101 +0,0 @@ -// -// schema-to-struct-translation.swift -// json2swift -// -// Created by Joshua Smith on 10/22/16. -// Copyright © 2016 iJoshSmith. All rights reserved. -// - -// MARK: - JSONElementSchema --> SwiftStruct -internal extension SwiftStruct { - static func create(from jsonElementSchema: JSONElementSchema) -> SwiftStruct { - let name = jsonElementSchema.name.toSwiftStructName() - let properties = SwiftProperty.createProperties(forStructBasedOn: jsonElementSchema) - let parameters = SwiftParameter.createParameters(for: properties) - let initializer = SwiftInitializer(parameters: parameters) - let failableInitializer = SwiftFailableInitializer.create(forStructBasedOn: jsonElementSchema) - let nestedStructs = createNestedStructs(forElementsIn: jsonElementSchema) - return SwiftStruct(name: name, - properties: properties, - initializer: initializer, - failableInitializer: failableInitializer, - nestedStructs: nestedStructs) - } - - private static func createNestedStructs(forElementsIn jsonElementSchema: JSONElementSchema) -> [SwiftStruct] { - return jsonElementSchema.attributes.values.compactMap(SwiftStruct.tryToCreate(fromJSONType:)) - } - - private static func tryToCreate(fromJSONType jsonType: JSONType) -> SwiftStruct? { - if let schema = jsonType.jsonElementSchema { - return SwiftStruct.create(from: schema) - } - else { - return nil - } - } -} - -// MARK: - JSONElementSchema --> SwiftProperty -fileprivate extension SwiftProperty { - static func createProperties(forStructBasedOn jsonElementSchema: JSONElementSchema) -> [SwiftProperty] { - return jsonElementSchema.attributes.map { (name, type) in - createProperty(basedOnJSONAttribute: name, and: type) - } - } - - private static func createProperty(basedOnJSONAttribute attributeName: String, and attributeType: JSONType) -> SwiftProperty { - let propertyName = attributeName.toSwiftPropertyName() - let propertyType = SwiftType.createType(from: attributeType) - return SwiftProperty(name: propertyName, type: propertyType) - } -} - -// MARK: - SwiftProperty --> SwiftParameter -fileprivate extension SwiftParameter { - static func createParameters(for properties: [SwiftProperty]) -> [SwiftParameter] { - return properties.map { SwiftParameter(name: $0.name, type: $0.type) } - } -} - -// MARK: - JSONType --> SwiftType -fileprivate extension SwiftType { - static func createType(from jsonType: JSONType) -> SwiftType { - let typeName = jsonType.swiftTypeName - let isOptional = jsonType.isRequired == false - return SwiftType(name: typeName, isOptional: isOptional) - } -} - -// MARK: - JSONType --> Swift type name -fileprivate extension JSONType { - var swiftTypeName: String { - switch self { - case let .element(_, schema): return schema.name.toSwiftStructName() - case let .elementArray(_, elementSchema, hasNullableElements): return JSONType.nameForArray(of: elementSchema, hasNullableElements) - case let .valueArray(_, valueType): return JSONType.nameForArray(of: valueType) - case let .number(_, isFloatingPoint): return isFloatingPoint ? "Double" : "Int" - case .date: return "Date" - case .url: return "URL" - case .string: return "String" - case .bool: return "Bool" - case .nullable, .anything: return "Any" - case .emptyArray: return "[Any?]" - } - } - - private static func nameForArray(of schema: JSONElementSchema, _ hasOptionalElements: Bool) -> String { - return nameForArray(of: schema.name.toSwiftStructName(), hasOptionalElements: hasOptionalElements) - } - - private static func nameForArray(of valueType: JSONType) -> String { - return nameForArray(of: valueType.swiftTypeName, hasOptionalElements: valueType.isRequired == false) - } - - private static func nameForArray(of typeName: String, hasOptionalElements: Bool) -> String { - let fullTypeName = hasOptionalElements - ? typeName + "?" - : typeName - return "[" + fullTypeName + "]" - } -} diff --git a/json2swift/swift-code-generation.swift b/json2swift/swift-code-generation.swift deleted file mode 100644 index 29994de..0000000 --- a/json2swift/swift-code-generation.swift +++ /dev/null @@ -1,303 +0,0 @@ -// -// swift-code-generation.swift -// json2swift -// -// Created by Joshua Smith on 10/14/16. -// Copyright © 2016 iJoshSmith. All rights reserved. -// - -import Foundation - -struct SwiftCodeGenerator { - /// This method is used when only one Swift file is being generated. - static func generateCodeWithJSONUtilities(for swiftStruct: SwiftStruct) -> String { - return [ - preamble, - "//", - "// MARK: - Data Model", - "//", - swiftStruct.toSwiftCode(), - "", - "//", - "// MARK: - JSON Utilities", - "//", - jsonUtilitiesTemplate, - ""].joined(separator: "\n") - } - - /// This method is used when multiple Swift files are being generated. - static func generateCode(for swiftStruct: SwiftStruct) -> String { - return [ - preamble, - swiftStruct.toSwiftCode(), - ""].joined(separator: "\n") - } - - /// This method is used to only create the JSON utility code once when multiple Swift files are being generated. - static func generateJSONUtilities() -> String { - return [ - preamble, - jsonUtilitiesTemplate, - ""].joined(separator: "\n") - } - - private static let preamble = [ - "// This file was generated by json2swift. https://github.com/ijoshsmith/json2swift", - "", - "import Foundation", - ""].joined(separator: "\n") -} - - -// MARK: - Implementation - -typealias SwiftCode = String -typealias LineOfCode = SwiftCode - -fileprivate struct Indentation { - private let chars: String - private let level: Int - private let value: String - - init(chars: String, level: Int = 0) { - precondition(level >= 0) - self.chars = chars - self.level = level - self.value = String(repeating: chars, count: level) - } - - func apply(toLineOfCode lineOfCode: LineOfCode) -> LineOfCode { - return value + lineOfCode - } - - func apply(toFirstLine firstLine: LineOfCode, - nestedLines generateNestedLines: (Indentation) -> [LineOfCode], - andLastLine lastLine: LineOfCode) -> [LineOfCode] { - let first = apply(toLineOfCode: firstLine) - let middle = generateNestedLines(self.increased()) - let last = apply(toLineOfCode: lastLine) - return [first] + middle + [last] - } - - private func increased() -> Indentation { - return Indentation(chars: chars, level: level + 1) - } -} - -fileprivate extension SwiftStruct { - func toSwiftCode(indentedBy indentChars: String = " ") -> SwiftCode { - let indentation = Indentation(chars: indentChars) - let linesOfCode = toLinesOfCode(at: indentation) - return linesOfCode.joined(separator: "\n") - } - - private func toLinesOfCode(at indentation: Indentation) -> [LineOfCode] { - return indentation.apply( - toFirstLine: "struct \(name): CreatableFromJSON {", - nestedLines: linesOfCodeForMembers(at:), - andLastLine: "}") - } - - private func linesOfCodeForMembers(at indentation: Indentation) -> [LineOfCode] { - return linesOfCodeForProperties(at: indentation) - + initializer.toLinesOfCode(at: indentation) - + failableInitializer.toLinesOfCode(at: indentation) - + linesOfCodeForNestedStructs(at: indentation) - } - - private func linesOfCodeForProperties(at indentation: Indentation) -> [LineOfCode] { - return sortedProperties.map { property in - let propertyCode = property.toLineOfCode() - return indentation.apply(toLineOfCode: propertyCode) - } - } - - private var sortedProperties: [SwiftProperty] { - return properties.sorted { (lhs, rhs) -> Bool in - return lhs.name.compare(rhs.name) == .orderedAscending - } - } - - private func linesOfCodeForNestedStructs(at indentation: Indentation) -> [LineOfCode] { - return sortedNestedStructs.flatMap { $0.toLinesOfCode(at: indentation) } - } - - private var sortedNestedStructs: [SwiftStruct] { - return nestedStructs.sorted(by: { (lhs, rhs) -> Bool in - return lhs.name.compare(rhs.name) == .orderedAscending - }) - } -} - -fileprivate extension SwiftType { - func toSwiftCode() -> SwiftCode { - return isOptional ? name + "?" : name - } -} - -fileprivate extension SwiftProperty { - func toLineOfCode() -> LineOfCode { - return "let \(name): \(type.toSwiftCode())" - } -} - -fileprivate extension SwiftParameter { - func toSwiftCode() -> SwiftCode { - return "\(name): \(type.toSwiftCode())" - } -} - -fileprivate extension SwiftInitializer { - func toLinesOfCode(at indentation: Indentation) -> [LineOfCode] { - return indentation.apply( - toFirstLine: "init(\(parameterList)) {", - nestedLines: linesOfCodeForPropertyAssignments(at:), - andLastLine: "}") - } - - private var parameterList: SwiftCode { - return sortedParameters - .map { $0.toSwiftCode() } - .joined(separator: ", ") - } - - private func linesOfCodeForPropertyAssignments(at indentation: Indentation) -> [LineOfCode] { - return sortedParameters - .map { "self.\($0.name) = \($0.name)" } - .map(indentation.apply(toLineOfCode:)) - } - - private var sortedParameters: [SwiftParameter] { - return parameters.sorted { (lhs, rhs) -> Bool in - return lhs.name.compare(rhs.name) == .orderedAscending - } - } -} - -fileprivate extension SwiftFailableInitializer { - func toLinesOfCode(at indentation: Indentation) -> [LineOfCode] { - return indentation.apply( - toFirstLine: "init?(json: [String: Any]) {", - nestedLines: linesOfCodeInMethodBody(at:), - andLastLine: "}") - } - - private func linesOfCodeInMethodBody(at indentation: Indentation) -> [LineOfCode] { - let linesOfCode = linesOfCodeForTransformations + [lineOfCodeForCallingInitializer] - return linesOfCode.map(indentation.apply(toLineOfCode:)) - } - - private var linesOfCodeForTransformations: [LineOfCode] { - let requiredTransformationLines = sortedRequiredTransformations.map { $0.guardedLetStatement } - let optionalTransformationLines = sortedOptionalTransformations.map { $0.letStatement } - return (requiredTransformationLines + optionalTransformationLines) - } - - private var lineOfCodeForCallingInitializer: LineOfCode { - let sortedPropertyNames = (requiredTransformations + optionalTransformations).map { $0.propertyName }.sorted() - let labeledArguments = sortedPropertyNames.map { $0 + ": " + $0 } - let argumentList = labeledArguments.joined(separator: ", ") - return "self.init(" + argumentList + ")" - } - - private var sortedRequiredTransformations: [TransformationFromJSON] { - return sort(transformations: requiredTransformations) - } - - private var sortedOptionalTransformations: [TransformationFromJSON] { - return sort(transformations: optionalTransformations) - } - - private func sort(transformations: [TransformationFromJSON]) -> [TransformationFromJSON] { - return transformations.sorted { (lhs, rhs) -> Bool in - return lhs.propertyName.compare(rhs.propertyName) == .orderedAscending - } - } -} - -// Internal for unit test access. -internal extension TransformationFromJSON { - var propertyName: String { - switch self { - case let .toCustomStruct(_, propertyName, _): return propertyName - case let .toPrimitiveValue(_, propertyName, _): return propertyName - case let .toCustomStructArray(_, propertyName, _, _): return propertyName - case let .toPrimitiveValueArray(_, propertyName, _, _): return propertyName - } - } - - var guardedLetStatement: LineOfCode { - return "guard \(letStatement) else { return nil }" - } - - var letStatement: LineOfCode { - switch self { - case let .toCustomStruct( attributeName, propertyName, type): return TransformationFromJSON.letStatementForCustomStruct( attributeName, propertyName, type) - case let .toPrimitiveValue( attributeName, propertyName, type): return TransformationFromJSON.letStatementForPrimitiveValue(attributeName, propertyName, type) - case let .toCustomStructArray( attributeName, propertyName, elementType, hasOptionalElements): return TransformationFromJSON.letStatementForCustomStructArray( attributeName, propertyName, elementType, hasOptionalElements) - case let .toPrimitiveValueArray(attributeName, propertyName, elementType, hasOptionalElements): return TransformationFromJSON.letStatementForPrimitiveValueArray(attributeName, propertyName, elementType, hasOptionalElements) - } - } - - private static func letStatementForCustomStruct(_ attributeName: String, _ propertyName: String, _ type: SwiftStruct) -> LineOfCode { - return "let \(propertyName) = \(type.name)(json: json, key: \"\(attributeName)\")" - } - - private static func letStatementForPrimitiveValue(_ attributeName: String, _ propertyName: String, _ type: SwiftPrimitiveValueType) -> LineOfCode { - switch type { - case .any: return "let \(propertyName) = json[\"\(attributeName)\"] as? Any" - case .emptyArray: return "let \(propertyName) = json[\"\(attributeName)\"] as? [Any?]" - case .bool, .int, .string: return "let \(propertyName) = json[\"\(attributeName)\"] as? \(type.name)" - case .double: return "let \(propertyName) = Double(json: json, key: \"\(attributeName)\")" // Allows an integer to be interpreted as a double. - case .url: return "let \(propertyName) = URL(json: json, key: \"\(attributeName)\")" - case .date(let format): return "let \(propertyName) = Date(json: json, key: \"\(attributeName)\", format: \"\(format)\")" - } - } - - private static func letStatementForCustomStructArray(_ attributeName: String, _ propertyName: String, _ elementType: SwiftStruct, _ hasOptionalElements: Bool) -> LineOfCode { - return hasOptionalElements - ? "let \(propertyName) = \(elementType.name).createOptionalInstances(from: json, arrayKey: \"\(attributeName)\")" - : "let \(propertyName) = \(elementType.name).createRequiredInstances(from: json, arrayKey: \"\(attributeName)\")" - } - - private static func letStatementForPrimitiveValueArray(_ attributeName: String, _ propertyName: String, _ elementType: SwiftPrimitiveValueType, _ hasOptionalElements: Bool) -> LineOfCode { - return hasOptionalElements - ? letStatementForArrayOfOptionalPrimitiveValues(attributeName, propertyName, elementType) - : letStatementForArrayOfRequiredPrimitiveValues(attributeName, propertyName, elementType) - } - - private static func letStatementForArrayOfOptionalPrimitiveValues(_ attributeName: String, _ propertyName: String, _ elementType: SwiftPrimitiveValueType) -> LineOfCode { - switch elementType { - case .any, .bool, .int, .string, .emptyArray: return "let \(propertyName) = (json[\"\(attributeName)\"] as? [Any]).map({ $0.toOptionalValueArray() as [\(elementType.name)?] })" - case .date(let format): return "let \(propertyName) = (json[\"\(attributeName)\"] as? [Any]).map({ $0.toOptionalDateArray(withFormat: \"\(format)\") })" - case .double: return "let \(propertyName) = (json[\"\(attributeName)\"] as? [Any]).map({ $0.toOptionalDoubleArray() })" - case .url: return "let \(propertyName) = (json[\"\(attributeName)\"] as? [Any]).map({ $0.toOptionalURLArray() })" - } - } - - private static func letStatementForArrayOfRequiredPrimitiveValues(_ attributeName: String, _ propertyName: String, _ elementType: SwiftPrimitiveValueType) -> LineOfCode { - switch elementType { - case .any: return "let \(propertyName) = json[\"\(attributeName)\"] as? [Any?]" // Any is treated as optional. - case .emptyArray: return "let \(propertyName) = json[\"\(attributeName)\"] as? [[Any?]]" - case .bool, .int, .string: return "let \(propertyName) = json[\"\(attributeName)\"] as? [\(elementType.name)]" - case .date(let format): return "let \(propertyName) = (json[\"\(attributeName)\"] as? [String]).flatMap({ $0.toDateArray(withFormat: \"\(format)\") })" - case .double: return "let \(propertyName) = (json[\"\(attributeName)\"] as? [NSNumber]).map({ $0.toDoubleArray() })" - case .url: return "let \(propertyName) = (json[\"\(attributeName)\"] as? [String]).flatMap({ $0.toURLArray() })" - } - } -} - -fileprivate extension SwiftPrimitiveValueType { - var name: String { - switch self { - case .any: return "Any" - case .bool: return "Bool" - case .date: return "Date" - case .double: return "Double" - case .emptyArray: return "[Any?]" - case .int: return "Int" - case .string: return "String" - case .url: return "URL" - } - } -} diff --git a/unit_tests/Info.plist b/unit_tests/Info.plist deleted file mode 100644 index 6c6c23c..0000000 --- a/unit_tests/Info.plist +++ /dev/null @@ -1,22 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - BNDL - CFBundleShortVersionString - 1.0 - CFBundleVersion - 1 - - diff --git a/unit_tests/swift-code-generation-primitive-array-tests.swift b/unit_tests/swift-code-generation-primitive-array-tests.swift deleted file mode 100644 index 11ef275..0000000 --- a/unit_tests/swift-code-generation-primitive-array-tests.swift +++ /dev/null @@ -1,96 +0,0 @@ -// -// swift-code-generation-primitive-array-tests.swift -// json2swift -// -// Created by Joshua Smith on 10/21/16. -// Copyright © 2016 iJoshSmith. All rights reserved. -// - -import XCTest - -class swift_code_generation_primitive_array_tests: XCTestCase { - - // MARK: - Array with required primitive values - - func test_array_of_required_any() { - let transformation = TransformationFromJSON.toPrimitiveValueArray(attributeName: "a", propertyName: "p", elementType: .any, hasOptionalElements: false) - XCTAssertEqual(transformation.letStatement, "let p = json[\"a\"] as? [Any?]") // Any is treated as optional. - } - - func test_array_of_required_bool() { - let transformation = TransformationFromJSON.toPrimitiveValueArray(attributeName: "a", propertyName: "p", elementType: .bool, hasOptionalElements: false) - XCTAssertEqual(transformation.letStatement, "let p = json[\"a\"] as? [Bool]") - } - - func test_array_of_required_date() { - let transformation = TransformationFromJSON.toPrimitiveValueArray(attributeName: "a", propertyName: "p", elementType: .date(format: "M/d/yyyy"), hasOptionalElements: false) - XCTAssertEqual(transformation.letStatement, "let p = (json[\"a\"] as? [String]).flatMap({ $0.toDateArray(withFormat: \"M/d/yyyy\") })") - } - - func test_array_of_required_double() { - let transformation = TransformationFromJSON.toPrimitiveValueArray(attributeName: "a", propertyName: "p", elementType: .double, hasOptionalElements: false) - XCTAssertEqual(transformation.letStatement, "let p = (json[\"a\"] as? [NSNumber]).map({ $0.toDoubleArray() })") - } - - func test_array_of_required_emptyArray() { - let transformation = TransformationFromJSON.toPrimitiveValueArray(attributeName: "a", propertyName: "p", elementType: .emptyArray, hasOptionalElements: false) - XCTAssertEqual(transformation.letStatement, "let p = json[\"a\"] as? [[Any?]]") - } - - func test_array_of_required_int() { - let transformation = TransformationFromJSON.toPrimitiveValueArray(attributeName: "a", propertyName: "p", elementType: .int, hasOptionalElements: false) - XCTAssertEqual(transformation.letStatement, "let p = json[\"a\"] as? [Int]") - } - - func test_array_of_required_string() { - let transformation = TransformationFromJSON.toPrimitiveValueArray(attributeName: "a", propertyName: "p", elementType: .string, hasOptionalElements: false) - XCTAssertEqual(transformation.letStatement, "let p = json[\"a\"] as? [String]") - } - - func test_array_of_required_url() { - let transformation = TransformationFromJSON.toPrimitiveValueArray(attributeName: "a", propertyName: "p", elementType: .url, hasOptionalElements: false) - XCTAssertEqual(transformation.letStatement, "let p = (json[\"a\"] as? [String]).flatMap({ $0.toURLArray() })") - } - - // MARK: - Array with optional primitive values - - func test_array_of_optional_any() { - let transformation = TransformationFromJSON.toPrimitiveValueArray(attributeName: "a", propertyName: "p", elementType: .any, hasOptionalElements: true) - XCTAssertEqual(transformation.letStatement, "let p = (json[\"a\"] as? [Any]).map({ $0.toOptionalValueArray() as [Any?] })") - } - - func test_array_of_optional_bool() { - let transformation = TransformationFromJSON.toPrimitiveValueArray(attributeName: "a", propertyName: "p", elementType: .bool, hasOptionalElements: true) - XCTAssertEqual(transformation.letStatement, "let p = (json[\"a\"] as? [Any]).map({ $0.toOptionalValueArray() as [Bool?] })") - } - - func test_array_of_optional_date() { - let transformation = TransformationFromJSON.toPrimitiveValueArray(attributeName: "a", propertyName: "p", elementType: .date(format: "M/d/yyyy"), hasOptionalElements: true) - XCTAssertEqual(transformation.letStatement, "let p = (json[\"a\"] as? [Any]).map({ $0.toOptionalDateArray(withFormat: \"M/d/yyyy\") })") - } - - func test_array_of_optional_double() { - let transformation = TransformationFromJSON.toPrimitiveValueArray(attributeName: "a", propertyName: "p", elementType: .double, hasOptionalElements: true) - XCTAssertEqual(transformation.letStatement, "let p = (json[\"a\"] as? [Any]).map({ $0.toOptionalDoubleArray() })") - } - - func test_array_of_optional_emptyArray() { - let transformation = TransformationFromJSON.toPrimitiveValueArray(attributeName: "a", propertyName: "p", elementType: .emptyArray, hasOptionalElements: true) - XCTAssertEqual(transformation.letStatement, "let p = (json[\"a\"] as? [Any]).map({ $0.toOptionalValueArray() as [[Any?]?] })") - } - - func test_array_of_optional_int() { - let transformation = TransformationFromJSON.toPrimitiveValueArray(attributeName: "a", propertyName: "p", elementType: .int, hasOptionalElements: true) - XCTAssertEqual(transformation.letStatement, "let p = (json[\"a\"] as? [Any]).map({ $0.toOptionalValueArray() as [Int?] })") - } - - func test_array_of_optional_string() { - let transformation = TransformationFromJSON.toPrimitiveValueArray(attributeName: "a", propertyName: "p", elementType: .string, hasOptionalElements: true) - XCTAssertEqual(transformation.letStatement, "let p = (json[\"a\"] as? [Any]).map({ $0.toOptionalValueArray() as [String?] })") - } - - func test_array_of_optional_url() { - let transformation = TransformationFromJSON.toPrimitiveValueArray(attributeName: "a", propertyName: "p", elementType: .url, hasOptionalElements: true) - XCTAssertEqual(transformation.letStatement, "let p = (json[\"a\"] as? [Any]).map({ $0.toOptionalURLArray() })") - } -}