├── .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 | 
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 | 
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 | 
2 |
3 | [](https://app.bitrise.io/app/869daca1801a29aa)
4 | [](https://github.com/YutoMizutani/JSONtoCodable)
5 | [](https://github.com/YutoMizutani/JSONtoCodable/blob/master/LICENSE)
6 | [](https://github.com/YutoMizutani/JSONtoCodable)
7 | [](https://github.com/YutoMizutani/JSONtoCodable)
8 | [](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 | 
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 | 
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 | 
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 |
--------------------------------------------------------------------------------