├── .swift-version ├── Apps ├── jc │ ├── .gitignore │ ├── Tests │ │ ├── LinuxMain.swift │ │ └── jcTests │ │ │ ├── XCTestManifests.swift │ │ │ └── jcTests.swift │ ├── Makefile │ ├── Package.resolved │ ├── Package.swift │ ├── README.md │ └── Sources │ │ └── jc │ │ ├── main.swift │ │ ├── Extensions.swift │ │ └── Help.swift └── JaCo │ ├── JaCo │ ├── Assets.xcassets │ │ ├── Contents.json │ │ └── AppIcon.appiconset │ │ │ ├── logo_icon-16x16.png │ │ │ ├── logo_icon-32x32.png │ │ │ ├── logo_icon-64x64.png │ │ │ ├── logo_icon-1024x1024.png │ │ │ ├── logo_icon-128x128.png │ │ │ ├── logo_icon-256x256-1.png │ │ │ ├── logo_icon-256x256.png │ │ │ ├── logo_icon-32x32-1.png │ │ │ ├── logo_icon-512x512-1.png │ │ │ ├── logo_icon-512x512.png │ │ │ └── Contents.json │ ├── JaCo.entitlements │ ├── Extensions │ │ └── NSTextView+.swift │ ├── AppDelegate.swift │ ├── Info.plist │ └── ViewController.swift │ ├── JaCo.xcodeproj │ └── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist │ ├── JaCo.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist │ ├── Podfile.lock │ ├── Podfile │ ├── README.md │ ├── JaCoTests │ ├── Info.plist │ └── JaCoTests.swift │ ├── JaCoUITests │ ├── Info.plist │ └── JaCoUITests.swift │ └── .gitignore ├── .codecov.yml ├── JSONtoCodable.xcodeproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── WorkspaceSettings.xcsettings │ │ └── IDEWorkspaceChecks.plist ├── xcshareddata │ └── xcschemes │ │ ├── xcschememanagement.plist │ │ ├── JSONtoCodable watchOS.xcscheme │ │ ├── JSONtoCodable iOS.xcscheme │ │ ├── JSONtoCodable tvOS.xcscheme │ │ └── JSONtoCodable macOS.xcscheme ├── JSONtoCodable_Info.plist └── JSONtoCodableTests_Info.plist ├── Sources ├── Error │ └── ErrorHandring.swift ├── Enum │ ├── LineType.swift │ ├── AccessModifier.swift │ ├── IndentType.swift │ ├── CaseType.swift │ └── Type.swift ├── Core │ ├── Config.swift │ ├── Property.swift │ └── JSONtoCodable.swift ├── Info.plist └── Extensions │ └── String+.swift ├── Tests ├── Info.plist └── JSONtoCodableTests │ ├── NoCodingkeysTests.swift │ ├── EscapeSequenceTests.swift │ ├── SymbolsTests.swift │ ├── LessParamsTests.swift │ ├── MergeStructTests.swift │ ├── LineTests.swift │ ├── MergeWithOptionalTests.swift │ ├── CaseTests.swift │ ├── NestingTests.swift │ ├── MergePropertiesTests.swift │ ├── ReservedWordsTests.swift │ ├── IndentTests.swift │ ├── JSONtoCodableTests.swift │ ├── TypeTests.swift │ └── ArrayTests.swift ├── JSONtoCodable.podspec ├── LICENSE ├── Package.swift ├── .swiftlint.yml ├── .gitignore └── README.md /.swift-version: -------------------------------------------------------------------------------- 1 | 4.2 2 | -------------------------------------------------------------------------------- /Apps/jc/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /.build 3 | /Packages 4 | /*.xcodeproj 5 | -------------------------------------------------------------------------------- /Apps/JaCo/JaCo/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Apps/jc/Tests/LinuxMain.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | 3 | import jcTests 4 | 5 | var tests = [XCTestCaseEntry]() 6 | tests += jcTests.allTests() 7 | XCTMain(tests) -------------------------------------------------------------------------------- /.codecov.yml: -------------------------------------------------------------------------------- 1 | coverage: 2 | precision: 2 3 | round: down 4 | range: 90...100" 5 | 6 | ignore: 7 | - Demo/* 8 | - Tests/* 9 | 10 | status: 11 | patch: false 12 | -------------------------------------------------------------------------------- /Apps/JaCo/JaCo/Assets.xcassets/AppIcon.appiconset/logo_icon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YutoMizutani/JSONtoCodable/HEAD/Apps/JaCo/JaCo/Assets.xcassets/AppIcon.appiconset/logo_icon-16x16.png -------------------------------------------------------------------------------- /Apps/JaCo/JaCo/Assets.xcassets/AppIcon.appiconset/logo_icon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YutoMizutani/JSONtoCodable/HEAD/Apps/JaCo/JaCo/Assets.xcassets/AppIcon.appiconset/logo_icon-32x32.png -------------------------------------------------------------------------------- /Apps/JaCo/JaCo/Assets.xcassets/AppIcon.appiconset/logo_icon-64x64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YutoMizutani/JSONtoCodable/HEAD/Apps/JaCo/JaCo/Assets.xcassets/AppIcon.appiconset/logo_icon-64x64.png -------------------------------------------------------------------------------- /Apps/JaCo/JaCo/Assets.xcassets/AppIcon.appiconset/logo_icon-1024x1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YutoMizutani/JSONtoCodable/HEAD/Apps/JaCo/JaCo/Assets.xcassets/AppIcon.appiconset/logo_icon-1024x1024.png -------------------------------------------------------------------------------- /Apps/JaCo/JaCo/Assets.xcassets/AppIcon.appiconset/logo_icon-128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YutoMizutani/JSONtoCodable/HEAD/Apps/JaCo/JaCo/Assets.xcassets/AppIcon.appiconset/logo_icon-128x128.png -------------------------------------------------------------------------------- /Apps/JaCo/JaCo/Assets.xcassets/AppIcon.appiconset/logo_icon-256x256-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YutoMizutani/JSONtoCodable/HEAD/Apps/JaCo/JaCo/Assets.xcassets/AppIcon.appiconset/logo_icon-256x256-1.png -------------------------------------------------------------------------------- /Apps/JaCo/JaCo/Assets.xcassets/AppIcon.appiconset/logo_icon-256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YutoMizutani/JSONtoCodable/HEAD/Apps/JaCo/JaCo/Assets.xcassets/AppIcon.appiconset/logo_icon-256x256.png -------------------------------------------------------------------------------- /Apps/JaCo/JaCo/Assets.xcassets/AppIcon.appiconset/logo_icon-32x32-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YutoMizutani/JSONtoCodable/HEAD/Apps/JaCo/JaCo/Assets.xcassets/AppIcon.appiconset/logo_icon-32x32-1.png -------------------------------------------------------------------------------- /Apps/JaCo/JaCo/Assets.xcassets/AppIcon.appiconset/logo_icon-512x512-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YutoMizutani/JSONtoCodable/HEAD/Apps/JaCo/JaCo/Assets.xcassets/AppIcon.appiconset/logo_icon-512x512-1.png -------------------------------------------------------------------------------- /Apps/JaCo/JaCo/Assets.xcassets/AppIcon.appiconset/logo_icon-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YutoMizutani/JSONtoCodable/HEAD/Apps/JaCo/JaCo/Assets.xcassets/AppIcon.appiconset/logo_icon-512x512.png -------------------------------------------------------------------------------- /Apps/jc/Tests/jcTests/XCTestManifests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | 3 | #if !os(macOS) 4 | public func allTests() -> [XCTestCaseEntry] { 5 | return [ 6 | testCase(jcTests.allTests), 7 | ] 8 | } 9 | #endif -------------------------------------------------------------------------------- /JSONtoCodable.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Apps/JaCo/JaCo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /JSONtoCodable.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Apps/JaCo/JaCo.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Apps/jc/Makefile: -------------------------------------------------------------------------------- 1 | PREFIX?=/usr/local 2 | 3 | deps: 4 | swift build 5 | 6 | gen: 7 | swift package generate-xcodeproj 8 | 9 | build: 10 | swift build --disable-sandbox -c release -Xswiftc -static-stdlib 11 | 12 | install: build 13 | mkdir -p "$(PREFIX)/bin" 14 | cp -f ".build/release/jc" "$(PREFIX)/bin/jc" 15 | -------------------------------------------------------------------------------- /Sources/Error/ErrorHandring.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ErrorHandring.swift 3 | // JSONtoCodable 4 | // 5 | // Created by Yuto Mizutani on 2018/09/20. 6 | // Copyright © 2018 Yuto Mizutani. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public enum JSONError: Error { 12 | case wrongFormat 13 | } 14 | -------------------------------------------------------------------------------- /Apps/JaCo/JaCo.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Apps/JaCo/JaCo.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /JSONtoCodable.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Sources/Enum/LineType.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LineType.swift 3 | // JSONtoCodable 4 | // 5 | // Created by Yuto Mizutani on 2018/09/20. 6 | // 7 | 8 | import Foundation 9 | 10 | public enum LineType: String { 11 | /// \n 12 | case lineFeed = "\n" 13 | /// \r 14 | case carriageReturn = "\r" 15 | /// \r\n 16 | case both = "\r\n" 17 | } 18 | -------------------------------------------------------------------------------- /JSONtoCodable.xcodeproj/xcshareddata/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | SchemeUserState 5 | 6 | JSONtoCodable-Package.xcscheme 7 | 8 | 9 | SuppressBuildableAutocreation 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /Apps/JaCo/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - JSONtoCodable (2.1.1) 3 | 4 | DEPENDENCIES: 5 | - JSONtoCodable 6 | 7 | SPEC REPOS: 8 | https://github.com/cocoapods/specs.git: 9 | - JSONtoCodable 10 | 11 | SPEC CHECKSUMS: 12 | JSONtoCodable: b0270fcdbeb25f2de1850dfbce9dbe0da45ee81e 13 | 14 | PODFILE CHECKSUM: 553562cd357d51760c5bc4db5f09085570595643 15 | 16 | COCOAPODS: 1.6.0.beta.2 17 | -------------------------------------------------------------------------------- /Sources/Enum/AccessModifier.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AccessModifier.swift 3 | // JSONtoCodable 4 | // 5 | // Created by Yuto Mizutani on 2018/09/19. 6 | // Copyright © 2018 Yuto Mizutani. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public enum AccessModifier: String { 12 | case `default` = "" 13 | case `private`, `fileprivate`, `internal`, `public`, open 14 | } 15 | -------------------------------------------------------------------------------- /Apps/JaCo/JaCo/JaCo.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.files.user-selected.read-only 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Apps/JaCo/JaCo/Extensions/NSTextView+.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NSTextView+.swift 3 | // JaCo 4 | // 5 | // Created by Yuto Mizutani on 2018/10/25. 6 | // Copyright © 2018 Yuto Mizutani. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | extension NSTextView { 12 | override open func paste(_ sender: Any?) { 13 | // Ignore text format 14 | pasteAsPlainText(sender) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Apps/jc/Package.resolved: -------------------------------------------------------------------------------- 1 | { 2 | "object": { 3 | "pins": [ 4 | { 5 | "package": "JSONtoCodable", 6 | "repositoryURL": "https://github.com/YutoMizutani/JSONtoCodable.git", 7 | "state": { 8 | "branch": null, 9 | "revision": "1f636f1bff9462e299cce7feccd0f9315c6c64de", 10 | "version": "2.0.4" 11 | } 12 | } 13 | ] 14 | }, 15 | "version": 1 16 | } 17 | -------------------------------------------------------------------------------- /Apps/JaCo/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment the next line to define a global platform for your project 2 | # platform :ios, '9.0' 3 | 4 | target 'JaCo' do 5 | # Comment the next line if you're not using Swift and don't want to use dynamic frameworks 6 | use_frameworks! 7 | 8 | pod 'JSONtoCodable' 9 | 10 | # Pods for JaCo 11 | 12 | target 'JaCoTests' do 13 | inherit! :search_paths 14 | # Pods for testing 15 | end 16 | 17 | target 'JaCoUITests' do 18 | inherit! :search_paths 19 | # Pods for testing 20 | end 21 | 22 | end 23 | -------------------------------------------------------------------------------- /Sources/Core/Config.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Config.swift 3 | // JSONtoCodable 4 | // 5 | // Created by Yuto Mizutani on 2018/09/19. 6 | // Copyright © 2018 Yuto Mizutani. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public struct Config { 12 | public var name: String = "Result" 13 | public var accessModifier: AccessModifier = .public 14 | public var caseType: (variable: CaseType, `struct`: CaseType) = (.camel, .pascal) 15 | public var lineType: LineType = .lineFeed 16 | public var indentType: IndentType = .space(4) 17 | } 18 | -------------------------------------------------------------------------------- /Sources/Enum/IndentType.swift: -------------------------------------------------------------------------------- 1 | // 2 | // IndentType.swift 3 | // JSONtoCodable 4 | // 5 | // Created by Yuto Mizutani on 2018/09/20. 6 | // 7 | 8 | import Foundation 9 | 10 | public enum IndentType { 11 | case space(Int) 12 | case tab(Int) 13 | } 14 | 15 | extension IndentType { 16 | var rawValue: String { 17 | switch self { 18 | case .space(let v): 19 | return String(repeating: " ", count: v) 20 | case .tab(let v): 21 | return String(repeating: "\t", count: v) 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Apps/JaCo/JaCo/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // JaCo 4 | // 5 | // Created by Yuto Mizutani on 2018/10/25. 6 | // Copyright © 2018 Yuto Mizutani. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | @NSApplicationMain 12 | class AppDelegate: NSObject, NSApplicationDelegate { 13 | 14 | 15 | 16 | func applicationDidFinishLaunching(_ aNotification: Notification) { 17 | // Insert code here to initialize your application 18 | } 19 | 20 | func applicationWillTerminate(_ aNotification: Notification) { 21 | // Insert code here to tear down your application 22 | } 23 | 24 | 25 | } 26 | 27 | -------------------------------------------------------------------------------- /Apps/JaCo/README.md: -------------------------------------------------------------------------------- 1 | # JaCo 2 | 3 | A generating tool from Raw **JSON to Codable** output application *JSONtoCodable* for macOS app. 4 | 5 | #### Current version: 2.1.1 [[Download](https://github.com/YutoMizutani/JSONtoCodable/releases/download/2.1.1/JaCo.zip)] 6 | 7 | ![demo_macos.png](https://raw.githubusercontent.com/YutoMizutani/JSONtoCodable/media/media/demo_macos.png) 8 | 9 | ## Get started 10 | 11 | **JaCo** is using [CocoaPods](https://cocoapods.org), enter it in your terminal, 12 | 13 | ``` 14 | $ pod install 15 | ``` 16 | 17 | ## License 18 | 19 | JSONtoCodable and JaCo is available under the [MIT license](https://github.com/YutoMizutani/JSONtoCodable/blob/master/LICENSE). 20 | -------------------------------------------------------------------------------- /Tests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /Apps/JaCo/JaCoTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /Apps/JaCo/JaCoUITests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /Tests/JSONtoCodableTests/NoCodingkeysTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NoCodingkeysTests.swift 3 | // JSONtoCodable 4 | // 5 | // Created by Yuto Mizutani on 2018/09/20. 6 | // 7 | 8 | import XCTest 9 | @testable import JSONtoCodable 10 | 11 | class NoCodingkeysTests: XCTestCase { 12 | var base: JSONtoCodable! 13 | 14 | override func setUp() { 15 | super.setUp() 16 | self.base = JSONtoCodable() 17 | } 18 | 19 | func testNoCodingkeys() { 20 | let json: String = """ 21 | { 22 | "hello": "World" 23 | } 24 | """ 25 | let expectation: String = """ 26 | public struct Result: Codable { 27 | public let hello: String 28 | } 29 | """ 30 | let result: String? = try? self.base.generate(json) 31 | XCTAssertEqual(result, expectation) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /JSONtoCodable.xcodeproj/JSONtoCodable_Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CFBundleDevelopmentRegion 5 | en 6 | CFBundleExecutable 7 | $(EXECUTABLE_NAME) 8 | CFBundleIdentifier 9 | $(PRODUCT_BUNDLE_IDENTIFIER) 10 | CFBundleInfoDictionaryVersion 11 | 6.0 12 | CFBundleName 13 | $(PRODUCT_NAME) 14 | CFBundlePackageType 15 | FMWK 16 | CFBundleShortVersionString 17 | 1.0 18 | CFBundleSignature 19 | ???? 20 | CFBundleVersion 21 | $(CURRENT_PROJECT_VERSION) 22 | NSPrincipalClass 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /JSONtoCodable.xcodeproj/JSONtoCodableTests_Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CFBundleDevelopmentRegion 5 | en 6 | CFBundleExecutable 7 | $(EXECUTABLE_NAME) 8 | CFBundleIdentifier 9 | $(PRODUCT_BUNDLE_IDENTIFIER) 10 | CFBundleInfoDictionaryVersion 11 | 6.0 12 | CFBundleName 13 | $(PRODUCT_NAME) 14 | CFBundlePackageType 15 | BNDL 16 | CFBundleShortVersionString 17 | 1.0 18 | CFBundleSignature 19 | ???? 20 | CFBundleVersion 21 | $(CURRENT_PROJECT_VERSION) 22 | NSPrincipalClass 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /Sources/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | NSPrincipalClass 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /Apps/jc/Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:4.2 2 | // The swift-tools-version declares the minimum version of Swift required to build this package. 3 | 4 | import PackageDescription 5 | 6 | let package = Package( 7 | name: "jc", 8 | dependencies: [ 9 | // Dependencies declare other packages that this package depends on. 10 | // .package(url: /* package url */, from: "1.0.0"), 11 | .package(url: "https://github.com/YutoMizutani/JSONtoCodable.git", from: "2.0.0"), 12 | ], 13 | targets: [ 14 | // Targets are the basic building blocks of a package. A target can define a module or a test suite. 15 | // Targets can depend on other targets in this package, and on products in packages which this package depends on. 16 | .target( 17 | name: "jc", 18 | dependencies: ["JSONtoCodable"]), 19 | .testTarget( 20 | name: "jcTests", 21 | dependencies: ["jc"]), 22 | ] 23 | ) 24 | -------------------------------------------------------------------------------- /Apps/jc/README.md: -------------------------------------------------------------------------------- 1 | # jc 2 | 3 | A generating tool from Raw **JSON to Codable** output application *JSONtoCodable* for CLI. 4 | 5 | #### Current version: 2.1.1 [[Download](https://github.com/YutoMizutani/JSONtoCodable/releases/download/2.1.1/jc.zip)] or [brew install](https://github.com/YutoMizutani/JSONtoCodable/tree/master/Apps/jc#installation) 6 | 7 | ![demo_cli.png](https://raw.githubusercontent.com/YutoMizutani/JSONtoCodable/media/media/demo_cli.png) 8 | 9 | ## Installation 10 | 11 | ``` 12 | $ brew tap YutoMizutani/jc 13 | $ brew install jc 14 | ``` 15 | 16 | ## Usage example 17 | 18 | ``` 19 | $ curl https://httpbin.org/get | jc 20 | ``` 21 | 22 | or generate *.swift* file, 23 | 24 | ``` 25 | $ curl https://httpbin.org/get | jc > Result.swift 26 | ``` 27 | 28 | ## Help command 29 | 30 | ``` 31 | $ jc -h 32 | ``` 33 | 34 | ## License 35 | 36 | JSONtoCodable and jc is available under the [MIT license](https://github.com/YutoMizutani/JSONtoCodable/blob/master/LICENSE). 37 | -------------------------------------------------------------------------------- /JSONtoCodable.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "JSONtoCodable" 3 | s.version = "2.1.1" 4 | s.summary = "A generating tool from Raw JSON to Codable" 5 | 6 | s.description = <<-DESC 7 | A generating tool from Raw JSON to Codable. 8 | DESC 9 | 10 | s.homepage = "https://github.com/YutoMizutani/JSONtoCodable" 11 | s.license = { :type => 'MIT', :file => 'LICENSE' } 12 | s.author = { "Yuto Mizutani" => "yuto.mizutani.dev@gmail.com" } 13 | s.source = { :git => "https://github.com/YutoMizutani/JSONtoCodable.git", :tag => s.version.to_s } 14 | s.social_media_url = 'https://twitter.com/EXPENSIVE_MAN' 15 | 16 | s.ios.deployment_target = '8.0' 17 | s.osx.deployment_target = '10.10' 18 | s.watchos.deployment_target = '4.0' 19 | s.tvos.deployment_target = '11.0' 20 | 21 | s.requires_arc = true 22 | 23 | s.source_files = 'Sources/**/*.swift' 24 | s.swift_version = '4.2' 25 | end 26 | -------------------------------------------------------------------------------- /Sources/Core/Property.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Property.swift 3 | // JSONtoCodable 4 | // 5 | // Created by Yuto Mizutani on 2018/09/21. 6 | // 7 | 8 | import Foundation 9 | 10 | class Property: Equatable { 11 | var prefix: String 12 | var immutables: [String] = [] 13 | var structs: [String] = [] 14 | var codingKeys: [String] = [] 15 | let suffix: String? 16 | 17 | init(_ key: String, accessModifier accessModifierType: AccessModifier = .default) { 18 | let accessModifier: String = accessModifierType == .default ? "" : "\(accessModifierType.rawValue) " 19 | self.prefix = "\(accessModifier)struct \(key): Codable {" 20 | self.suffix = "}" 21 | } 22 | 23 | static func == (lhs: Property, rhs: Property) -> Bool { 24 | return 25 | lhs.prefix == rhs.prefix && 26 | lhs.immutables == rhs.immutables && 27 | lhs.structs == rhs.structs && 28 | lhs.codingKeys == rhs.codingKeys && 29 | lhs.suffix == rhs.suffix 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Apps/JaCo/JaCoTests/JaCoTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // JaCoTests.swift 3 | // JaCoTests 4 | // 5 | // Created by Yuto Mizutani on 2018/10/25. 6 | // Copyright © 2018 Yuto Mizutani. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import JaCo 11 | 12 | class JaCoTests: XCTestCase { 13 | 14 | override func setUp() { 15 | // Put setup code here. This method is called before the invocation of each test method in the class. 16 | } 17 | 18 | override func tearDown() { 19 | // Put teardown code here. This method is called after the invocation of each test method in the class. 20 | } 21 | 22 | func testExample() { 23 | // This is an example of a functional test case. 24 | // Use XCTAssert and related functions to verify your tests produce the correct results. 25 | } 26 | 27 | func testPerformanceExample() { 28 | // This is an example of a performance test case. 29 | self.measure { 30 | // Put the code you want to measure the time of here. 31 | } 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /Sources/Enum/CaseType.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CaseType.swift 3 | // JSONtoCodable 4 | // 5 | // Created by Yuto Mizutani on 2018/09/19. 6 | // Copyright © 2018 Yuto Mizutani. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public enum CaseType { 12 | /// PascalCase 13 | case pascal 14 | /// camelCase 15 | case camel 16 | /// snake_case 17 | case snake 18 | /// SCREAMING_SNAKE_CASE 19 | case screamingSnake 20 | } 21 | 22 | public extension Array where Element == String { 23 | func joined(with caseType: CaseType) -> String { 24 | switch caseType { 25 | case .pascal: 26 | return self.map { $0.capitalized }.joined() 27 | case .camel: 28 | return self.enumerated().map { $0.offset == 0 ? $0.element.lowercased() : $0.element.capitalized }.joined() 29 | case .snake: 30 | return self.map { $0.lowercased() }.joined(separator: "_") 31 | case .screamingSnake: 32 | return self.map { $0.uppercased() }.joined(separator: "_") 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Yuto Mizutani 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:4.0 2 | // The swift-tools-version declares the minimum version of Swift required to build this package. 3 | 4 | import PackageDescription 5 | 6 | let package = Package( 7 | name: "JSONtoCodable", 8 | products: [ 9 | // Products define the executables and libraries produced by a package, and make them visible to other packages. 10 | .library( 11 | name: "JSONtoCodable", 12 | targets: ["JSONtoCodable"]), 13 | ], 14 | dependencies: [ 15 | // Dependencies declare other packages that this package depends on. 16 | // .package(url: /* package url */, from: "1.0.0"), 17 | ], 18 | targets: [ 19 | // Targets are the basic building blocks of a package. A target can define a module or a test suite. 20 | // Targets can depend on other targets in this package, and on products in packages which this package depends on. 21 | .target( 22 | name: "JSONtoCodable", 23 | path: "Sources"), 24 | .testTarget( 25 | name: "JSONtoCodableTests", 26 | dependencies: ["JSONtoCodable"], 27 | path: "Tests"), 28 | ] 29 | ) 30 | -------------------------------------------------------------------------------- /Apps/JaCo/JaCo/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleVersion 22 | 1 23 | LSMinimumSystemVersion 24 | $(MACOSX_DEPLOYMENT_TARGET) 25 | NSHumanReadableCopyright 26 | Copyright © 2018 Yuto Mizutani. All rights reserved. 27 | NSMainStoryboardFile 28 | Main 29 | NSPrincipalClass 30 | NSApplication 31 | 32 | 33 | -------------------------------------------------------------------------------- /Apps/JaCo/JaCoUITests/JaCoUITests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // JaCoUITests.swift 3 | // JaCoUITests 4 | // 5 | // Created by Yuto Mizutani on 2018/10/25. 6 | // Copyright © 2018 Yuto Mizutani. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | 11 | class JaCoUITests: XCTestCase { 12 | 13 | override func setUp() { 14 | // Put setup code here. This method is called before the invocation of each test method in the class. 15 | 16 | // In UI tests it is usually best to stop immediately when a failure occurs. 17 | continueAfterFailure = false 18 | 19 | // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method. 20 | XCUIApplication().launch() 21 | 22 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. 23 | } 24 | 25 | override func tearDown() { 26 | // Put teardown code here. This method is called after the invocation of each test method in the class. 27 | } 28 | 29 | func testExample() { 30 | // Use recording to get started writing UI tests. 31 | // Use XCTAssert and related functions to verify your tests produce the correct results. 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /Tests/JSONtoCodableTests/EscapeSequenceTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EscapeSequenceTests.swift 3 | // JSONtoCodable 4 | // 5 | // Created by Yuto Mizutani on 2018/09/25. 6 | // 7 | 8 | import XCTest 9 | @testable import JSONtoCodable 10 | 11 | class EscapeSequenceTests: XCTestCase { 12 | var base: JSONtoCodable! 13 | 14 | override func setUp() { 15 | super.setUp() 16 | self.base = JSONtoCodable() 17 | } 18 | 19 | func testEscapeSequence() { 20 | let json: String = """ 21 | { 22 | "hello": "Hello, \\"world!!\\"" 23 | } 24 | """ 25 | let expectation: String = """ 26 | public struct Result: Codable { 27 | public let hello: String 28 | } 29 | """ 30 | let result: String? = try? self.base.generate(json) 31 | XCTAssertEqual(result, expectation) 32 | } 33 | 34 | func testDoubleEscapeSequence() { 35 | let json: String = """ 36 | { 37 | "hello": "Hello, \\"world!!\\"", 38 | "hello2nd": "Hello, \\"world!!\\"" 39 | } 40 | """ 41 | let expectation: String = """ 42 | public struct Result: Codable { 43 | public let hello: String 44 | public let hello2nd: String 45 | } 46 | """ 47 | let result: String? = try? self.base.generate(json) 48 | XCTAssertEqual(result, expectation) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Apps/jc/Tests/jcTests/jcTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import class Foundation.Bundle 3 | 4 | final class jcTests: XCTestCase { 5 | func testExample() throws { 6 | // This is an example of a functional test case. 7 | // Use XCTAssert and related functions to verify your tests produce the correct 8 | // results. 9 | 10 | // Some of the APIs that we use below are available in macOS 10.13 and above. 11 | guard #available(macOS 10.13, *) else { 12 | return 13 | } 14 | 15 | let fooBinary = productsDirectory.appendingPathComponent("jc") 16 | 17 | let process = Process() 18 | process.executableURL = fooBinary 19 | 20 | let pipe = Pipe() 21 | process.standardOutput = pipe 22 | 23 | try process.run() 24 | process.waitUntilExit() 25 | 26 | let data = pipe.fileHandleForReading.readDataToEndOfFile() 27 | let output = String(data: data, encoding: .utf8) 28 | 29 | XCTAssertEqual(output, "Hello, world!\n") 30 | } 31 | 32 | /// Returns path to the built products directory. 33 | var productsDirectory: URL { 34 | #if os(macOS) 35 | for bundle in Bundle.allBundles where bundle.bundlePath.hasSuffix(".xctest") { 36 | return bundle.bundleURL.deletingLastPathComponent() 37 | } 38 | fatalError("couldn't find the products directory") 39 | #else 40 | return Bundle.main.bundleURL 41 | #endif 42 | } 43 | 44 | static var allTests = [ 45 | ("testExample", testExample), 46 | ] 47 | } 48 | -------------------------------------------------------------------------------- /Apps/JaCo/JaCo/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "16x16", 5 | "idiom" : "mac", 6 | "filename" : "logo_icon-16x16.png", 7 | "scale" : "1x" 8 | }, 9 | { 10 | "size" : "16x16", 11 | "idiom" : "mac", 12 | "filename" : "logo_icon-32x32.png", 13 | "scale" : "2x" 14 | }, 15 | { 16 | "size" : "32x32", 17 | "idiom" : "mac", 18 | "filename" : "logo_icon-32x32-1.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "32x32", 23 | "idiom" : "mac", 24 | "filename" : "logo_icon-64x64.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "128x128", 29 | "idiom" : "mac", 30 | "filename" : "logo_icon-128x128.png", 31 | "scale" : "1x" 32 | }, 33 | { 34 | "size" : "128x128", 35 | "idiom" : "mac", 36 | "filename" : "logo_icon-256x256.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "256x256", 41 | "idiom" : "mac", 42 | "filename" : "logo_icon-256x256-1.png", 43 | "scale" : "1x" 44 | }, 45 | { 46 | "size" : "256x256", 47 | "idiom" : "mac", 48 | "filename" : "logo_icon-512x512.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "512x512", 53 | "idiom" : "mac", 54 | "filename" : "logo_icon-512x512-1.png", 55 | "scale" : "1x" 56 | }, 57 | { 58 | "size" : "512x512", 59 | "idiom" : "mac", 60 | "filename" : "logo_icon-1024x1024.png", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } -------------------------------------------------------------------------------- /.swiftlint.yml: -------------------------------------------------------------------------------- 1 | disabled_rules: 2 | - colon 3 | - object_literal 4 | - file_length 5 | - file_header 6 | - identifier_name 7 | - file_name 8 | - nesting 9 | - function_body_length 10 | - cyclomatic_complexity 11 | - force_cast 12 | opt_in_rules: 13 | - array_init 14 | - attributes 15 | - closure_end_indentation 16 | - closure_spacing 17 | - contains_over_first_not_nil 18 | - empty_count 19 | - explicit_init 20 | - extension_access_modifier 21 | - fatal_error_message 22 | - file_header 23 | - file_name 24 | - first_where 25 | - joined_default_parameter 26 | - let_var_whitespace 27 | - literal_expression_end_indentation 28 | - nimble_operator 29 | - object_literal 30 | - operator_usage_whitespace 31 | - overridden_super_call 32 | - override_in_extension 33 | - pattern_matching_keywords 34 | - private_action 35 | - private_outlet 36 | - prohibited_super_call 37 | - quick_discouraged_call 38 | - quick_discouraged_focused_test 39 | - quick_discouraged_pending_test 40 | - redundant_nil_coalescing 41 | - single_test_class 42 | - sorted_first_last 43 | - sorted_imports 44 | - switch_case_on_newline 45 | - unneeded_parentheses_in_closure_argument 46 | - vertical_parameter_alignment_on_call 47 | - yoda_condition 48 | excluded: 49 | - Package.swift 50 | - Carthage 51 | - Demo 52 | - Tests 53 | - .build 54 | 55 | line_length: 56 | warning: 160 57 | ignores_comments: true 58 | type_body_length: 59 | - 300 # warning 60 | - 400 # error 61 | file_length: 62 | warning: 500 63 | error: 1200 64 | 65 | # SwiftLint 66 | # https://github.com/realm/SwiftLint 67 | # JP - doc 68 | # https://qiita.com/kagemiku/items/80e6d905dc0059c342b3 69 | -------------------------------------------------------------------------------- /Apps/JaCo/JaCo/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // JaCo 4 | // 5 | // Created by Yuto Mizutani on 2018/09/22. 6 | // Copyright © 2018 Yuto Mizutani. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | import JSONtoCodable 11 | 12 | class ViewController: NSViewController { 13 | @IBOutlet weak var textView: NSTextView! 14 | @IBOutlet weak var generateButton: NSButton! 15 | 16 | var codable: JSONtoCodable = JSONtoCodable() 17 | 18 | override func viewDidLoad() { 19 | super.viewDidLoad() 20 | 21 | self.configureSetting() 22 | self.configureView() 23 | self.configureAction() 24 | } 25 | } 26 | 27 | // MARK: - configure view 28 | 29 | private extension ViewController { 30 | private func configureSetting() { 31 | self.codable.config.accessModifier = .public 32 | } 33 | 34 | private func configureView() { 35 | self.textView.string = """ 36 | { 37 | "user": { 38 | "Name": "Yuto Mizutani" 39 | }, 40 | "lib": { 41 | "lib-name": "JSONtoCodable", 42 | "year": 2018, 43 | "version": "2.1.1", 44 | "released": "2018-09-22" 45 | }, 46 | "text": "Hello, world!!" 47 | } 48 | """ 49 | } 50 | 51 | private func configureAction() { 52 | self.generateButton.action = #selector(self.generate) 53 | } 54 | } 55 | 56 | // MARK: - actions 57 | 58 | private extension ViewController { 59 | @objc private func generate() { 60 | guard let text = try? self.codable.generate(self.textView.string) else { return } 61 | self.textView.string = text 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /Tests/JSONtoCodableTests/SymbolsTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SymbolsTests.swift 3 | // JSONtoCodable 4 | // 5 | // Created by Yuto Mizutani on 2018/09/25. 6 | // 7 | 8 | import XCTest 9 | @testable import JSONtoCodable 10 | 11 | class SymbolsTests: XCTestCase { 12 | var base: JSONtoCodable! 13 | 14 | override func setUp() { 15 | super.setUp() 16 | self.base = JSONtoCodable() 17 | } 18 | 19 | func testAtSign() { 20 | let json: String = """ 21 | { 22 | "@hello": "Hello, world!!" 23 | } 24 | """ 25 | let expectation: String = """ 26 | public struct Result: Codable { 27 | public let hello: String 28 | 29 | private enum CodingKeys: String, CodingKey { 30 | case hello = "@hello" 31 | } 32 | } 33 | """ 34 | let result: String? = try? self.base.generate(json) 35 | XCTAssertEqual(result, expectation) 36 | } 37 | 38 | func testColon() { 39 | let json: String = """ 40 | { 41 | "hello:world": "Hello, world!!" 42 | } 43 | """ 44 | let expectation: String = """ 45 | public struct Result: Codable { 46 | public let helloWorld: String 47 | 48 | private enum CodingKeys: String, CodingKey { 49 | case helloWorld = "hello:world" 50 | } 51 | } 52 | """ 53 | let result: String? = try? self.base.generate(json) 54 | XCTAssertEqual(result, expectation) 55 | } 56 | 57 | func testUnderScore() { 58 | let json: String = """ 59 | { 60 | "_hello": "Hello, world!!" 61 | } 62 | """ 63 | let expectation: String = """ 64 | public struct Result: Codable { 65 | public let hello: String 66 | 67 | private enum CodingKeys: String, CodingKey { 68 | case hello = "_hello" 69 | } 70 | } 71 | """ 72 | let result: String? = try? self.base.generate(json) 73 | XCTAssertEqual(result, expectation) 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /Tests/JSONtoCodableTests/LessParamsTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LessParamsTests.swift 3 | // JSONtoCodable 4 | // 5 | // Created by Yuto Mizutani on 2018/09/20. 6 | // 7 | 8 | import XCTest 9 | @testable import JSONtoCodable 10 | 11 | class NoBracketTests: XCTestCase { 12 | var base: JSONtoCodable! 13 | 14 | override func setUp() { 15 | super.setUp() 16 | self.base = JSONtoCodable() 17 | } 18 | 19 | func testNoBracket() { 20 | let json: String = """ 21 | "Hello": "World" 22 | """ 23 | let expectation: String = """ 24 | public struct Result: Codable { 25 | public let hello: String 26 | 27 | private enum CodingKeys: String, CodingKey { 28 | case hello = "Hello" 29 | } 30 | } 31 | """ 32 | let result: String? = try? self.base.generate(json) 33 | XCTAssertEqual(expectation, result) 34 | } 35 | 36 | func testNoLines() { 37 | let json: String = """ 38 | {"Hello": "World"} 39 | """ 40 | let expectation: String = """ 41 | public struct Result: Codable { 42 | public let hello: String 43 | 44 | private enum CodingKeys: String, CodingKey { 45 | case hello = "Hello" 46 | } 47 | } 48 | """ 49 | let result: String? = try? self.base.generate(json) 50 | XCTAssertEqual(result, expectation) 51 | } 52 | 53 | func testBeginSingleSpaceAndNoBracket() { 54 | let json: String = " \"Hello\": \"World\"" 55 | let expectation: String = """ 56 | public struct Result: Codable { 57 | public let hello: String 58 | 59 | private enum CodingKeys: String, CodingKey { 60 | case hello = "Hello" 61 | } 62 | } 63 | """ 64 | let result: String? = try? self.base.generate(json) 65 | XCTAssertEqual(result, expectation) 66 | } 67 | 68 | func testBeginSingleSpaceAndNoLines() { 69 | let json: String = " {\"Hello\": \"World\"}" 70 | let expectation: String = """ 71 | public struct Result: Codable { 72 | public let hello: String 73 | 74 | private enum CodingKeys: String, CodingKey { 75 | case hello = "Hello" 76 | } 77 | } 78 | """ 79 | let result: String? = try? self.base.generate(json) 80 | XCTAssertEqual(result, expectation) 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /Apps/jc/Sources/jc/main.swift: -------------------------------------------------------------------------------- 1 | // 2 | // main.swift 3 | // jc 4 | // 5 | // Created by Yuto Mizutani on 2018/09/21. 6 | // Copyright © 2018 Yuto Mizutani. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import JSONtoCodable 11 | 12 | let codable = JSONtoCodable() 13 | let argv = ProcessInfo.processInfo.arguments 14 | 15 | let args = Array(argv[1.. 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 34 | 35 | 45 | 46 | 52 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 70 | 71 | 72 | 73 | 75 | 76 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /Tests/JSONtoCodableTests/CaseTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CaseTests.swift 3 | // JSONtoCodable 4 | // 5 | // Created by Yuto Mizutani on 2018/09/20. 6 | // 7 | 8 | import XCTest 9 | @testable import JSONtoCodable 10 | 11 | class CaseTests: XCTestCase { 12 | var base: JSONtoCodable! 13 | 14 | override func setUp() { 15 | super.setUp() 16 | self.base = JSONtoCodable() 17 | } 18 | 19 | func testPascalCase() { 20 | let json: String = """ 21 | { 22 | "Hello": "Hello, world!!", 23 | "HelloWorld": "Hello, world!!" 24 | } 25 | """ 26 | let expectation: String = """ 27 | public struct Result: Codable { 28 | public let Hello: String 29 | public let HelloWorld: String 30 | } 31 | """ 32 | self.base.config.caseType.variable = .pascal 33 | let result: String? = try? self.base.generate(json) 34 | XCTAssertEqual(result, expectation) 35 | } 36 | 37 | func testCamelCase() { 38 | let json: String = """ 39 | { 40 | "Hello": "Hello, world!!", 41 | "HelloWorld": "Hello, world!!" 42 | } 43 | """ 44 | let expectation: String = """ 45 | public struct Result: Codable { 46 | public let hello: String 47 | public let helloWorld: String 48 | 49 | private enum CodingKeys: String, CodingKey { 50 | case hello = "Hello" 51 | case helloWorld = "HelloWorld" 52 | } 53 | } 54 | """ 55 | self.base.config.caseType.variable = .camel 56 | let result: String? = try? self.base.generate(json) 57 | XCTAssertEqual(result, expectation) 58 | } 59 | 60 | func testSnakeCase() { 61 | let json: String = """ 62 | { 63 | "Hello": "Hello, world!!", 64 | "HelloWorld": "Hello, world!!" 65 | } 66 | """ 67 | let expectation: String = """ 68 | public struct Result: Codable { 69 | public let hello: String 70 | public let hello_world: String 71 | 72 | private enum CodingKeys: String, CodingKey { 73 | case hello = "Hello" 74 | case hello_world = "HelloWorld" 75 | } 76 | } 77 | """ 78 | self.base.config.caseType.variable = .snake 79 | let result: String? = try? self.base.generate(json) 80 | XCTAssertEqual(result, expectation) 81 | } 82 | 83 | func testScreamingSnakeCase() { 84 | let json: String = """ 85 | { 86 | "Hello": "Hello, world!!", 87 | "HelloWorld": "Hello, world!!" 88 | } 89 | """ 90 | let expectation: String = """ 91 | public struct Result: Codable { 92 | public let HELLO: String 93 | public let HELLO_WORLD: String 94 | 95 | private enum CodingKeys: String, CodingKey { 96 | case HELLO = "Hello" 97 | case HELLO_WORLD = "HelloWorld" 98 | } 99 | } 100 | """ 101 | self.base.config.caseType.variable = .screamingSnake 102 | let result: String? = try? self.base.generate(json) 103 | XCTAssertEqual(result, expectation) 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /Apps/jc/Sources/jc/Extensions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Extensions.swift 3 | // jc 4 | // 5 | // Created by Yuto Mizutani on 2018/10/22. 6 | // Copyright © 2018 Yuto Mizutani. All rights reserved. 7 | // 8 | 9 | import JSONtoCodable 10 | 11 | extension AccessModifier { 12 | init?(_ text: String) { 13 | switch text { 14 | case "d", "default": 15 | self = .default 16 | case "pr", "private": 17 | self = .private 18 | case "f", "fileprivate": 19 | self = .fileprivate 20 | case "i", "internal": 21 | self = .internal 22 | case "pu", "public": 23 | self = .public 24 | case "o", "open": 25 | self = .open 26 | default: 27 | return nil 28 | } 29 | } 30 | 31 | var parameterString: String { 32 | switch self { 33 | case .default: 34 | return "default" 35 | case .private: 36 | return "private" 37 | case .fileprivate: 38 | return "fileprivate" 39 | case .internal: 40 | return "internal" 41 | case .public: 42 | return "public" 43 | case .open: 44 | return "open" 45 | } 46 | } 47 | } 48 | 49 | extension CaseType { 50 | init?(_ text: String) { 51 | switch text { 52 | case "p", "pascal": 53 | self = .pascal 54 | case "c", "camel": 55 | self = .camel 56 | case "sn", "snake": 57 | self = .snake 58 | case "ss", "screaming-snake": 59 | self = .screamingSnake 60 | default: 61 | return nil 62 | } 63 | } 64 | 65 | var parameterString: String { 66 | switch self { 67 | case .pascal: 68 | return "PascalCase" 69 | case .camel: 70 | return "camelCase" 71 | case .snake: 72 | return "snake_case" 73 | case .screamingSnake: 74 | return "SCREAMING_SNAKE_CASE" 75 | } 76 | } 77 | } 78 | 79 | extension LineType { 80 | init?(_ text: String) { 81 | switch text { 82 | case "\n", "\\n", "n", "line-feed": 83 | self = .lineFeed 84 | case "\r", "\\r", "r", "carriage-return": 85 | self = .carriageReturn 86 | case "\r\n", "\\r\\n", "rn", "both": 87 | self = .both 88 | default: 89 | return nil 90 | } 91 | } 92 | 93 | var parameterString: String { 94 | switch self { 95 | case .lineFeed: 96 | return "Line Feed (LF)" 97 | case .carriageReturn: 98 | return "Carriage Return (CR)" 99 | case .both: 100 | return "Both (CRLF)" 101 | } 102 | } 103 | } 104 | 105 | extension IndentType { 106 | init?(_ text: String) { 107 | guard let num = Int(String(text[text.index(after: text.startIndex).. 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 49 | 50 | 51 | 52 | 53 | 54 | 64 | 65 | 71 | 72 | 73 | 74 | 75 | 76 | 82 | 83 | 89 | 90 | 91 | 92 | 94 | 95 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /JSONtoCodable.xcodeproj/xcshareddata/xcschemes/JSONtoCodable tvOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 49 | 50 | 51 | 52 | 53 | 54 | 64 | 65 | 71 | 72 | 73 | 74 | 75 | 76 | 82 | 83 | 89 | 90 | 91 | 92 | 94 | 95 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /JSONtoCodable.xcodeproj/xcshareddata/xcschemes/JSONtoCodable macOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 49 | 50 | 51 | 52 | 53 | 54 | 64 | 65 | 71 | 72 | 73 | 74 | 75 | 76 | 82 | 83 | 89 | 90 | 91 | 92 | 94 | 95 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /Tests/JSONtoCodableTests/MergePropertiesTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MergePropertiesTests.swift 3 | // JSONtoCodable 4 | // 5 | // Created by Yuto Mizutani on 2018/09/24. 6 | // 7 | 8 | import XCTest 9 | @testable import JSONtoCodable 10 | 11 | class MergePropertiesTests: XCTestCase { 12 | var base: JSONtoCodable! 13 | 14 | override func setUp() { 15 | super.setUp() 16 | self.continueAfterFailure = false 17 | self.base = JSONtoCodable() 18 | } 19 | 20 | func testEmpty() { 21 | let properties: [Property] = [] 22 | XCTAssertNil(self.base.merge(properties)) 23 | } 24 | 25 | func testSingle() { 26 | let property: Property = Property("Result") 27 | let properties: [Property] = [property] 28 | let expectation: Property = property 29 | let result = self.base.merge(properties) 30 | XCTAssertNotNil(result) 31 | XCTAssertEqual(result!, expectation) 32 | } 33 | 34 | func testSameProperties() { 35 | let property: Property = Property("Result") 36 | let properties: [Property] = [Property](repeating: property, count: 5) 37 | let expectation: Property = property 38 | let result = self.base.merge(properties) 39 | XCTAssertNotNil(result) 40 | XCTAssertEqual(result!, expectation) 41 | } 42 | 43 | func testSameJSONProperties() { 44 | let json1: String = """ 45 | { 46 | "a": 1, 47 | "b": 2 48 | } 49 | """ 50 | let json2: String = """ 51 | { 52 | "a": 1, 53 | "b": 2 54 | } 55 | """ 56 | let prop1 = try? self.base.generateProperty(json1) 57 | let prop2 = try? self.base.generateProperty(json2) 58 | XCTAssertNotNil(prop1) 59 | XCTAssertNotNil(prop2) 60 | let properties: [Property] = [prop1!, prop2!] 61 | let expectation: Property = prop1! 62 | let result = self.base.merge(properties) 63 | XCTAssertNotNil(result) 64 | XCTAssertEqual(result!, expectation) 65 | } 66 | 67 | func testDifferentJSONProperties() { 68 | let json1: String = """ 69 | { 70 | "a": 1, 71 | "b": 2 72 | } 73 | """ 74 | let json2: String = """ 75 | { 76 | "a": 1, 77 | "c": 3 78 | } 79 | """ 80 | let prop1 = try? self.base.generateProperty(json1) 81 | let prop2 = try? self.base.generateProperty(json2) 82 | XCTAssertNotNil(prop1) 83 | XCTAssertNotNil(prop2) 84 | let properties: [Property] = [prop1!, prop2!] 85 | let expectation: Property = prop1! 86 | let result = self.base.merge(properties) 87 | XCTAssertNotNil(result) 88 | XCTAssertEqual(result!, expectation) 89 | let resultText: String = self.base.createStructScope(result!) 90 | let expectationText: String = """ 91 | public struct Result: Codable { 92 | public let a: Int 93 | public let b: Int? 94 | public let c: Int? 95 | } 96 | """ 97 | XCTAssertEqual(resultText, expectationText) 98 | } 99 | 100 | func testDifferentJSONPropertiesWithReturnNonOptionals() { 101 | let json1: String = """ 102 | { 103 | "a": 1, 104 | "b": 2, 105 | "d": 4 106 | } 107 | """ 108 | let json2: String = """ 109 | { 110 | "a": 1, 111 | "c": 3, 112 | "d": 4 113 | } 114 | """ 115 | let prop1 = try? self.base.generateProperty(json1) 116 | let prop2 = try? self.base.generateProperty(json2) 117 | XCTAssertNotNil(prop1) 118 | XCTAssertNotNil(prop2) 119 | let properties: [Property] = [prop1!, prop2!] 120 | let expectation: Property = prop1! 121 | let result = self.base.merge(properties) 122 | XCTAssertNotNil(result) 123 | XCTAssertEqual(result!, expectation) 124 | let resultText: String = self.base.createStructScope(result!) 125 | let expectationText: String = """ 126 | public struct Result: Codable { 127 | public let a: Int 128 | public let b: Int? 129 | public let c: Int? 130 | public let d: Int 131 | } 132 | """ 133 | XCTAssertEqual(resultText, expectationText) 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /Tests/JSONtoCodableTests/ReservedWordsTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ReservedWordsTests.swift 3 | // JSONtoCodable 4 | // 5 | // Created by Yuto Mizutani on 2018/09/25. 6 | // 7 | 8 | import XCTest 9 | @testable import JSONtoCodable 10 | 11 | /** 12 | ReservedWordsTests 13 | 14 | - seealso: 15 | https://github.com/apple/swift/blob/master/docs/archive/LangRefNew.rst#reserved-keywords 16 | */ 17 | class ReservedWordsTests: XCTestCase { 18 | var base: JSONtoCodable! 19 | 20 | override func setUp() { 21 | super.setUp() 22 | self.base = JSONtoCodable() 23 | } 24 | 25 | func testLowerDeclarationsAndTypeKeywords() { 26 | let json: String = """ 27 | { 28 | "class": "Hello, world!!" 29 | "extension": "Hello, world!!" 30 | "import": "Hello, world!!" 31 | "init": "Hello, world!!" 32 | "func": "Hello, world!!" 33 | "enum": "Hello, world!!" 34 | "protocol": "Hello, world!!" 35 | "struct": "Hello, world!!" 36 | "subscript": "Hello, world!!" 37 | "typealias": "Hello, world!!" 38 | "let": "Hello, world!!" 39 | "var": "Hello, world!!" 40 | "where": "Hello, world!!" 41 | } 42 | """ 43 | let expectation: String = """ 44 | public struct Result: Codable { 45 | public let `class`: String 46 | public let `extension`: String 47 | public let `import`: String 48 | public let `init`: String 49 | public let `func`: String 50 | public let `enum`: String 51 | public let `protocol`: String 52 | public let `struct`: String 53 | public let `subscript`: String 54 | public let `typealias`: String 55 | public let `let`: String 56 | public let `var`: String 57 | public let `where`: String 58 | } 59 | """ 60 | let result: String? = try? self.base.generate(json) 61 | XCTAssertEqual(result, expectation) 62 | } 63 | 64 | func testStatements() { 65 | let json: String = """ 66 | { 67 | "break": "Hello, world!!" 68 | "case": "Hello, world!!" 69 | "continue": "Hello, world!!" 70 | "default": "Hello, world!!" 71 | "do": "Hello, world!!" 72 | "else": "Hello, world!!" 73 | "if": "Hello, world!!" 74 | "in": "Hello, world!!" 75 | "for": "Hello, world!!" 76 | "return": "Hello, world!!" 77 | "switch": "Hello, world!!" 78 | "while": "Hello, world!!" 79 | } 80 | """ 81 | let expectation: String = """ 82 | public struct Result: Codable { 83 | public let `break`: String 84 | public let `case`: String 85 | public let `continue`: String 86 | public let `default`: String 87 | public let `do`: String 88 | public let `else`: String 89 | public let `if`: String 90 | public let `in`: String 91 | public let `for`: String 92 | public let `return`: String 93 | public let `switch`: String 94 | public let `while`: String 95 | } 96 | """ 97 | let result: String? = try? self.base.generate(json) 98 | XCTAssertEqual(result, expectation) 99 | } 100 | 101 | func testLowerExpressions() { 102 | let json: String = """ 103 | { 104 | "as": "Hello, world!!" 105 | "is": "Hello, world!!" 106 | "super": "Hello, world!!" 107 | "self": "Hello, world!!" 108 | } 109 | """ 110 | let expectation: String = """ 111 | public struct Result: Codable { 112 | public let `as`: String 113 | public let `is`: String 114 | public let `super`: String 115 | public let `self`: String 116 | } 117 | """ 118 | let result: String? = try? self.base.generate(json) 119 | XCTAssertEqual(result, expectation) 120 | } 121 | 122 | func testUpperExpressions() { 123 | let json: String = """ 124 | { 125 | "Self": "Hello, world!!" 126 | } 127 | """ 128 | let expectation: String = """ 129 | public struct Result: Codable { 130 | public let `Self`: String 131 | } 132 | """ 133 | self.base.config.caseType.variable = .pascal 134 | let result: String? = try? self.base.generate(json) 135 | XCTAssertEqual(result, expectation) 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![JSONtoCodable](https://github.com/YutoMizutani/JSONtoCodable/blob/media/media/logo_text.png?raw=true) 2 | 3 | [![Build Status](https://app.bitrise.io/app/869daca1801a29aa/status.svg?token=9lhf2DEdWQhg6AUaUqGXAA&branch=develop)](https://app.bitrise.io/app/869daca1801a29aa) 4 | [![CocoaPods](https://img.shields.io/cocoapods/p/JSONtoCodable.svg)](https://github.com/YutoMizutani/JSONtoCodable) 5 | [![MIT License](http://img.shields.io/badge/license-MIT-blue.svg?style=flat)](https://github.com/YutoMizutani/JSONtoCodable/blob/master/LICENSE) 6 | [![CocoaPods](https://img.shields.io/cocoapods/v/JSONtoCodable.svg)](https://github.com/YutoMizutani/JSONtoCodable) 7 | [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/YutoMizutani/JSONtoCodable) 8 | [![codecov](https://codecov.io/gh/YutoMizutani/JSONtoCodable/branch/master/graph/badge.svg)](https://codecov.io/gh/YutoMizutani/JSONtoCodable) 9 | 10 | **JSONtoCodable** is a generating tool from Raw JSON to Codable (Swift4) text written in Swift4. 11 | 12 | Qiita: [JSONからCodable化されたstructを自動生成するツールを作った話 - Qiita](https://qiita.com/YutoMizutani/items/106cae55091f26bba641) 13 | 14 | ![demo_macos.png](https://raw.githubusercontent.com/YutoMizutani/JSONtoCodable/media/media/demo_macos.png) 15 | 16 | ## TL;DR 17 | 18 | From JSON, 19 | 20 | ```json 21 | { 22 | "user": { 23 | "Name": "Yuto Mizutani" 24 | }, 25 | "lib": { 26 | "lib-name": "JSONtoCodable", 27 | "year": 2018, 28 | "version": "1.0.2", 29 | "released": "2018-09-22" 30 | }, 31 | "text": "Hello, world!!" 32 | } 33 | ``` 34 | 35 | to Codable. 36 | 37 | ```swift 38 | public struct Result: Codable { 39 | public let user: User 40 | public let lib: Lib 41 | public let text: String 42 | 43 | public struct User: Codable { 44 | public let name: String 45 | 46 | private enum CodingKeys: String, CodingKey { 47 | case name = "Name" 48 | } 49 | } 50 | 51 | public struct Lib: Codable { 52 | public let libName: String 53 | public let year: Int 54 | public let version: String 55 | public let released: String 56 | 57 | private enum CodingKeys: String, CodingKey { 58 | case libName = "lib-name" 59 | case year 60 | case version 61 | case released 62 | } 63 | } 64 | } 65 | ``` 66 | 67 | ## [JaCo (macOS App)](https://github.com/YutoMizutani/JSONtoCodable/tree/master/Apps/JaCo) 68 | 69 | ![demo_macos.png](https://raw.githubusercontent.com/YutoMizutani/JSONtoCodable/media/media/demo_macos.png) 70 | 71 | ## [jc (CLI App)](https://github.com/YutoMizutani/JSONtoCodable/tree/master/Apps/jc) 72 | 73 | ### Installation 74 | 75 | ``` 76 | $ brew tap YutoMizutani/jc 77 | $ brew install jc 78 | ``` 79 | 80 | ### Usage example 81 | 82 | ``` 83 | $ curl https://httpbin.org/get | jc 84 | ``` 85 | 86 | or generate *.swift* file, 87 | 88 | ``` 89 | $ curl https://httpbin.org/get | jc > Result.swift 90 | ``` 91 | 92 | ### Help command 93 | 94 | ``` 95 | $ jc -h 96 | ``` 97 | 98 | ### Screen shot 99 | 100 | ![demo_cli.png](https://raw.githubusercontent.com/YutoMizutani/JSONtoCodable/media/media/demo_cli.png) 101 | 102 | ## Support formats 103 | 104 | - Type 105 | - String 106 | - Bool 107 | - Int 108 | - Double 109 | - struct(s) 110 | - Optional 111 | - Array 112 | - Start array 113 | - Muptiple array 114 | - Arrayed objects 115 | - Optional array 116 | - Number of nested array and objects 117 | - Infinity 118 | - Number of spaces in entered JSON 119 | - 0 to infinity 120 | 121 | ## Translations 122 | 123 | |JSON Value|Swift Type| 124 | |:-:|:-:| 125 | |"text"|String| 126 | |true|Bool| 127 | |-10|Int| 128 | |1.0|Double| 129 | |null|\?| 130 | |(the others)|Any| 131 | 132 | ## Usage 133 | 134 | ```swift 135 | import JSONtoCodable 136 | 137 | let json: String = """ 138 | { 139 | "Hello": "Hello, world!!" 140 | } 141 | """ 142 | 143 | let jsonToCodable = JSONtoCodable() 144 | let codable = try? jsonToCodable.generate(json) 145 | 146 | print(codable) 147 | /* 148 | struct Result: Codable { 149 | let hello: String 150 | 151 | private enum CodingKeys: String, CodingKey { 152 | case hello = "Hello" 153 | } 154 | } 155 | */ 156 | ``` 157 | 158 | ## Config 159 | 160 | ```swift 161 | let config = Config() 162 | config.name = "Result" // struct Result: Codable {} 163 | config.accessModifier = AccessModifier.public // public struct 164 | config.caseType = (variable: CaseType.camel, struct: CaseType.pascal) 165 | config.lineType = LineType.lineFeed 166 | config.indentType = IndentType.space(4) 167 | ``` 168 | 169 | [See more: Config.swift](https://github.com/YutoMizutani/JSONtoCodable/blob/master/Sources/Core/Config.swift) 170 | 171 | ## Installation 172 | 173 | #### Cocoapods 174 | 175 | Add this to your Podfile: 176 | 177 | ``` 178 | pod 'JSONtoCodable' 179 | ``` 180 | 181 | and 182 | 183 | ``` 184 | $ pod install 185 | ``` 186 | 187 | #### Carthage 188 | 189 | Add this to your Cartfile: 190 | 191 | ``` 192 | github "YutoMizutani/JSONtoCodable" 193 | ``` 194 | 195 | and 196 | 197 | ``` 198 | $ carthage update 199 | ``` 200 | 201 | ## License 202 | 203 | JSONtoCodable is available under the [MIT license](https://github.com/YutoMizutani/JSONtoCodable/blob/master/LICENSE). -------------------------------------------------------------------------------- /Sources/Extensions/String+.swift: -------------------------------------------------------------------------------- 1 | // 2 | // String+.swift 3 | // JSONtoCodable 4 | // 5 | // Created by Yuto Mizutani on 2018/09/19. 6 | // Copyright © 2018 Yuto Mizutani. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | extension String { 12 | func updateCased(with caseType: CaseType) -> String { 13 | return self.escapeSymbols().separated.joined(with: caseType).escapedByReservedWords() 14 | } 15 | 16 | private func changeCased() -> String { 17 | return (self as NSString).replacingOccurrences(of: "([A-Z])", 18 | with: "-$1", 19 | options: .regularExpression, 20 | range: NSRange(location: 0, length: count) 21 | ) 22 | .trimmingCharacters(in: .whitespacesAndNewlines) 23 | .capitalized 24 | } 25 | 26 | private var separated: [String] { 27 | let replaceWords: [Character] = ["-", "_", ":"] 28 | let last: Character = replaceWords.last! 29 | var text = self != self.uppercased() ? self.changeCased() : self 30 | Array(replaceWords.dropLast()).forEach { key in 31 | text = text.replacingOccurrences(of: String(key), with: String(last)) 32 | } 33 | return text.split(separator: last).map { String($0) } 34 | } 35 | 36 | func escaped() -> String { 37 | return "`\(self)`" 38 | } 39 | 40 | private func escapedByReservedWords() -> String { 41 | let declarationsAndTypeKeywords: [String] = [ 42 | "class", 43 | "extension", 44 | "import", 45 | "init", 46 | "func", 47 | "enum", 48 | "protocol", 49 | "struct", 50 | "subscript", 51 | "typealias", 52 | "let", 53 | "var", 54 | "where" 55 | ] 56 | let statements: [String] = [ 57 | "break", 58 | "case", 59 | "continue", 60 | "default", 61 | "do", 62 | "else", 63 | "if", 64 | "in", 65 | "for", 66 | "return", 67 | "switch", 68 | "while" 69 | ] 70 | let expressions: [String] = [ 71 | "as", 72 | "is", 73 | "super", 74 | "self", 75 | "Self", 76 | "__COLUMN__", 77 | "__FILE__", 78 | "__LINE__" 79 | ] 80 | 81 | return (declarationsAndTypeKeywords + statements + expressions).filter({ $0 == self }).isEmpty ? self : self.escaped() 82 | } 83 | 84 | private func escapeSymbols() -> String { 85 | let symbols: [String] = ["@"] 86 | var escaped: String = self 87 | symbols.forEach { escaped = escaped.replacingOccurrences(of: $0, with: "") } 88 | return escaped 89 | } 90 | 91 | fileprivate func optional() -> String { 92 | return self.last != "?" ? "\(self)?" : self 93 | } 94 | } 95 | 96 | extension Array where Element == String { 97 | private func optionalAll() -> [String] { 98 | return self.map { $0.optional() } 99 | } 100 | } 101 | 102 | extension Array where Element == [String] { 103 | func merge() -> [String] { 104 | guard !self.isEmpty else { return [] } 105 | guard self.count != 1 else { return self[0] } 106 | let array = self.map { NSOrderedSet(array: $0).array as? [String] ?? [] } 107 | 108 | // Could not allow empty 109 | guard array.filter({ $0.isEmpty }).isEmpty else { 110 | return array.filter({ !$0.isEmpty }).merge() 111 | } 112 | 113 | var rawResult: [String] = [] 114 | for a in array { 115 | var stack: [String] = [] 116 | for t in a { 117 | stack.append(t) 118 | if let index = rawResult.index(of: t) { 119 | rawResult.insert(contentsOf: stack, at: index) 120 | stack = [] 121 | } 122 | } 123 | rawResult += stack 124 | } 125 | 126 | return NSOrderedSet(array: rawResult).array as? [String] ?? [] 127 | } 128 | 129 | func mergeWithOptional() -> [String] { 130 | guard !self.isEmpty else { return [] } 131 | guard self.count != 1 else { return self[0] } 132 | let count = self.count 133 | let array = self.map { NSOrderedSet(array: $0).array as? [String] ?? [] } 134 | 135 | // Could not allow empty 136 | guard array.filter({ $0.isEmpty }).isEmpty else { 137 | return array.filter({ !$0.isEmpty }).mergeWithOptional().optionalAll() 138 | } 139 | 140 | var rawResult: [String] = [] 141 | for a in array { 142 | var stack: [String] = [] 143 | for t in a { 144 | stack.append(t) 145 | if let index = rawResult.index(of: t) { 146 | rawResult.insert(contentsOf: stack, at: index) 147 | stack = [] 148 | } 149 | } 150 | rawResult += stack 151 | } 152 | 153 | var result: [String] = NSOrderedSet(array: rawResult).array as? [String] ?? [] 154 | result = result.map { r in rawResult.filter({ $0 == r }).count == count ? r : r.optional() } 155 | return result 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /Apps/jc/Sources/jc/Help.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Help.swift 3 | // jc 4 | // 5 | // Created by Yuto Mizutani on 2018/10/22. 6 | // Copyright © 2018 Yuto Mizutani. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import JSONtoCodable 11 | 12 | func help() { 13 | print(""" 14 | jc - JSONtoCodable 15 | Usage: jc [options] 16 | JSONtoCodable is a generating tool from Raw JSON to Codable output. 17 | 18 | Options: 19 | --name, -n Output struct name 20 | --access-modifier, -a Access modifer 21 | --case-variable, -cv Case type for variable 22 | --case-struct, -cs Case type for struct 23 | --line-type, -l Line type 24 | --indent-type, -i Indent type 25 | 26 | Further help: 27 | jc help [OPTION] 28 | """) 29 | } 30 | 31 | 32 | func helpCommand(_ command: String) { 33 | let defaultConfig = JSONtoCodable().config 34 | 35 | switch command { 36 | 37 | // Help 38 | case "-h", "help": 39 | help() 40 | exit(EXIT_SUCCESS) 41 | 42 | // Struct name 43 | case "-n", "--name": 44 | print(""" 45 | jc \(command) formula 46 | Output the name top of struct 47 | The default is `\(defaultConfig.name)` 48 | 49 | EXAMPLE: 50 | jc \(command) Hello 51 | 52 | OUTPUT: 53 | struct Hello: Codable { ... } 54 | """) 55 | 56 | // AccessModifier 57 | case "-a", "--access-modifier": 58 | print(""" 59 | jc \(command) formula 60 | Output the access modifer 61 | The default is `\(defaultConfig.accessModifier.parameterString)` 62 | 63 | PARAMETERS: 64 | default, d DEFAULT (empty) 65 | private, pr private 66 | fileprivate, f fileprivate 67 | internal, i internal 68 | public, pu public 69 | open, o open 70 | 71 | EXAMPLE: 72 | jc \(command) private 73 | 74 | OUTPUT: 75 | private struct \(defaultConfig.name): Codable { ... } 76 | """) 77 | 78 | // caseType: variable 79 | case "-cv", "--case-variable": 80 | print(""" 81 | jc \(command) formula 82 | Output case pattern for variables 83 | The default is `\(defaultConfig.caseType.variable.parameterString)` 84 | 85 | PARAMETERS: 86 | pascal, p PascalCase 87 | camel, c camelCase 88 | snake, sn snake_case 89 | screaming-snake, ss SCREAMING_SNAKE_CASE 90 | 91 | EXAMPLE: 92 | jc \(command) camel 93 | 94 | OUTPUT: 95 | struct \(defaultConfig.name): Codable { 96 | var exampleString: String 97 | } 98 | """) 99 | 100 | // CaseType: struct 101 | case "-cs", "--case-struct": 102 | print(""" 103 | jc \(command) formula 104 | Output case pattern for structs 105 | The default is `\(defaultConfig.caseType.struct.parameterString)` 106 | 107 | PARAMETERS: 108 | pascal, p PascalCase 109 | camel, c camelCase 110 | snake, sn snake_case 111 | screaming-snake, ss SCREAMING_SNAKE_CASE 112 | 113 | EXAMPLE: 114 | jc \(command) screaming-snake 115 | 116 | OUTPUT: 117 | struct \([defaultConfig.name].joined(with: .screamingSnake)): Codable { ... } 118 | """) 119 | 120 | // LineType 121 | case "-l", "--line-type": 122 | print(""" 123 | jc \(command) formula 124 | Output line type 125 | The default is `\(defaultConfig.lineType.parameterString)` 126 | 127 | PARAMETERS: 128 | line-feed, \\n, n Line feed (LF) 129 | carriage-return, \\r, r Carriage return (CR) 130 | both, \\r\\n, rn Both (CRLF) 131 | 132 | EXAMPLE: 133 | jc \(command) carriage-return 134 | 135 | OUTPUT: 136 | struct \(defaultConfig.name): Codable {\\r ... \\r} 137 | """) 138 | 139 | // IndentType 140 | case "-i", "--indent-type": 141 | print(""" 142 | jc \(command) formula 143 | Output indent type 144 | The default is `\(defaultConfig.indentType.parameterString)` 145 | 146 | PARAMETERS: 147 | sX Spaces X times 148 | tX Tabs X times 149 | 150 | EXAMPLE: 151 | jc \(command) t4 152 | 153 | OUTPUT: 154 | struct \([defaultConfig.name].joined(with: .screamingSnake)): Codable { 155 | \\t\\t\\t\\tvar exampleString: String 156 | } 157 | """) 158 | 159 | default: 160 | print("Error: Unknown command: \(command)") 161 | exit(EXIT_FAILURE) 162 | } 163 | } 164 | 165 | -------------------------------------------------------------------------------- /Tests/JSONtoCodableTests/IndentTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // IndentTests.swift 3 | // JSONtoCodable 4 | // 5 | // Created by Yuto Mizutani on 2018/09/20. 6 | // 7 | 8 | import XCTest 9 | @testable import JSONtoCodable 10 | 11 | class IndentTests: XCTestCase { 12 | var base: JSONtoCodable! 13 | 14 | override func setUp() { 15 | super.setUp() 16 | self.base = JSONtoCodable() 17 | } 18 | 19 | func testDefault() { 20 | let json: String = """ 21 | { 22 | "Hello": "Hello, world!!", 23 | "HelloWorld": "Hello, world!!" 24 | } 25 | """ 26 | let expectation: String = """ 27 | public struct Result: Codable { 28 | public let hello: String 29 | public let helloWorld: String 30 | 31 | private enum CodingKeys: String, CodingKey { 32 | case hello = "Hello" 33 | case helloWorld = "HelloWorld" 34 | } 35 | } 36 | """ 37 | let result: String? = try? self.base.generate(json) 38 | XCTAssertEqual(result, expectation) 39 | } 40 | 41 | func testZeroSpace() { 42 | let json: String = """ 43 | { 44 | "Hello": "Hello, world!!", 45 | "HelloWorld": "Hello, world!!" 46 | } 47 | """ 48 | let expectation: String = """ 49 | public struct Result: Codable { 50 | public let hello: String 51 | public let helloWorld: String 52 | 53 | private enum CodingKeys: String, CodingKey { 54 | case hello = "Hello" 55 | case helloWorld = "HelloWorld" 56 | } 57 | } 58 | """ 59 | self.base.config.indentType = .space(0) 60 | let result: String? = try? self.base.generate(json) 61 | XCTAssertEqual(result, expectation) 62 | } 63 | 64 | func testTwoSpace() { 65 | let json: String = """ 66 | { 67 | "Hello": "Hello, world!!", 68 | "HelloWorld": "Hello, world!!" 69 | } 70 | """ 71 | let expectation: String = """ 72 | public struct Result: Codable { 73 | public let hello: String 74 | public let helloWorld: String 75 | 76 | private enum CodingKeys: String, CodingKey { 77 | case hello = "Hello" 78 | case helloWorld = "HelloWorld" 79 | } 80 | } 81 | """ 82 | self.base.config.indentType = .space(2) 83 | let result: String? = try? self.base.generate(json) 84 | XCTAssertEqual(result, expectation) 85 | } 86 | 87 | func testFourSpace() { 88 | let json: String = """ 89 | { 90 | "Hello": "Hello, world!!", 91 | "HelloWorld": "Hello, world!!" 92 | } 93 | """ 94 | let expectation: String = """ 95 | public struct Result: Codable { 96 | public let hello: String 97 | public let helloWorld: String 98 | 99 | private enum CodingKeys: String, CodingKey { 100 | case hello = "Hello" 101 | case helloWorld = "HelloWorld" 102 | } 103 | } 104 | """ 105 | self.base.config.indentType = .space(4) 106 | let result: String? = try? self.base.generate(json) 107 | XCTAssertEqual(result, expectation) 108 | } 109 | 110 | func testEightSpace() { 111 | let json: String = """ 112 | { 113 | "Hello": "Hello, world!!", 114 | "HelloWorld": "Hello, world!!" 115 | } 116 | """ 117 | let expectation: String = """ 118 | public struct Result: Codable { 119 | public let hello: String 120 | public let helloWorld: String 121 | 122 | private enum CodingKeys: String, CodingKey { 123 | case hello = "Hello" 124 | case helloWorld = "HelloWorld" 125 | } 126 | } 127 | """ 128 | self.base.config.indentType = .space(8) 129 | let result: String? = try? self.base.generate(json) 130 | XCTAssertEqual(result, expectation) 131 | } 132 | 133 | func testZeroTab() { 134 | let json: String = """ 135 | { 136 | "Hello": "Hello, world!!", 137 | "HelloWorld": "Hello, world!!" 138 | } 139 | """ 140 | let expectation: String = """ 141 | public struct Result: Codable { 142 | public let hello: String 143 | public let helloWorld: String 144 | 145 | private enum CodingKeys: String, CodingKey { 146 | case hello = "Hello" 147 | case helloWorld = "HelloWorld" 148 | } 149 | } 150 | """ 151 | self.base.config.indentType = .tab(0) 152 | let result: String? = try? self.base.generate(json) 153 | XCTAssertEqual(result, expectation) 154 | } 155 | 156 | func testOneTab() { 157 | let json: String = """ 158 | { 159 | "Hello": "Hello, world!!", 160 | "HelloWorld": "Hello, world!!" 161 | } 162 | """ 163 | let expectation: String = """ 164 | public struct Result: Codable { 165 | \tpublic let hello: String 166 | \tpublic let helloWorld: String 167 | 168 | \tprivate enum CodingKeys: String, CodingKey { 169 | \t\tcase hello = "Hello" 170 | \t\tcase helloWorld = "HelloWorld" 171 | \t} 172 | } 173 | """ 174 | self.base.config.indentType = .tab(1) 175 | let result: String? = try? self.base.generate(json) 176 | XCTAssertEqual(result, expectation) 177 | } 178 | 179 | func testTwoTab() { 180 | let json: String = """ 181 | { 182 | "Hello": "Hello, world!!", 183 | "HelloWorld": "Hello, world!!" 184 | } 185 | """ 186 | let expectation: String = """ 187 | public struct Result: Codable { 188 | \t\tpublic let hello: String 189 | \t\tpublic let helloWorld: String 190 | 191 | \t\tprivate enum CodingKeys: String, CodingKey { 192 | \t\t\t\tcase hello = "Hello" 193 | \t\t\t\tcase helloWorld = "HelloWorld" 194 | \t\t} 195 | } 196 | """ 197 | self.base.config.indentType = .tab(2) 198 | let result: String? = try? self.base.generate(json) 199 | XCTAssertEqual(result, expectation) 200 | } 201 | } 202 | -------------------------------------------------------------------------------- /Sources/Enum/Type.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Type.swift 3 | // JSONtoCodable 4 | // 5 | // Created by Yuto Mizutani on 2018/09/20. 6 | // 7 | 8 | import Foundation 9 | 10 | public enum Type: Hashable { 11 | case string 12 | case bool 13 | case int 14 | case double 15 | case any 16 | case `struct`(String) 17 | 18 | case stringArray 19 | case boolArray 20 | case intArray 21 | case doubleArray 22 | case anyArray 23 | case structArray(String) 24 | 25 | case optionalString 26 | case optionalBool 27 | case optionalInt 28 | case optionalDouble 29 | case optionalAny 30 | case optionalStruct(String) 31 | 32 | case optionalStringArray 33 | case optionalBoolArray 34 | case optionalIntArray 35 | case optionalDoubleArray 36 | case optionalAnyArray 37 | case optionalStructArray(String) 38 | } 39 | 40 | public extension Type { 41 | var order: Int { 42 | switch self { 43 | case .string: 44 | return 0 45 | case .bool: 46 | return 1 47 | case .int: 48 | return 2 49 | case .double: 50 | return 3 51 | case .any: 52 | return 4 53 | case .struct: 54 | return 5 55 | 56 | case .stringArray: 57 | return 6 58 | case .boolArray: 59 | return 7 60 | case .intArray: 61 | return 8 62 | case .doubleArray: 63 | return 9 64 | case .anyArray: 65 | return 10 66 | case .structArray: 67 | return 11 68 | 69 | case .optionalString: 70 | return 12 71 | case .optionalBool: 72 | return 13 73 | case .optionalInt: 74 | return 14 75 | case .optionalDouble: 76 | return 15 77 | case .optionalAny: 78 | return 16 79 | case .optionalStruct: 80 | return 17 81 | 82 | case .optionalStringArray: 83 | return 18 84 | case .optionalBoolArray: 85 | return 19 86 | case .optionalIntArray: 87 | return 20 88 | case .optionalDoubleArray: 89 | return 21 90 | case .optionalAnyArray: 91 | return 22 92 | case .optionalStructArray: 93 | return 23 94 | } 95 | } 96 | 97 | var rawValue: String { 98 | switch self { 99 | case .string: 100 | return "String" 101 | case .bool: 102 | return "Bool" 103 | case .int: 104 | return "Int" 105 | case .double: 106 | return "Double" 107 | case .any: 108 | return "Any" 109 | case .struct(let v): 110 | return v 111 | 112 | case .stringArray: 113 | return "[\(Type.string.rawValue)]" 114 | case .boolArray: 115 | return "[\(Type.bool.rawValue)]" 116 | case .intArray: 117 | return "[\(Type.int.rawValue)]" 118 | case .doubleArray: 119 | return "[\(Type.double.rawValue)]" 120 | case .anyArray: 121 | return "[\(Type.any.rawValue)]" 122 | case .structArray(let v): 123 | return "[\(Type.struct(v).rawValue)]" 124 | 125 | case .optionalString: 126 | return "\(Type.string.rawValue)?" 127 | case .optionalBool: 128 | return "\(Type.bool.rawValue)?" 129 | case .optionalInt: 130 | return "\(Type.int.rawValue)?" 131 | case .optionalDouble: 132 | return "\(Type.double.rawValue)?" 133 | case .optionalAny: 134 | return "\(Type.any.rawValue)?" 135 | case .optionalStruct(let v): 136 | return "\(Type.struct(v).rawValue)?" 137 | 138 | case .optionalStringArray: 139 | return "[\(Type.optionalString.rawValue)]" 140 | case .optionalBoolArray: 141 | return "[\(Type.optionalBool.rawValue)]" 142 | case .optionalIntArray: 143 | return "[\(Type.optionalInt.rawValue)]" 144 | case .optionalDoubleArray: 145 | return "[\(Type.optionalDouble.rawValue)]" 146 | case .optionalAnyArray: 147 | return "[\(Type.optionalAny.rawValue)]" 148 | case .optionalStructArray(let v): 149 | return "[\(Type.optionalStruct(v).rawValue)]" 150 | } 151 | } 152 | 153 | var isOptional: Bool { 154 | return self.order >= Type.optionalString.order 155 | } 156 | 157 | func optional() -> Type { 158 | switch self { 159 | case .string, .optionalString: 160 | return .optionalString 161 | case .bool, .optionalBool: 162 | return .optionalBool 163 | case .int, .optionalInt: 164 | return .optionalInt 165 | case .double, .optionalDouble: 166 | return .optionalDouble 167 | case .any, .optionalAny: 168 | return .optionalAny 169 | case .struct(let v), .optionalStruct(let v): 170 | return .optionalStruct(v) 171 | 172 | case .stringArray, .optionalStringArray: 173 | return .optionalStringArray 174 | case .boolArray, .optionalBoolArray: 175 | return .optionalBoolArray 176 | case .intArray, .optionalIntArray: 177 | return .optionalIntArray 178 | case .doubleArray, .optionalDoubleArray: 179 | return .optionalDoubleArray 180 | case .anyArray, .optionalAnyArray: 181 | return .optionalAnyArray 182 | case .structArray(let v), .optionalStructArray(let v): 183 | return .optionalStructArray(v) 184 | } 185 | } 186 | 187 | func toArray() -> Type { 188 | switch self { 189 | case .string, .stringArray: 190 | return .stringArray 191 | case .bool, .boolArray: 192 | return .boolArray 193 | case .int, .intArray: 194 | return .intArray 195 | case .double, .doubleArray: 196 | return .doubleArray 197 | case .any, .anyArray: 198 | return .anyArray 199 | case .struct(let v), .structArray(let v): 200 | return .structArray(v) 201 | 202 | case .optionalString, .optionalStringArray: 203 | return .optionalStringArray 204 | case .optionalBool, .optionalBoolArray: 205 | return .optionalBoolArray 206 | case .optionalInt, .optionalIntArray: 207 | return .optionalIntArray 208 | case .optionalDouble, .optionalDoubleArray: 209 | return .optionalDoubleArray 210 | case .optionalAny, .optionalAnyArray: 211 | return .optionalAnyArray 212 | case .optionalStruct(let v), .optionalStructArray(let v): 213 | return .optionalStructArray(v) 214 | } 215 | } 216 | } 217 | 218 | public extension Array where Element == Type { 219 | func unique() -> [Type] { 220 | return Array(Set(self)).sorted(by: { $0.order < $1.order }) 221 | } 222 | 223 | func hasOptional() -> Bool { 224 | return !self.filter({ $0.isOptional }).isEmpty 225 | } 226 | 227 | func sumType() -> Type { 228 | let types: [Type] = self.unique() 229 | switch types.count { 230 | case 0: 231 | return .optionalAnyArray 232 | case 1: 233 | return types[0].toArray() 234 | case 2: 235 | if types[0].order < Type.optionalString.order && 236 | types[1].order - types[0].order == Type.optionalString.order { 237 | return types[1].toArray() 238 | } else { 239 | fallthrough 240 | } 241 | default: 242 | return self.hasOptional() ? Type.optionalAnyArray : Type.anyArray 243 | } 244 | } 245 | } 246 | -------------------------------------------------------------------------------- /Tests/JSONtoCodableTests/JSONtoCodableTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // JSONtoCodableTests.swift 3 | // JSONtoCodable 4 | // 5 | // Created by Yuto Mizutani on 2018/09/21. 6 | // 7 | 8 | import XCTest 9 | @testable import JSONtoCodable 10 | 11 | class JSONtoCodableTests: XCTestCase { 12 | var base: JSONtoCodable! 13 | 14 | override func setUp() { 15 | super.setUp() 16 | self.base = JSONtoCodable() 17 | } 18 | 19 | func testDecisionType() { 20 | XCTAssertEqual(self.base.decisionType("\"True\""), .string) 21 | XCTAssertEqual(self.base.decisionType("\"false\""), .string) 22 | XCTAssertEqual(self.base.decisionType("\"0\""), .string) 23 | XCTAssertEqual(self.base.decisionType("\"-10\""), .string) 24 | XCTAssertEqual(self.base.decisionType("\"1000\""), .string) 25 | XCTAssertEqual(self.base.decisionType("\"1.0\""), .string) 26 | XCTAssertEqual(self.base.decisionType("\"-10.0\""), .string) 27 | XCTAssertEqual(self.base.decisionType("\"1000.5\""), .string) 28 | XCTAssertEqual(self.base.decisionType("\"null\""), .string) 29 | XCTAssertEqual(self.base.decisionType("\"NULL\""), .string) 30 | XCTAssertEqual(self.base.decisionType("\"Hello\""), .string) 31 | 32 | XCTAssertEqual(self.base.decisionType("True"), .bool) 33 | XCTAssertEqual(self.base.decisionType("false"), .bool) 34 | XCTAssertEqual(self.base.decisionType("0"), .int) 35 | XCTAssertEqual(self.base.decisionType("-10"), .int) 36 | XCTAssertEqual(self.base.decisionType("1000"), .int) 37 | XCTAssertEqual(self.base.decisionType("1.0"), .double) 38 | XCTAssertEqual(self.base.decisionType("-10.0"), .double) 39 | XCTAssertEqual(self.base.decisionType("1000.5"), .double) 40 | XCTAssertEqual(self.base.decisionType("null"), .optionalAny) 41 | XCTAssertEqual(self.base.decisionType("NULL"), .optionalAny) 42 | XCTAssertEqual(self.base.decisionType("Hello"), .any) 43 | } 44 | 45 | func testCreateStructproperty() { 46 | var key: String 47 | var expectation: String 48 | 49 | key = "HelloWorld" 50 | XCTAssertEqual(Property(key).suffix, "}") 51 | 52 | key = "HelloWorld" 53 | expectation = "struct HelloWorld: Codable {" 54 | XCTAssertEqual(Property(key).prefix, expectation) 55 | key = "HelloWorld" 56 | expectation = "fileprivate struct HelloWorld: Codable {" 57 | XCTAssertEqual(Property(key, accessModifier: .fileprivate).prefix, expectation) 58 | 59 | // NOTE: There will occur compile errors when create the .swift file, but it is not interested this method 60 | key = "1234" 61 | expectation = "open struct 1234: Codable {" 62 | XCTAssertEqual(Property(key, accessModifier: .open).prefix, expectation) 63 | } 64 | 65 | func testCreateImmutable() { 66 | var seed: (key: String, value: String, type: Type?) 67 | var expectation: String 68 | self.base.config.accessModifier = .default 69 | 70 | seed = ("hello", "", .string) 71 | expectation = "let hello: String" 72 | XCTAssertEqual(self.base.createImmutable(seed), expectation) 73 | seed = ("hello", "", .bool) 74 | expectation = "let hello: Bool" 75 | XCTAssertEqual(self.base.createImmutable(seed), expectation) 76 | seed = ("hello", "", .int) 77 | expectation = "let hello: Int" 78 | XCTAssertEqual(self.base.createImmutable(seed), expectation) 79 | seed = ("hello", "", .double) 80 | expectation = "let hello: Double" 81 | XCTAssertEqual(self.base.createImmutable(seed), expectation) 82 | seed = ("hello", "", .optionalAny) 83 | expectation = "let hello: Any?" 84 | XCTAssertEqual(self.base.createImmutable(seed), expectation) 85 | seed = ("hello", "", .any) 86 | expectation = "let hello: Any" 87 | XCTAssertEqual(self.base.createImmutable(seed), expectation) 88 | 89 | seed = ("HelloWorld", "", .struct("helloWorld")) 90 | expectation = "let HelloWorld: helloWorld" 91 | self.base.config.caseType = (variable: CaseType.pascal, struct: CaseType.camel) 92 | XCTAssertEqual(self.base.createImmutable(seed), expectation) 93 | seed = ("HelloWorld", "", .struct("hello_world")) 94 | expectation = "let HELLO_WORLD: hello_world" 95 | self.base.config.caseType = (variable: CaseType.screamingSnake, struct: CaseType.snake) 96 | XCTAssertEqual(self.base.createImmutable(seed), expectation) 97 | seed = ("HELLO_WORLD", "", .struct("HELLO_WORLD")) 98 | expectation = "let helloWorld: HELLO_WORLD" 99 | self.base.config.caseType = (variable: CaseType.camel, struct: CaseType.screamingSnake) 100 | XCTAssertEqual(self.base.createImmutable(seed), expectation) 101 | seed = ("HELLO_WORLD", "", .struct("HelloWorld")) 102 | expectation = "let hello_world: HelloWorld" 103 | self.base.config.caseType = (variable: CaseType.snake, struct: CaseType.pascal) 104 | XCTAssertEqual(self.base.createImmutable(seed), expectation) 105 | 106 | // NOTE: There will occur compile errors when create the .swift file, but it is not interested this method 107 | seed = ("HelloWorld", "", .struct("HelloWorld")) 108 | expectation = "let HelloWorld: HelloWorld" 109 | self.base.config.caseType = (variable: CaseType.pascal, struct: CaseType.pascal) 110 | XCTAssertEqual(self.base.createImmutable(seed), expectation) 111 | seed = ("1234", "", .struct("1234")) 112 | expectation = "let 1234: 1234" 113 | self.base.config.caseType = (variable: CaseType.camel, struct: CaseType.pascal) 114 | XCTAssertEqual(self.base.createImmutable(seed), expectation) 115 | } 116 | 117 | func testCreateCodingKey() { 118 | let jsonKey: String = "HelloWorld" 119 | var expectation: String 120 | 121 | expectation = "case HelloWorld" 122 | self.base.config.caseType.variable = .pascal 123 | XCTAssertEqual(self.base.createCodingKey(jsonKey), expectation) 124 | expectation = "case helloWorld = \"HelloWorld\"" 125 | self.base.config.caseType.variable = .camel 126 | XCTAssertEqual(self.base.createCodingKey(jsonKey), expectation) 127 | expectation = "case hello_world = \"HelloWorld\"" 128 | self.base.config.caseType.variable = .snake 129 | XCTAssertEqual(self.base.createCodingKey(jsonKey), expectation) 130 | expectation = "case HELLO_WORLD = \"HelloWorld\"" 131 | self.base.config.caseType.variable = .screamingSnake 132 | XCTAssertEqual(self.base.createCodingKey(jsonKey), expectation) 133 | } 134 | 135 | func testCreateCodingKeyScope() { 136 | var keys: [String] 137 | var expectation: String 138 | 139 | let hello = "case hello = \"Hello\"" 140 | keys = Array(repeating: hello, count: 1) 141 | expectation = """ 142 | private enum CodingKeys: String, CodingKey { 143 | case hello = "Hello" 144 | } 145 | """ 146 | XCTAssertEqual(self.base.createCodingKeyScope(keys), expectation) 147 | keys = Array(repeating: hello, count: 2) 148 | expectation = """ 149 | private enum CodingKeys: String, CodingKey { 150 | case hello = "Hello" 151 | case hello = "Hello" 152 | } 153 | """ 154 | XCTAssertEqual(self.base.createCodingKeyScope(keys), expectation) 155 | keys = Array(repeating: hello, count: 3) 156 | expectation = """ 157 | private enum CodingKeys: String, CodingKey { 158 | case hello = "Hello" 159 | case hello = "Hello" 160 | case hello = "Hello" 161 | } 162 | """ 163 | XCTAssertEqual(self.base.createCodingKeyScope(keys), expectation) 164 | } 165 | 166 | func testCreateStructScope() { 167 | let structTitle: String = "Result" 168 | var values: [String] 169 | var property: Property 170 | var expectation: String 171 | 172 | self.base.config.accessModifier = .default 173 | values = ["Single1", "Single2"] 174 | property = Property(structTitle) 175 | property.immutables = values.map { (key: $0, "", type: .string) }.map { self.base.createImmutable($0)! } 176 | property.codingKeys = values.map { self.base.createCodingKey($0) } 177 | expectation = """ 178 | struct Result: Codable { 179 | let single1: String 180 | let single2: String 181 | 182 | private enum CodingKeys: String, CodingKey { 183 | case single1 = "Single1" 184 | case single2 = "Single2" 185 | } 186 | } 187 | """ 188 | XCTAssertEqual(self.base.createStructScope(property), expectation) 189 | 190 | self.base.config.accessModifier = .default 191 | values = ["single1", "single2", "single3"] 192 | property = Property(structTitle) 193 | property.immutables = values.map { (key: $0, "", type: .string) }.map { self.base.createImmutable($0)! } 194 | expectation = """ 195 | struct Result: Codable { 196 | let single1: String 197 | let single2: String 198 | let single3: String 199 | } 200 | """ 201 | XCTAssertEqual(self.base.createStructScope(property), expectation) 202 | 203 | var internalStructString: String 204 | 205 | self.base.config.accessModifier = .default 206 | values = ["Single1", "Single2", "Single3"] 207 | property = Property(structTitle) 208 | internalStructString = "struct Single2: Codable {\n let double1: String\n\n private enum CodingKeys: String, CodingKey {\n case double1 = \"Double1\"\n }\n}" 209 | property = Property(structTitle) 210 | property.structs = [internalStructString] 211 | property.immutables = values.map { (key: $0, "", type: $0 != "Single2" ? .string : .struct("Single2")) as JSONtoCodable.RawJSON }.map { self.base.createImmutable($0)! } 212 | property.codingKeys = values.map { self.base.createCodingKey($0) } 213 | expectation = """ 214 | struct Result: Codable { 215 | let single1: String 216 | let single2: Single2 217 | let single3: String 218 | 219 | struct Single2: Codable { 220 | let double1: String 221 | 222 | private enum CodingKeys: String, CodingKey { 223 | case double1 = "Double1" 224 | } 225 | } 226 | 227 | private enum CodingKeys: String, CodingKey { 228 | case single1 = "Single1" 229 | case single2 = "Single2" 230 | case single3 = "Single3" 231 | } 232 | } 233 | """ 234 | XCTAssertEqual(self.base.createStructScope(property), expectation) 235 | 236 | self.base.config.accessModifier = .default 237 | values = ["Single1", "Single2", "Single3"] 238 | property = Property(structTitle) 239 | internalStructString = "struct Single2: Codable {\n let double1: String\n let double2: Double2\n let double3: String\n\n struct Double2: Codable {\n let triple1: String\n\n private enum CodingKeys: String, CodingKey {\n case triple1 = \"Triple1\"\n }\n }\n\n private enum CodingKeys: String, CodingKey {\n case double1 = \"Double1\"\n case double2 = \"Double2\"\n case double3 = \"Double3\"\n }\n}" 240 | property = Property(structTitle) 241 | property.structs = [internalStructString] 242 | property.immutables = values.map { (key: $0, "", type: $0 != "Single2" ? .string : .struct("Single2")) as JSONtoCodable.RawJSON }.map { self.base.createImmutable($0)! } 243 | property.codingKeys = values.map { self.base.createCodingKey($0) } 244 | expectation = """ 245 | struct Result: Codable { 246 | let single1: String 247 | let single2: Single2 248 | let single3: String 249 | 250 | struct Single2: Codable { 251 | let double1: String 252 | let double2: Double2 253 | let double3: String 254 | 255 | struct Double2: Codable { 256 | let triple1: String 257 | 258 | private enum CodingKeys: String, CodingKey { 259 | case triple1 = "Triple1" 260 | } 261 | } 262 | 263 | private enum CodingKeys: String, CodingKey { 264 | case double1 = "Double1" 265 | case double2 = "Double2" 266 | case double3 = "Double3" 267 | } 268 | } 269 | 270 | private enum CodingKeys: String, CodingKey { 271 | case single1 = "Single1" 272 | case single2 = "Single2" 273 | case single3 = "Single3" 274 | } 275 | } 276 | """ 277 | XCTAssertEqual(self.base.createStructScope(property), expectation) 278 | } 279 | } 280 | -------------------------------------------------------------------------------- /Tests/JSONtoCodableTests/TypeTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TypeTests.swift 3 | // JSONtoCodable 4 | // 5 | // Created by Yuto Mizutani on 2018/09/20. 6 | // 7 | 8 | import XCTest 9 | @testable import JSONtoCodable 10 | 11 | class TypeTests: XCTestCase { 12 | var base: JSONtoCodable! 13 | 14 | override func setUp() { 15 | super.setUp() 16 | self.base = JSONtoCodable() 17 | } 18 | 19 | func testTypesOrder() { 20 | XCTAssertEqual(Type.string.order, 0) 21 | XCTAssertEqual(Type.bool.order, 1) 22 | XCTAssertEqual(Type.int.order, 2) 23 | XCTAssertEqual(Type.double.order, 3) 24 | XCTAssertEqual(Type.any.order, 4) 25 | XCTAssertEqual(Type.struct("Test").order, 5) 26 | 27 | XCTAssertEqual(Type.stringArray.order, 6) 28 | XCTAssertEqual(Type.boolArray.order, 7) 29 | XCTAssertEqual(Type.intArray.order, 8) 30 | XCTAssertEqual(Type.doubleArray.order, 9) 31 | XCTAssertEqual(Type.anyArray.order, 10) 32 | XCTAssertEqual(Type.structArray("Test").order, 11) 33 | 34 | XCTAssertEqual(Type.optionalString.order, 12) 35 | XCTAssertEqual(Type.optionalBool.order, 13) 36 | XCTAssertEqual(Type.optionalInt.order, 14) 37 | XCTAssertEqual(Type.optionalDouble.order, 15) 38 | XCTAssertEqual(Type.optionalAny.order, 16) 39 | XCTAssertEqual(Type.optionalStruct("Test").order, 17) 40 | 41 | XCTAssertEqual(Type.optionalStringArray.order, 18) 42 | XCTAssertEqual(Type.optionalBoolArray.order, 19) 43 | XCTAssertEqual(Type.optionalIntArray.order, 20) 44 | XCTAssertEqual(Type.optionalDoubleArray.order, 21) 45 | XCTAssertEqual(Type.optionalAnyArray.order, 22) 46 | XCTAssertEqual(Type.optionalStructArray("Test").order, 23) 47 | } 48 | 49 | func testRawValueTypes() { 50 | XCTAssertEqual(Type.string.rawValue, "String") 51 | XCTAssertEqual(Type.bool.rawValue, "Bool") 52 | XCTAssertEqual(Type.int.rawValue, "Int") 53 | XCTAssertEqual(Type.double.rawValue, "Double") 54 | XCTAssertEqual(Type.any.rawValue, "Any") 55 | XCTAssertEqual(Type.struct("Test").rawValue, "Test") 56 | 57 | XCTAssertEqual(Type.stringArray.rawValue, "[String]") 58 | XCTAssertEqual(Type.boolArray.rawValue, "[Bool]") 59 | XCTAssertEqual(Type.intArray.rawValue, "[Int]") 60 | XCTAssertEqual(Type.doubleArray.rawValue, "[Double]") 61 | XCTAssertEqual(Type.anyArray.rawValue, "[Any]") 62 | XCTAssertEqual(Type.structArray("Test").rawValue, "[Test]") 63 | 64 | XCTAssertEqual(Type.optionalString.rawValue, "String?") 65 | XCTAssertEqual(Type.optionalBool.rawValue, "Bool?") 66 | XCTAssertEqual(Type.optionalInt.rawValue, "Int?") 67 | XCTAssertEqual(Type.optionalDouble.rawValue, "Double?") 68 | XCTAssertEqual(Type.optionalAny.rawValue, "Any?") 69 | XCTAssertEqual(Type.optionalStruct("Test").rawValue, "Test?") 70 | 71 | XCTAssertEqual(Type.optionalStringArray.rawValue, "[String?]") 72 | XCTAssertEqual(Type.optionalBoolArray.rawValue, "[Bool?]") 73 | XCTAssertEqual(Type.optionalIntArray.rawValue, "[Int?]") 74 | XCTAssertEqual(Type.optionalDoubleArray.rawValue, "[Double?]") 75 | XCTAssertEqual(Type.optionalAnyArray.rawValue, "[Any?]") 76 | XCTAssertEqual(Type.optionalStructArray("Test").rawValue, "[Test?]") 77 | } 78 | 79 | func testTypesArray() { 80 | XCTAssertEqual(Type.string.toArray(), Type.stringArray) 81 | XCTAssertEqual(Type.bool.toArray(), Type.boolArray) 82 | XCTAssertEqual(Type.int.toArray(), Type.intArray) 83 | XCTAssertEqual(Type.double.toArray(), Type.doubleArray) 84 | XCTAssertEqual(Type.any.toArray(), Type.anyArray) 85 | XCTAssertEqual(Type.struct("Test").toArray(), Type.structArray("Test")) 86 | 87 | XCTAssertEqual(Type.stringArray.toArray(), Type.stringArray) 88 | XCTAssertEqual(Type.boolArray.toArray(), Type.boolArray) 89 | XCTAssertEqual(Type.intArray.toArray(), Type.intArray) 90 | XCTAssertEqual(Type.doubleArray.toArray(), Type.doubleArray) 91 | XCTAssertEqual(Type.anyArray.toArray(), Type.anyArray) 92 | XCTAssertEqual(Type.structArray("Test").toArray(), Type.structArray("Test")) 93 | 94 | XCTAssertEqual(Type.optionalString.toArray(), Type.optionalStringArray) 95 | XCTAssertEqual(Type.optionalBool.toArray(), Type.optionalBoolArray) 96 | XCTAssertEqual(Type.optionalInt.toArray(), Type.optionalIntArray) 97 | XCTAssertEqual(Type.optionalDouble.toArray(), Type.optionalDoubleArray) 98 | XCTAssertEqual(Type.optionalAny.toArray(), Type.optionalAnyArray) 99 | XCTAssertEqual(Type.optionalStruct("Test").toArray(), Type.optionalStructArray("Test")) 100 | 101 | XCTAssertEqual(Type.optionalStringArray.toArray(), Type.optionalStringArray) 102 | XCTAssertEqual(Type.optionalBoolArray.toArray(), Type.optionalBoolArray) 103 | XCTAssertEqual(Type.optionalIntArray.toArray(), Type.optionalIntArray) 104 | XCTAssertEqual(Type.optionalDoubleArray.toArray(), Type.optionalDoubleArray) 105 | XCTAssertEqual(Type.optionalAnyArray.toArray(), Type.optionalAnyArray) 106 | XCTAssertEqual(Type.optionalStructArray("Test").toArray(), Type.optionalStructArray("Test")) 107 | } 108 | 109 | func testOptionalTypes() { 110 | XCTAssertEqual(Type.string.optional(), Type.optionalString) 111 | XCTAssertEqual(Type.bool.optional(), Type.optionalBool) 112 | XCTAssertEqual(Type.int.optional(), Type.optionalInt) 113 | XCTAssertEqual(Type.double.optional(), Type.optionalDouble) 114 | XCTAssertEqual(Type.any.optional(), Type.optionalAny) 115 | XCTAssertEqual(Type.struct("Test").optional(), Type.optionalStruct("Test")) 116 | 117 | XCTAssertEqual(Type.stringArray.optional(), Type.optionalStringArray) 118 | XCTAssertEqual(Type.boolArray.optional(), Type.optionalBoolArray) 119 | XCTAssertEqual(Type.intArray.optional(), Type.optionalIntArray) 120 | XCTAssertEqual(Type.doubleArray.optional(), Type.optionalDoubleArray) 121 | XCTAssertEqual(Type.anyArray.optional(), Type.optionalAnyArray) 122 | XCTAssertEqual(Type.structArray("Test").optional(), Type.optionalStructArray("Test")) 123 | 124 | XCTAssertEqual(Type.optionalString.optional(), Type.optionalString) 125 | XCTAssertEqual(Type.optionalBool.optional(), Type.optionalBool) 126 | XCTAssertEqual(Type.optionalInt.optional(), Type.optionalInt) 127 | XCTAssertEqual(Type.optionalDouble.optional(), Type.optionalDouble) 128 | XCTAssertEqual(Type.optionalAny.optional(), Type.optionalAny) 129 | XCTAssertEqual(Type.optionalStruct("Test").optional(), Type.optionalStruct("Test")) 130 | 131 | XCTAssertEqual(Type.optionalStringArray.optional(), Type.optionalStringArray) 132 | XCTAssertEqual(Type.optionalBoolArray.optional(), Type.optionalBoolArray) 133 | XCTAssertEqual(Type.optionalIntArray.optional(), Type.optionalIntArray) 134 | XCTAssertEqual(Type.optionalDoubleArray.optional(), Type.optionalDoubleArray) 135 | XCTAssertEqual(Type.optionalAnyArray.optional(), Type.optionalAnyArray) 136 | XCTAssertEqual(Type.optionalStructArray("Test").optional(), Type.optionalStructArray("Test")) 137 | } 138 | 139 | func testUnique() { 140 | var result: [Type] 141 | var expectation: [Type] 142 | 143 | result = [.string, .string, .string, .string, .string] 144 | expectation = [.string] 145 | XCTAssertEqual(result.unique(), expectation) 146 | 147 | result = [.optionalIntArray, .string, .string] 148 | expectation = [.string, .optionalIntArray] 149 | XCTAssertEqual(result.unique(), expectation) 150 | 151 | result = [.optionalStruct("A"), .optionalStruct("B"), .optionalStructArray("C"), .optionalStructArray("D")] 152 | XCTAssertEqual(result.unique().count, result.count) 153 | } 154 | 155 | func testHasOptional() { 156 | var result: [Type] 157 | var expectation: Bool 158 | 159 | result = [ 160 | .string, .int, .double, .any, .struct("Test"), 161 | .stringArray, .intArray, .anyArray, .structArray("Test") 162 | ] 163 | expectation = false 164 | XCTAssertEqual(result.hasOptional(), expectation) 165 | 166 | result = [ 167 | .optionalString, .optionalInt, .optionalAny, .optionalStruct("Test"), 168 | .optionalStringArray, .optionalIntArray, .optionalAnyArray, .optionalStructArray("Test") 169 | ] 170 | expectation = true 171 | XCTAssertEqual(result.hasOptional(), expectation) 172 | } 173 | 174 | func testSumType() { 175 | var result: [Type] 176 | var expectation: Type 177 | 178 | result = [] 179 | expectation = .optionalAnyArray 180 | XCTAssertEqual(result.sumType(), expectation) 181 | 182 | result = [ 183 | .string, .string, .string, .string, .string 184 | ] 185 | expectation = .stringArray 186 | XCTAssertEqual(result.sumType(), expectation) 187 | 188 | result = [ 189 | .string, .string, .optionalString 190 | ] 191 | expectation = .optionalStringArray 192 | XCTAssertEqual(result.sumType(), expectation) 193 | 194 | result = [ 195 | .int, .int, .optionalInt 196 | ] 197 | expectation = .optionalIntArray 198 | XCTAssertEqual(result.sumType(), expectation) 199 | 200 | result = [ 201 | .boolArray, .boolArray, .boolArray 202 | ] 203 | expectation = .boolArray 204 | XCTAssertEqual(result.sumType(), expectation) 205 | 206 | result = [ 207 | .doubleArray, .doubleArray, .optionalDoubleArray 208 | ] 209 | expectation = .optionalDoubleArray 210 | XCTAssertEqual(result.sumType(), expectation) 211 | 212 | result = [ 213 | .string, .int, .double 214 | ] 215 | expectation = .anyArray 216 | XCTAssertEqual(result.sumType(), expectation) 217 | 218 | result = [ 219 | .string, .int, .optionalBool 220 | ] 221 | expectation = .optionalAnyArray 222 | XCTAssertEqual(result.sumType(), expectation) 223 | } 224 | 225 | func testTypes() { 226 | let json: String = """ 227 | { 228 | "string": "String", 229 | "bool": true, 230 | "int": 0, 231 | "double": 1.0, 232 | "any": any, 233 | "optionalany": null 234 | } 235 | """ 236 | let expectation: String = """ 237 | public struct Result: Codable { 238 | public let string: String 239 | public let bool: Bool 240 | public let int: Int 241 | public let double: Double 242 | public let any: Any 243 | public let optionalany: Any? 244 | } 245 | """ 246 | let result: String? = try? self.base.generate(json) 247 | XCTAssertEqual(result, expectation) 248 | } 249 | 250 | func testString() { 251 | let json: String = """ 252 | { 253 | "string": "String" 254 | } 255 | """ 256 | let expectation: String = """ 257 | public struct Result: Codable { 258 | public let string: String 259 | } 260 | """ 261 | let result: String? = try? self.base.generate(json) 262 | XCTAssertEqual(result, expectation) 263 | } 264 | 265 | func testBool() { 266 | let json: String = """ 267 | { 268 | "booltrue": true, 269 | "boolfalse": false 270 | } 271 | """ 272 | let expectation: String = """ 273 | public struct Result: Codable { 274 | public let booltrue: Bool 275 | public let boolfalse: Bool 276 | } 277 | """ 278 | let result: String? = try? self.base.generate(json) 279 | XCTAssertEqual(result, expectation) 280 | } 281 | 282 | func testInt() { 283 | let json: String = """ 284 | { 285 | "int": 0 286 | } 287 | """ 288 | let expectation: String = """ 289 | public struct Result: Codable { 290 | public let int: Int 291 | } 292 | """ 293 | let result: String? = try? self.base.generate(json) 294 | XCTAssertEqual(result, expectation) 295 | } 296 | 297 | func testDouble() { 298 | let json: String = """ 299 | { 300 | "double": 1.0 301 | } 302 | """ 303 | let expectation: String = """ 304 | public struct Result: Codable { 305 | public let double: Double 306 | } 307 | """ 308 | let result: String? = try? self.base.generate(json) 309 | XCTAssertEqual(result, expectation) 310 | } 311 | 312 | func testAny() { 313 | let json: String = """ 314 | { 315 | "any": any 316 | } 317 | """ 318 | let expectation: String = """ 319 | public struct Result: Codable { 320 | public let any: Any 321 | } 322 | """ 323 | let result: String? = try? self.base.generate(json) 324 | XCTAssertEqual(result, expectation) 325 | } 326 | 327 | func testOptionalAny() { 328 | let json: String = """ 329 | { 330 | "optionalany": null 331 | } 332 | """ 333 | let expectation: String = """ 334 | public struct Result: Codable { 335 | public let optionalany: Any? 336 | } 337 | """ 338 | let result: String? = try? self.base.generate(json) 339 | XCTAssertEqual(result, expectation) 340 | } 341 | } 342 | -------------------------------------------------------------------------------- /Sources/Core/JSONtoCodable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // JSONtoCodable.swift 3 | // JSONtoCodable 4 | // 5 | // Created by Yuto Mizutani on 2018/09/21. 6 | // 7 | 8 | import Foundation 9 | 10 | public class JSONtoCodable { 11 | typealias RawJSON = (key: String, value: String, type: Type?) 12 | typealias ImmutableSeed = (key: String, type: Type) 13 | 14 | enum GenerateState { 15 | case prepareKey, inKey, prepareValue, inValue, inArray, inArrayObject(Int) 16 | } 17 | 18 | public var config: Config = Config() 19 | 20 | // MARK: - Initializers 21 | 22 | public init() {} 23 | } 24 | 25 | // MARK: - public methods 26 | 27 | public extension JSONtoCodable { 28 | func generate(_ text: String) throws -> String { 29 | let indentType = self.config.indentType 30 | self.config.indentType = .space(4) 31 | let property = try self.generateProperty(text) 32 | let duplicatedResult: String = self.createStructScope(property) 33 | var result = mergeStructs(duplicatedResult) 34 | result = result.replacingOccurrences(of: self.config.indentType.rawValue, with: indentType.rawValue) 35 | return result 36 | } 37 | } 38 | 39 | extension JSONtoCodable { 40 | func mergeStructs(_ text: String) -> String { 41 | let lineType: LineType = config.lineType 42 | let indentType = config.indentType 43 | let accessModifier = config.accessModifier 44 | 45 | let replacedLine: Character = "\n" 46 | let array = text.replacingOccurrences(of: lineType.rawValue, with: "\(replacedLine)").components(separatedBy: .newlines) 47 | 48 | struct Directory { 49 | var prefix: String 50 | var immutables: [String] 51 | var codingKeys: [String] 52 | var suffix: String 53 | var contents: [String] 54 | var directories: [Directory] 55 | 56 | var value: [String] { 57 | let p = [self.prefix] 58 | let i = !self.immutables.isEmpty ? self.immutables : [] 59 | let c = !self.codingKeys.isEmpty ? [""] + self.codingKeys : self.codingKeys 60 | let s = [self.suffix] 61 | return p + i + self.contents + c + s 62 | } 63 | } 64 | 65 | func analyseDirecoty(_ directory: Directory) -> Directory { 66 | var directory = directory 67 | let array = directory.contents 68 | var ignoreLine: Int = 0 69 | let accessModifierText = accessModifier.rawValue != "" ? "\(accessModifier.rawValue) " : "" 70 | var hasStruct: Bool = false 71 | 72 | for (i, e) in array.enumerated() { 73 | guard i >= ignoreLine else { continue } 74 | if e.hasSuffix(": Codable {") && e.replacingOccurrences(of: indentType.rawValue, with: "").hasPrefix("\(accessModifierText)struct") { 75 | hasStruct = true 76 | let indents: String = String(e[e.startIndex.. i else { continue } 80 | if se == bracket { 81 | ignoreLine = si 82 | var dir = Directory( 83 | prefix: e, 84 | immutables: [], 85 | codingKeys: [], 86 | suffix: se, 87 | contents: Array(array[i + 1.. 0 && array[i - 1] == "" && Array(array[0.. si + 1 && array[si + 1] == "" && array[si + 2].contains("enum CodingKeys: String, CodingKey {") { 94 | directory.codingKeys = Array(array[si + 2.. index ? Array(array[index + 1.. [Directory] { 127 | /// Remove duplicated in imutables 128 | func mergeImutables(_ rhs: [String], _ lhs: [String]) -> [String] { 129 | var merged = [rhs, lhs].mergeWithOptional() 130 | merged = Array(NSOrderedSet(array: merged)) as! [String] 131 | return merged 132 | } 133 | 134 | /// Remove duplicated in content 135 | func mergeContents(_ rhs: [String], _ lhs: [String]) -> [String] { 136 | var merged = [rhs + lhs].merge() 137 | merged = Array(NSOrderedSet(array: merged)) as! [String] 138 | return merged 139 | } 140 | 141 | /// Remove duplicated in codingkeys 142 | func mergeCodingKeys(_ rhs: [String], _ lhs: [String]) -> [String] { 143 | guard !rhs.isEmpty, !lhs.isEmpty else { 144 | return rhs + lhs 145 | } 146 | 147 | var merged = [Array(rhs[1.. Directory { 155 | var directory = rhs 156 | directory.immutables = mergeImutables(rhs.immutables, lhs.immutables) 157 | directory.codingKeys = mergeCodingKeys(rhs.codingKeys, lhs.codingKeys) 158 | directory.contents = [] 159 | directory.directories += lhs.directories 160 | 161 | var settedDirectories: [Directory] = [] 162 | for d in directory.directories { 163 | let prefix = d.prefix 164 | 165 | if let resultDir = settedDirectories.first(where: { $0.prefix == prefix }) { 166 | 167 | for (i, e) in settedDirectories.enumerated() where e.prefix == prefix { 168 | settedDirectories[i] = removeDuplicate(resultDir, d) 169 | break 170 | } 171 | } else { 172 | settedDirectories.append(d) 173 | continue 174 | } 175 | } 176 | directory.directories = settedDirectories 177 | return directory 178 | } 179 | 180 | var settedDirectories: [Directory] = [] 181 | for directory in directories { 182 | let prefix = directory.prefix 183 | 184 | if let resultDir = settedDirectories.first(where: { $0.prefix == prefix }) { 185 | for (i, e) in settedDirectories.enumerated() where e.prefix == prefix { 186 | settedDirectories[i] = removeDuplicate(resultDir, directory) 187 | break 188 | } 189 | } else { 190 | settedDirectories.append(directory) 191 | continue 192 | } 193 | } 194 | 195 | if !settedDirectories.filter({ !$0.directories.isEmpty }).isEmpty { 196 | for (i, e) in settedDirectories.enumerated() { 197 | settedDirectories[i].directories = setDirectories(e.directories) 198 | } 199 | } 200 | return settedDirectories 201 | } 202 | 203 | func mergeDirectries(_ directories: [Directory]) -> [String] { 204 | let directories = directories.map { 205 | Directory( 206 | prefix: $0.prefix, 207 | immutables: $0.immutables, 208 | codingKeys: $0.codingKeys, 209 | suffix: $0.suffix, 210 | contents: !$0.directories.isEmpty ? mergeDirectries($0.directories) : $0.contents, 211 | directories: [] 212 | ) 213 | } 214 | return directories.map { $0.value }.reduce([]) { $0 + [""] + $1 } 215 | } 216 | 217 | let settedDirectories = setDirectories(directory.directories) 218 | var mergedContents = mergeDirectries(settedDirectories) 219 | mergedContents = Array(mergedContents[1.. Property { 229 | var isStartedArray: Bool? 230 | 231 | var properties: [Property] = [] 232 | var state: GenerateState = .prepareKey 233 | var json: RawJSON = (key: "", value: "", type: nil) 234 | 235 | var register: String = "" 236 | 237 | /// Array values 238 | var values: [String] = [] 239 | var arrayProperties: [Property] = [] 240 | 241 | func ignore() {} 242 | func startKey() { 243 | state = .inKey 244 | json = ("", "", nil) 245 | } 246 | func addKey(_ c: Character) { 247 | json.key.append(c) 248 | } 249 | func endKey() { 250 | state = .prepareValue 251 | } 252 | 253 | func startValue() { 254 | state = .inValue 255 | } 256 | func addValue(_ c: Character) { 257 | json.value.append(c) 258 | } 259 | func endValue() throws { 260 | guard !properties.isEmpty else { throw JSONError.wrongFormat } 261 | 262 | if json.type == nil { 263 | json.type = self.decisionType(json.value) 264 | } 265 | guard let immutable = createImmutable(json) else { throw JSONError.wrongFormat } 266 | properties[properties.count - 1].immutables.append(immutable) 267 | properties[properties.count - 1].codingKeys.append(createCodingKey(json.key)) 268 | 269 | state = .prepareKey 270 | } 271 | 272 | func startArray() { 273 | state = .inArray 274 | values = [""] 275 | arrayProperties = [] 276 | } 277 | func addArrayValue(_ c: Character) throws { 278 | guard !values.isEmpty else { throw JSONError.wrongFormat } 279 | values[values.count - 1].append(c) 280 | } 281 | func nextArray() { 282 | values.append("") 283 | } 284 | func finishMainArrayObject() throws { 285 | guard let property = merge(arrayProperties) else { throw JSONError.wrongFormat } 286 | properties = [property] 287 | } 288 | func finishArrayObject() throws { 289 | let caseType = config.caseType.struct 290 | let name = config.name 291 | let structName = json.key.updateCased(with: caseType) 292 | 293 | guard let property = merge(arrayProperties) else { throw JSONError.wrongFormat } 294 | json.type = Type.structArray(structName) 295 | guard !properties.isEmpty else { throw JSONError.wrongFormat } 296 | property.prefix = property.prefix.replacingOccurrences(of: name, with: structName) 297 | let structString: String = createStructScope(property) 298 | properties[properties.count - 1].structs.append(structString) 299 | } 300 | func endArray() throws { 301 | if isStartedArray == true { 302 | try finishMainArrayObject() 303 | } else { 304 | if values.filter({ $0 != "" }).isEmpty && !arrayProperties.isEmpty { 305 | try finishArrayObject() 306 | } else { 307 | json.type = decisionType(values) 308 | } 309 | 310 | try endValue() 311 | } 312 | 313 | state = .prepareKey 314 | } 315 | 316 | func startArrayObject() { 317 | state = .inArrayObject(1) 318 | register = "" 319 | } 320 | func addArrayObject(_ n: Int) { 321 | let n = n + 1 322 | state = .inArrayObject(n) 323 | } 324 | func addArrayObjectValue(_ c: Character) { 325 | register.append(c) 326 | } 327 | func removeArrayObject(_ n: Int) throws { 328 | let n = n - 1 329 | state = .inArrayObject(n) 330 | if n == 0 { 331 | try endArrayObject() 332 | } 333 | } 334 | func endArrayObject() throws { 335 | let property = try generateProperty(register) 336 | arrayProperties.append(property) 337 | state = .inArray 338 | } 339 | 340 | func startStruct() throws { 341 | let caseType: CaseType = config.caseType.struct 342 | let type: String = json.key.updateCased(with: caseType) 343 | 344 | // end value 345 | json.type = .struct(type) 346 | try endValue() 347 | 348 | // add property 349 | let property = Property(type, accessModifier: config.accessModifier) 350 | properties.append(property) 351 | state = .prepareKey 352 | } 353 | func endStruct() { 354 | guard properties.count >= 2 else { return } 355 | let structString: String = self.createStructScope(properties.last!) 356 | properties.remove(at: properties.count - 1) 357 | properties[properties.count - 1].structs.append(structString) 358 | } 359 | 360 | properties.append(Property(config.name, accessModifier: config.accessModifier)) 361 | for character in text { 362 | switch state { 363 | case .prepareKey: 364 | switch character { 365 | case "\"": 366 | if isStartedArray == nil { 367 | isStartedArray = false 368 | } 369 | startKey() 370 | case " ", ",": 371 | ignore() 372 | case "}": 373 | endStruct() 374 | case "[": 375 | if isStartedArray == nil { 376 | isStartedArray = true 377 | } 378 | startArray() 379 | default: 380 | ignore() 381 | } 382 | case .inKey: 383 | switch character { 384 | case "\"": 385 | endKey() 386 | default: 387 | addKey(character) 388 | } 389 | case .prepareValue: 390 | switch character { 391 | case ":", " ": 392 | ignore() 393 | case "{": 394 | try startStruct() 395 | case "[": 396 | startArray() 397 | default: 398 | startValue() 399 | addValue(character) 400 | } 401 | case .inValue: 402 | switch character { 403 | case "\"": 404 | addValue(character) 405 | if String(json.value.suffix(2)) != "\\\"" { 406 | try endValue() 407 | } 408 | case " ", "\r", "\n", ",": 409 | if decisionType(json.value) == .string { 410 | addValue(character) 411 | } else { 412 | try endValue() 413 | } 414 | default: 415 | addValue(character) 416 | } 417 | case .inArray: 418 | switch character { 419 | case "{": 420 | startArrayObject() 421 | addArrayObjectValue(character) 422 | case "]": 423 | try endArray() 424 | case ",": 425 | nextArray() 426 | case " ", "\r", "\n": 427 | guard !values.isEmpty else { throw JSONError.wrongFormat } 428 | if decisionType(values[values.count - 1]) == .string { 429 | try addArrayValue(character) 430 | } 431 | default: 432 | try addArrayValue(character) 433 | } 434 | case .inArrayObject(let n): 435 | addArrayObjectValue(character) 436 | switch character { 437 | case "{": 438 | addArrayObject(n) 439 | case "}": 440 | try removeArrayObject(n) 441 | default: 442 | break 443 | } 444 | } 445 | } 446 | 447 | return properties.first! 448 | } 449 | 450 | func decisionType(_ value: String) -> Type { 451 | if String(value.prefix(1)) == "\"" { 452 | return .string 453 | } 454 | 455 | switch value.lowercased() { 456 | case "true", "false": 457 | return .bool 458 | case "nil", "null": 459 | return.optionalAny 460 | case let value where value == Int(value)?.description: 461 | return .int 462 | case let value where value == Double(value)?.description: 463 | return .double 464 | default: 465 | return .any 466 | } 467 | } 468 | 469 | func decisionType(_ values: [String]) -> Type { 470 | return values.map { decisionType($0) }.sumType() 471 | } 472 | 473 | func createImmutable(_ json: RawJSON) -> String? { 474 | guard let type = json.type?.rawValue else { return nil } 475 | let accessModifier: AccessModifier = config.accessModifier 476 | let caseTypes = config.caseType 477 | 478 | let prefix: String = accessModifier == .default ? "" : "\(accessModifier.rawValue) " 479 | let key: String = json.key.updateCased(with: caseTypes.variable) 480 | return "\(prefix)let \(key): \(type)" 481 | } 482 | 483 | func createCodingKey(_ jsonKey: String) -> String { 484 | let caseType = config.caseType.variable 485 | 486 | let key: String = jsonKey.updateCased(with: caseType) 487 | return "case \((key == jsonKey || key == jsonKey.escaped()) ? key : "\(key) = \"\(jsonKey)\"")" 488 | } 489 | 490 | func createCodingKeyScope(_ keys: [String]) -> String? { 491 | guard !keys.filter({ $0.contains("=") }).isEmpty else { return nil } 492 | 493 | let indent: String = config.indentType.rawValue 494 | let line: String = config.lineType.rawValue 495 | let prefix: String = "private enum CodingKeys: String, CodingKey {" 496 | let suffix: String = "}" 497 | 498 | let contents: String = keys.map { indent + $0 }.joined(separator: line) 499 | 500 | return [prefix, contents, suffix].joined(separator: line) 501 | } 502 | 503 | func createStructScope(_ property: Property) -> String { 504 | let line: String = config.lineType.rawValue 505 | let indent: String = config.indentType.rawValue 506 | 507 | let prefix: String = property.prefix 508 | let suffix: String = property.suffix ?? "" 509 | 510 | let immutableString: String = property.immutables.joined(separator: line) 511 | let internalStructString: String? = !property.structs.isEmpty ? property.structs.joined(separator: "\(line)\(line)") : nil 512 | let codingKeyString: String? = self.createCodingKeyScope(property.codingKeys) 513 | 514 | var contents: String = [immutableString, internalStructString, codingKeyString] 515 | .filter { $0 != nil }.map { $0! } 516 | .joined(separator: "\(line)\(line)") 517 | .replacingOccurrences(of: line, with: "\(line)\(indent)") 518 | .replacingOccurrences(of: "\(line)\(indent)\(line)", with: "\(line)\(line)") 519 | contents = indent + contents 520 | return [prefix, contents, suffix].joined(separator: line) 521 | } 522 | 523 | func merge(_ properties: [Property]) -> Property? { 524 | guard !properties.isEmpty else { return nil } 525 | 526 | let property = properties[0] 527 | property.immutables = properties.map { $0.immutables }.mergeWithOptional() 528 | guard let structs: [String] = (NSOrderedSet(array: properties.map { $0.structs }.flatMap { $0 }).array as? [String]) else { return nil } 529 | property.structs = structs 530 | property.codingKeys = properties.map { $0.codingKeys }.merge() 531 | return property 532 | } 533 | } 534 | -------------------------------------------------------------------------------- /Tests/JSONtoCodableTests/ArrayTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ArrayTests.swift 3 | // JSONtoCodable 4 | // 5 | // Created by Yuto Mizutani on 2018/09/23. 6 | // 7 | 8 | import XCTest 9 | @testable import JSONtoCodable 10 | 11 | class ArrayTests: XCTestCase { 12 | var base: JSONtoCodable! 13 | 14 | override func setUp() { 15 | super.setUp() 16 | self.base = JSONtoCodable() 17 | } 18 | 19 | func testRawEmptyArray() { 20 | let json: String = """ 21 | { 22 | "a": [] 23 | } 24 | """ 25 | let expectation: String = """ 26 | public struct Result: Codable { 27 | public let a: [Any] 28 | } 29 | """ 30 | let result: String? = try? self.base.generate(json) 31 | XCTAssertEqual(result, expectation) 32 | } 33 | 34 | func testRawStringArray() { 35 | let json: String = """ 36 | { 37 | "a": ["b", "c", "d"] 38 | } 39 | """ 40 | let expectation: String = """ 41 | public struct Result: Codable { 42 | public let a: [String] 43 | } 44 | """ 45 | let result: String? = try? self.base.generate(json) 46 | XCTAssertEqual(result, expectation) 47 | } 48 | 49 | func testRawFormattedStringArray() { 50 | let json: String = """ 51 | { 52 | "a": [ 53 | "b", 54 | "c", 55 | "d" 56 | ] 57 | } 58 | """ 59 | let expectation: String = """ 60 | public struct Result: Codable { 61 | public let a: [String] 62 | } 63 | """ 64 | let result: String? = try? self.base.generate(json) 65 | XCTAssertEqual(result, expectation) 66 | } 67 | 68 | func testRawIntArray() { 69 | let json: String = """ 70 | { 71 | "a": [1, 2, 3] 72 | } 73 | """ 74 | let expectation: String = """ 75 | public struct Result: Codable { 76 | public let a: [Int] 77 | } 78 | """ 79 | let result: String? = try? self.base.generate(json) 80 | XCTAssertEqual(result, expectation) 81 | } 82 | 83 | func testRawFormattedIntArray() { 84 | let json: String = """ 85 | { 86 | "a": [ 87 | 1, 88 | 2, 89 | 3 90 | ] 91 | } 92 | """ 93 | let expectation: String = """ 94 | public struct Result: Codable { 95 | public let a: [Int] 96 | } 97 | """ 98 | let result: String? = try? self.base.generate(json) 99 | XCTAssertEqual(result, expectation) 100 | } 101 | 102 | func testRawMultTypesArray() { 103 | let json: String = """ 104 | { 105 | "a": ["b", 1, true, []] 106 | } 107 | """ 108 | let expectation: String = """ 109 | public struct Result: Codable { 110 | public let a: [Any] 111 | } 112 | """ 113 | let result: String? = try? self.base.generate(json) 114 | XCTAssertEqual(result, expectation) 115 | } 116 | 117 | func testRawMultTypesWithNullArray() { 118 | let json: String = """ 119 | { 120 | "a": ["b", 1, true, null] 121 | } 122 | """ 123 | let expectation: String = """ 124 | public struct Result: Codable { 125 | public let a: [Any?] 126 | } 127 | """ 128 | let result: String? = try? self.base.generate(json) 129 | XCTAssertEqual(result, expectation) 130 | } 131 | 132 | func testRawFormattedMultTypesWithOojectArray() { 133 | let json: String = """ 134 | { 135 | "a": [ 136 | "b", 137 | { 138 | "c": "d" 139 | } 140 | ] 141 | } 142 | """ 143 | let expectation: String = """ 144 | public struct Result: Codable { 145 | public let a: [Any] 146 | } 147 | """ 148 | let result: String? = try? self.base.generate(json) 149 | XCTAssertEqual(result, expectation) 150 | } 151 | 152 | func testRawFormattedMultTypesWithNullOojectArray() { 153 | let json: String = """ 154 | { 155 | "a": [ 156 | "b", 157 | null, 158 | { 159 | "c": "d" 160 | } 161 | ] 162 | } 163 | """ 164 | let expectation: String = """ 165 | public struct Result: Codable { 166 | public let a: [Any?] 167 | } 168 | """ 169 | let result: String? = try? self.base.generate(json) 170 | XCTAssertEqual(result, expectation) 171 | } 172 | 173 | func testBeginArray() { 174 | let json: String = """ 175 | [ 176 | { 177 | "a": 1, 178 | "b": 2 179 | }, 180 | { 181 | "a": 1, 182 | "b": 2 183 | }, 184 | ] 185 | """ 186 | let expectation: String = """ 187 | public struct Result: Codable { 188 | public let a: Int 189 | public let b: Int 190 | } 191 | """ 192 | let result: String? = try? self.base.generate(json) 193 | XCTAssertEqual(result, expectation) 194 | } 195 | 196 | func testBeginArrayOptional() { 197 | let json: String = """ 198 | [ 199 | { 200 | "a": 1, 201 | "b": 2 202 | }, 203 | { 204 | "a": 1, 205 | "c": 3 206 | }, 207 | ] 208 | """ 209 | let expectation: String = """ 210 | public struct Result: Codable { 211 | public let a: Int 212 | public let b: Int? 213 | public let c: Int? 214 | } 215 | """ 216 | let result: String? = try? self.base.generate(json) 217 | XCTAssertEqual(result, expectation) 218 | } 219 | 220 | func testBeginArrayWithCodingKeys() { 221 | let json: String = """ 222 | [ 223 | { 224 | "A": 1, 225 | "B": 2 226 | }, 227 | { 228 | "A": 1, 229 | "B": 2 230 | }, 231 | ] 232 | """ 233 | let expectation: String = """ 234 | public struct Result: Codable { 235 | public let a: Int 236 | public let b: Int 237 | 238 | private enum CodingKeys: String, CodingKey { 239 | case a = "A" 240 | case b = "B" 241 | } 242 | } 243 | """ 244 | let result: String? = try? self.base.generate(json) 245 | XCTAssertEqual(result, expectation) 246 | } 247 | 248 | func testBeginArrayOptionalWithCodingKeys() { 249 | let json: String = """ 250 | [ 251 | { 252 | "A": 1, 253 | "B": 2 254 | }, 255 | { 256 | "A": 1, 257 | "C": 3 258 | }, 259 | ] 260 | """ 261 | let expectation: String = """ 262 | public struct Result: Codable { 263 | public let a: Int 264 | public let b: Int? 265 | public let c: Int? 266 | 267 | private enum CodingKeys: String, CodingKey { 268 | case a = "A" 269 | case b = "B" 270 | case c = "C" 271 | } 272 | } 273 | """ 274 | let result: String? = try? self.base.generate(json) 275 | XCTAssertEqual(result, expectation) 276 | } 277 | 278 | func testArray() { 279 | let json: String = """ 280 | { 281 | "array": [ 282 | { 283 | "a": 1 284 | }, 285 | { 286 | "a": 2 287 | }, 288 | ] 289 | } 290 | """ 291 | let expectation: String = """ 292 | public struct Result: Codable { 293 | public let array: [Array] 294 | 295 | public struct Array: Codable { 296 | public let a: Int 297 | } 298 | } 299 | """ 300 | let result: String? = try? self.base.generate(json) 301 | XCTAssertEqual(result, expectation) 302 | } 303 | 304 | func testArrayOptional() { 305 | let json: String = """ 306 | { 307 | "array": [ 308 | { 309 | "a": 1, 310 | "b": 2 311 | }, 312 | { 313 | "a": 1, 314 | "c": 3 315 | }, 316 | ] 317 | } 318 | """ 319 | let expectation: String = """ 320 | public struct Result: Codable { 321 | public let array: [Array] 322 | 323 | public struct Array: Codable { 324 | public let a: Int 325 | public let b: Int? 326 | public let c: Int? 327 | } 328 | } 329 | """ 330 | let result: String? = try? self.base.generate(json) 331 | XCTAssertEqual(result, expectation) 332 | } 333 | 334 | func testArrayWithArrayCodingKeys() { 335 | let json: String = """ 336 | { 337 | "Array": [ 338 | { 339 | "a": 1 340 | }, 341 | { 342 | "a": 1 343 | }, 344 | ] 345 | } 346 | """ 347 | let expectation: String = """ 348 | public struct Result: Codable { 349 | public let array: [Array] 350 | 351 | public struct Array: Codable { 352 | public let a: Int 353 | } 354 | 355 | private enum CodingKeys: String, CodingKey { 356 | case array = "Array" 357 | } 358 | } 359 | """ 360 | let result: String? = try? self.base.generate(json) 361 | XCTAssertEqual(result, expectation) 362 | } 363 | 364 | func testArrayOptionalWithArrayCodingKeys() { 365 | let json: String = """ 366 | { 367 | "Array": [ 368 | { 369 | "a": 1, 370 | "b": 2 371 | }, 372 | { 373 | "a": 1, 374 | "c": 3 375 | }, 376 | ] 377 | } 378 | """ 379 | let expectation: String = """ 380 | public struct Result: Codable { 381 | public let array: [Array] 382 | 383 | public struct Array: Codable { 384 | public let a: Int 385 | public let b: Int? 386 | public let c: Int? 387 | } 388 | 389 | private enum CodingKeys: String, CodingKey { 390 | case array = "Array" 391 | } 392 | } 393 | """ 394 | let result: String? = try? self.base.generate(json) 395 | XCTAssertEqual(result, expectation) 396 | } 397 | 398 | func testArrayWithCodingKeys() { 399 | let json: String = """ 400 | { 401 | "array": [ 402 | { 403 | "A": 1 404 | }, 405 | { 406 | "A": 1 407 | }, 408 | ] 409 | } 410 | """ 411 | let expectation: String = """ 412 | public struct Result: Codable { 413 | public let array: [Array] 414 | 415 | public struct Array: Codable { 416 | public let a: Int 417 | 418 | private enum CodingKeys: String, CodingKey { 419 | case a = "A" 420 | } 421 | } 422 | } 423 | """ 424 | let result: String? = try? self.base.generate(json) 425 | XCTAssertEqual(result, expectation) 426 | } 427 | 428 | func testArrayOptionalWithCodingKeys() { 429 | let json: String = """ 430 | { 431 | "array": [ 432 | { 433 | "A": 1, 434 | "B": 2 435 | }, 436 | { 437 | "A": 1, 438 | "C": 3 439 | }, 440 | ] 441 | } 442 | """ 443 | let expectation: String = """ 444 | public struct Result: Codable { 445 | public let array: [Array] 446 | 447 | public struct Array: Codable { 448 | public let a: Int 449 | public let b: Int? 450 | public let c: Int? 451 | 452 | private enum CodingKeys: String, CodingKey { 453 | case a = "A" 454 | case b = "B" 455 | case c = "C" 456 | } 457 | } 458 | } 459 | """ 460 | let result: String? = try? self.base.generate(json) 461 | XCTAssertEqual(result, expectation) 462 | } 463 | 464 | func testArrayOptionalWithAllCodingKeys() { 465 | let json: String = """ 466 | { 467 | "Array": [ 468 | { 469 | "A": 1, 470 | "B": 2 471 | }, 472 | { 473 | "A": 1, 474 | "C": 3 475 | }, 476 | ] 477 | } 478 | """ 479 | let expectation: String = """ 480 | public struct Result: Codable { 481 | public let array: [Array] 482 | 483 | public struct Array: Codable { 484 | public let a: Int 485 | public let b: Int? 486 | public let c: Int? 487 | 488 | private enum CodingKeys: String, CodingKey { 489 | case a = "A" 490 | case b = "B" 491 | case c = "C" 492 | } 493 | } 494 | 495 | private enum CodingKeys: String, CodingKey { 496 | case array = "Array" 497 | } 498 | } 499 | """ 500 | let result: String? = try? self.base.generate(json) 501 | XCTAssertEqual(result, expectation) 502 | } 503 | 504 | func testArrayOptionalArrayWithAllCodingKeys() { 505 | let json: String = """ 506 | { 507 | "Array": [ 508 | { 509 | "A": 1, 510 | "B": [2] 511 | }, 512 | { 513 | "A": 1, 514 | "C": [3] 515 | }, 516 | ] 517 | } 518 | """ 519 | let expectation: String = """ 520 | public struct Result: Codable { 521 | public let array: [Array] 522 | 523 | public struct Array: Codable { 524 | public let a: Int 525 | public let b: [Int]? 526 | public let c: [Int]? 527 | 528 | private enum CodingKeys: String, CodingKey { 529 | case a = "A" 530 | case b = "B" 531 | case c = "C" 532 | } 533 | } 534 | 535 | private enum CodingKeys: String, CodingKey { 536 | case array = "Array" 537 | } 538 | } 539 | """ 540 | let result: String? = try? self.base.generate(json) 541 | XCTAssertEqual(result, expectation) 542 | } 543 | 544 | func testMultArrayWithAllCodingKeys() { 545 | let json: String = """ 546 | [ 547 | { 548 | "test": "test" 549 | "Array": [ 550 | { 551 | "A": 1, 552 | "B": 2 553 | }, 554 | { 555 | "A": 1, 556 | "C": 3 557 | }, 558 | ] 559 | }, 560 | { 561 | "Array": [ 562 | { 563 | "A": 1, 564 | "B": 2 565 | }, 566 | { 567 | "A": 1, 568 | "C": 3 569 | }, 570 | ] 571 | } 572 | ] 573 | """ 574 | let expectation: String = """ 575 | public struct Result: Codable { 576 | public let test: String? 577 | public let array: [Array] 578 | 579 | public struct Array: Codable { 580 | public let a: Int 581 | public let b: Int? 582 | public let c: Int? 583 | 584 | private enum CodingKeys: String, CodingKey { 585 | case a = "A" 586 | case b = "B" 587 | case c = "C" 588 | } 589 | } 590 | 591 | private enum CodingKeys: String, CodingKey { 592 | case test 593 | case array = "Array" 594 | } 595 | } 596 | """ 597 | let result: String? = try? self.base.generate(json) 598 | XCTAssertEqual(result, expectation) 599 | } 600 | 601 | func testArrayInObject() { 602 | let json: String = """ 603 | { 604 | "Object": { 605 | "array": [ 606 | 1, 607 | 2, 608 | 3 609 | ] 610 | } 611 | } 612 | """ 613 | let expectation: String = """ 614 | public struct Result: Codable { 615 | public let object: Object 616 | 617 | public struct Object: Codable { 618 | public let array: [Int] 619 | } 620 | 621 | private enum CodingKeys: String, CodingKey { 622 | case object = "Object" 623 | } 624 | } 625 | """ 626 | let result: String? = try? self.base.generate(json) 627 | XCTAssertEqual(result, expectation) 628 | } 629 | 630 | func testArrayObject() { 631 | let json: String = """ 632 | { 633 | "array": [ 634 | { 635 | "number": 1, 636 | "InArray-Object": { 637 | "hello": "world" 638 | } 639 | }, 640 | { 641 | "number": 1, 642 | "InArray-Object": { 643 | "hello": "world" 644 | } 645 | } 646 | ] 647 | } 648 | """ 649 | let expectation: String = """ 650 | public struct Result: Codable { 651 | public let array: [Array] 652 | 653 | public struct Array: Codable { 654 | public let number: Int 655 | public let inArrayObject: InArrayObject 656 | 657 | public struct InArrayObject: Codable { 658 | public let hello: String 659 | } 660 | 661 | private enum CodingKeys: String, CodingKey { 662 | case number 663 | case inArrayObject = "InArray-Object" 664 | } 665 | } 666 | } 667 | """ 668 | let result: String? = try? self.base.generate(json) 669 | XCTAssertEqual(result, expectation) 670 | } 671 | 672 | func testNestedArrayObjectInObject() { 673 | let json: String = """ 674 | { 675 | "Object": { 676 | "array": [ 677 | { 678 | "InArray-Object": { 679 | "hello": "world" 680 | }, 681 | "number": 1 682 | }, 683 | { 684 | "InArray-Object": { 685 | "hello": "world" 686 | }, 687 | "number": 1 688 | } 689 | ] 690 | } 691 | } 692 | """ 693 | let expectation: String = """ 694 | public struct Result: Codable { 695 | public let object: Object 696 | 697 | public struct Object: Codable { 698 | public let array: [Array] 699 | 700 | public struct Array: Codable { 701 | public let inArrayObject: InArrayObject 702 | public let number: Int 703 | 704 | public struct InArrayObject: Codable { 705 | public let hello: String 706 | } 707 | 708 | private enum CodingKeys: String, CodingKey { 709 | case inArrayObject = "InArray-Object" 710 | case number 711 | } 712 | } 713 | } 714 | 715 | private enum CodingKeys: String, CodingKey { 716 | case object = "Object" 717 | } 718 | } 719 | """ 720 | let result: String? = try? self.base.generate(json) 721 | XCTAssertEqual(result, expectation) 722 | } 723 | 724 | func testArrayObjectWithOptionals() { 725 | let json: String = """ 726 | { 727 | "array": [ 728 | { 729 | "InArray-Object:WithOptionals": { 730 | "hello": "world" 731 | } 732 | }, 733 | { 734 | "InArray-Object:WithOptionals": { 735 | "hello": "world", 736 | "number": 1 737 | } 738 | } 739 | ] 740 | } 741 | """ 742 | let expectation: String = """ 743 | public struct Result: Codable { 744 | public let array: [Array] 745 | 746 | public struct Array: Codable { 747 | public let inArrayObjectWithOptionals: InArrayObjectWithOptionals 748 | 749 | public struct InArrayObjectWithOptionals: Codable { 750 | public let hello: String 751 | public let number: Int? 752 | } 753 | 754 | private enum CodingKeys: String, CodingKey { 755 | case inArrayObjectWithOptionals = "InArray-Object:WithOptionals" 756 | } 757 | } 758 | } 759 | """ 760 | let result: String? = try? self.base.generate(json) 761 | XCTAssertEqual(result, expectation) 762 | } 763 | 764 | func testMultArrayObjectWithOptionals() { 765 | let json: String = """ 766 | { 767 | "array": [ 768 | { 769 | "Obj:First": { 770 | "hello": "world" 771 | } 772 | }, 773 | { 774 | "Obj:First": { 775 | "hello": "world" 776 | }, 777 | "Obj:Second": { 778 | "hello": "world" 779 | } 780 | } 781 | ] 782 | } 783 | """ 784 | let expectation: String = """ 785 | public struct Result: Codable { 786 | public let array: [Array] 787 | 788 | public struct Array: Codable { 789 | public let objFirst: ObjFirst 790 | public let objSecond: ObjSecond? 791 | 792 | public struct ObjFirst: Codable { 793 | public let hello: String 794 | } 795 | 796 | public struct ObjSecond: Codable { 797 | public let hello: String 798 | } 799 | 800 | private enum CodingKeys: String, CodingKey { 801 | case objFirst = "Obj:First" 802 | case objSecond = "Obj:Second" 803 | } 804 | } 805 | } 806 | """ 807 | let result: String? = try? self.base.generate(json) 808 | XCTAssertEqual(result, expectation) 809 | } 810 | 811 | func testArrayObjectWithOptionalsWithAllCodingKeys() { 812 | let json: String = """ 813 | { 814 | "array": [ 815 | { 816 | "InArray-Object:WithOptionals": { 817 | "hello": "world" 818 | } 819 | }, 820 | { 821 | "InArray-Object:WithOptionals": { 822 | "hello": "world", 823 | "Number": 1 824 | } 825 | } 826 | ] 827 | } 828 | """ 829 | let expectation: String = """ 830 | public struct Result: Codable { 831 | public let array: [Array] 832 | 833 | public struct Array: Codable { 834 | public let inArrayObjectWithOptionals: InArrayObjectWithOptionals 835 | 836 | public struct InArrayObjectWithOptionals: Codable { 837 | public let hello: String 838 | public let number: Int? 839 | 840 | private enum CodingKeys: String, CodingKey { 841 | case hello 842 | case number = "Number" 843 | } 844 | } 845 | 846 | private enum CodingKeys: String, CodingKey { 847 | case inArrayObjectWithOptionals = "InArray-Object:WithOptionals" 848 | } 849 | } 850 | } 851 | """ 852 | let result: String? = try? self.base.generate(json) 853 | XCTAssertEqual(result, expectation) 854 | } 855 | 856 | func testNestedArrayOptionalObjectInObject() { 857 | let json: String = """ 858 | { 859 | "Object": { 860 | "array": [ 861 | { 862 | "number": 1, 863 | "Optional-Object": { 864 | "hello": "world" 865 | } 866 | }, 867 | { 868 | "number": 1 869 | } 870 | ] 871 | } 872 | } 873 | """ 874 | let expectation: String = """ 875 | public struct Result: Codable { 876 | public let object: Object 877 | 878 | public struct Object: Codable { 879 | public let array: [Array] 880 | 881 | public struct Array: Codable { 882 | public let number: Int 883 | public let optionalObject: OptionalObject? 884 | 885 | public struct OptionalObject: Codable { 886 | public let hello: String 887 | } 888 | 889 | private enum CodingKeys: String, CodingKey { 890 | case number 891 | case optionalObject = "Optional-Object" 892 | } 893 | } 894 | } 895 | 896 | private enum CodingKeys: String, CodingKey { 897 | case object = "Object" 898 | } 899 | } 900 | """ 901 | let result: String? = try? self.base.generate(json) 902 | XCTAssertEqual(result, expectation) 903 | } 904 | } 905 | --------------------------------------------------------------------------------