Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion Decodable.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@
8FB48ECD1D306C4700BC50A1 /* KeyPath.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8FB48EC91D306C4700BC50A1 /* KeyPath.swift */; };
8FD3D92F1C270A2D00D1AF4E /* MissingKeyOperatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8FD3D92E1C270A2D00D1AF4E /* MissingKeyOperatorTests.swift */; };
8FD3D9301C270A2D00D1AF4E /* MissingKeyOperatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8FD3D92E1C270A2D00D1AF4E /* MissingKeyOperatorTests.swift */; };
8FE6CAE91E69F51700662B12 /* JSON.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8FE6CAE81E69F51700662B12 /* JSON.swift */; };
8FE6CAEA1E69F51700662B12 /* JSON.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8FE6CAE81E69F51700662B12 /* JSON.swift */; };
8FE6CAEB1E69F51700662B12 /* JSON.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8FE6CAE81E69F51700662B12 /* JSON.swift */; };
8FE6CAEC1E69F51700662B12 /* JSON.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8FE6CAE81E69F51700662B12 /* JSON.swift */; };
8FE7B5661B4C9FB900837609 /* Decodable.h in Headers */ = {isa = PBXBuildFile; fileRef = 8FE7B5651B4C9FB900837609 /* Decodable.h */; settings = {ATTRIBUTES = (Public, ); }; };
8FE7B56D1B4C9FB900837609 /* Decodable.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8FE7B5621B4C9FB900837609 /* Decodable.framework */; };
8FE7B5721B4C9FB900837609 /* DecodableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8FE7B5711B4C9FB900837609 /* DecodableTests.swift */; };
Expand Down Expand Up @@ -144,6 +148,7 @@
8F956D1E1B4D6FF700243072 /* Operators.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Operators.swift; sourceTree = "<group>"; };
8FB48EC91D306C4700BC50A1 /* KeyPath.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyPath.swift; sourceTree = "<group>"; };
8FD3D92E1C270A2D00D1AF4E /* MissingKeyOperatorTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MissingKeyOperatorTests.swift; sourceTree = "<group>"; };
8FE6CAE81E69F51700662B12 /* JSON.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JSON.swift; sourceTree = "<group>"; };
8FE7B5621B4C9FB900837609 /* Decodable.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Decodable.framework; sourceTree = BUILT_PRODUCTS_DIR; };
8FE7B5651B4C9FB900837609 /* Decodable.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Decodable.h; sourceTree = "<group>"; };
8FE7B5671B4C9FB900837609 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
Expand Down Expand Up @@ -264,6 +269,7 @@
8F72DC551C3CB8C500A39E10 /* NSValueCastable.swift */,
8F3E45A31D327E4500FB71FC /* Decoders.swift */,
8FB48EC91D306C4700BC50A1 /* KeyPath.swift */,
8FE6CAE81E69F51700662B12 /* JSON.swift */,
8F3E45991D31362B00FB71FC /* OptionalKeyPath.swift */,
8F956D1E1B4D6FF700243072 /* Operators.swift */,
8F00623E1C81EF61007BCF48 /* Overloads.swift */,
Expand Down Expand Up @@ -630,6 +636,7 @@
8F87BCC51B592F0E00E53A8C /* DecodingError.swift in Sources */,
8FFAB8131B7CFA9500E2D724 /* Parse.swift in Sources */,
8F012EF61BB5A920007D0B5C /* Castable.swift in Sources */,
8FE6CAEA1E69F51700662B12 /* JSON.swift in Sources */,
8FB48ECB1D306C4700BC50A1 /* KeyPath.swift in Sources */,
17FB810E1B5311840012F106 /* Decodable.swift in Sources */,
17FB810F1B5311870012F106 /* Operators.swift in Sources */,
Expand Down Expand Up @@ -671,6 +678,7 @@
57FCDE5B1BA283C900130C48 /* DecodingError.swift in Sources */,
57FCDE5C1BA283C900130C48 /* Parse.swift in Sources */,
8F012EF81BB5A928007D0B5C /* Castable.swift in Sources */,
8FE6CAEC1E69F51700662B12 /* JSON.swift in Sources */,
8FB48ECD1D306C4700BC50A1 /* KeyPath.swift in Sources */,
57FCDE5D1BA283C900130C48 /* Decodable.swift in Sources */,
57FCDE5E1BA283C900130C48 /* Operators.swift in Sources */,
Expand All @@ -687,6 +695,7 @@
buildActionMask = 2147483647;
files = (
8F87BCC41B592F0E00E53A8C /* DecodingError.swift in Sources */,
8FE6CAE91E69F51700662B12 /* JSON.swift in Sources */,
8FA733591D328D13003A90A7 /* Header.swift in Sources */,
8FFAB8121B7CFA9500E2D724 /* Parse.swift in Sources */,
8F012EF51BB5A920007D0B5C /* Castable.swift in Sources */,
Expand Down Expand Up @@ -732,6 +741,7 @@
D0DC547A1B78150900F79CB0 /* DecodingError.swift in Sources */,
8FFAB8141B7CFA9500E2D724 /* Parse.swift in Sources */,
8F012EF71BB5A920007D0B5C /* Castable.swift in Sources */,
8FE6CAEB1E69F51700662B12 /* JSON.swift in Sources */,
8FB48ECC1D306C4700BC50A1 /* KeyPath.swift in Sources */,
D0DC54771B78150900F79CB0 /* Decodable.swift in Sources */,
D0DC54781B78150900F79CB0 /* Operators.swift in Sources */,
Expand Down Expand Up @@ -1000,7 +1010,7 @@
PRODUCT_BUNDLE_IDENTIFIER = anviking.Decodable;
PRODUCT_NAME = Decodable;
SKIP_INSTALL = YES;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
};
name = Debug;
};
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>classNames</key>
<dict>
<key>DecodableTests</key>
<dict>
<key>testDecodeArrayOfRepositoriesAndMeasureTime()</key>
<dict>
<key>com.apple.XCTPerformanceMetric_WallClockTime</key>
<dict>
<key>baselineAverage</key>
<real>0.034971</real>
<key>baselineIntegrationDisplayName</key>
<string>Local Baseline</string>
</dict>
</dict>
</dict>
</dict>
</dict>
</plist>
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,37 @@
<string>com.apple.platform.iphonesimulator</string>
</dict>
</dict>
<key>88050F31-6133-4363-B1E9-9E5ADFEE3886</key>
<dict>
<key>localComputer</key>
<dict>
<key>busSpeedInMHz</key>
<integer>100</integer>
<key>cpuCount</key>
<integer>1</integer>
<key>cpuKind</key>
<string>Intel Core i7</string>
<key>cpuSpeedInMHz</key>
<integer>2300</integer>
<key>logicalCPUCoresPerPackage</key>
<integer>8</integer>
<key>modelCode</key>
<string>MacBookPro10,1</string>
<key>physicalCPUCoresPerPackage</key>
<integer>4</integer>
<key>platformIdentifier</key>
<string>com.apple.platform.macosx</string>
</dict>
<key>targetArchitecture</key>
<string>x86_64</string>
<key>targetDevice</key>
<dict>
<key>modelCode</key>
<string>iPhone9,2</string>
<key>platformIdentifier</key>
<string>com.apple.platform.iphonesimulator</string>
</dict>
</dict>
</dict>
</dict>
</plist>
15 changes: 10 additions & 5 deletions Generator/Generator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -168,12 +168,13 @@ indirect enum Decodable {

let arguments = provider.takenNames.values.sorted().map { $0 + ": Decodable" }
let generics = arguments.count > 0 ? "<\(arguments.joined(separator: ", "))>" : ""
var final = ""

switch operatorString {
case "=>":
behaviour = Behaviour(throwsIfKeyMissing: true, throwsIfNull: !isOptional, throwsFromDecodeClosure: true)
keyPathType = "KeyPath"

/*
// Start again
guard isOptional else { break }
Expand All @@ -190,14 +191,18 @@ indirect enum Decodable {
// Never trows if null
behaviour = Behaviour(throwsIfKeyMissing: false, throwsIfNull: false, throwsFromDecodeClosure: true)
keyPathType = "OptionalKeyPath"
final = ".flatMap{$0}" // flatten double optionals
default:
fatalError()
}

let documentation = generateDocumentationComment(behaviour)
return overloads + [documentation + "public func \(operatorString) \(generics)(json: Any, keyPath: \(keyPathType)) throws -> \(returnType) {\n" +
" return try parse(json, keyPath: keyPath, decoder: \(decodeClosure(provider)))\n" +
"}"
return overloads + [documentation + "public func \(operatorString) \(generics)(json: JSON, keyPath: \(keyPathType)) throws -> \(returnType) {\n" +
" return try json.parse(keyPath: keyPath, decoder: \(decodeClosure(provider)))\(final)\n" +
"}",
documentation + "public func \(operatorString) \(generics)(json: Any, keyPath: \(keyPathType)) throws -> \(returnType) {\n" +
" return try JSON(value: json).parse(keyPath: keyPath, decoder: \(decodeClosure(provider)))\(final)\n" +
"}",
]
}
}
Expand Down Expand Up @@ -233,7 +238,7 @@ dateFormatter.dateStyle = .short

let date = dateFormatter.string(from: Date())

let overloads = Decodable.T(Unique()).generateAllPossibleChildren(4)
let overloads = Decodable.T(Unique()).generateAllPossibleChildren(2)
let types = overloads.map { $0.typeString(TypeNameProvider()) }
let all = overloads.flatMap { $0.generateOverloads("=>") } + overloads.flatMap(filterOptionals).map{ $0.wrapInOptionalIfNeeded() }.flatMap { $0.generateOverloads("=>?") }

Expand Down
43 changes: 20 additions & 23 deletions Sources/Castable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,9 @@ import Foundation
/// Attempt to cast an `Any` to `T` or throw
///
/// - throws: `DecodingError.typeMismatch(expected, actual, metadata)`
public func cast<T>(_ object: Any) throws -> T {
guard let result = object as? T else {
let metadata = DecodingError.Metadata(object: object)
throw DecodingError.typeMismatch(expected: T.self, actual: type(of: object), metadata)
public func cast<T>(_ object: JSON) throws -> T {
guard let result = object.json as? T else {
throw DecodingError.typeMismatch(expected: T.self, actual: type(of: object.json), object.metadata)
}
return result
}
Expand All @@ -31,27 +30,27 @@ public protocol DynamicDecodable {
/// from their `decode` function.
///
/// - note: This is intended as a set-once thing.
static var decoder: (Any) throws -> DecodedType {get set}
static var decoder: (JSON) throws -> DecodedType {get set}
}

extension Decodable where Self: DynamicDecodable, Self.DecodedType == Self {
public static func decode(_ json: Any) throws -> Self {
public static func decode(_ json: JSON) throws -> Self {
return try decoder(json)

}
}

extension String: Decodable, DynamicDecodable {
public static var decoder: (Any) throws -> String = { try cast($0) }
public static var decoder: (JSON) throws -> String = { try cast($0) }
}
extension Int: Decodable, DynamicDecodable {
public static var decoder: (Any) throws -> Int = { try cast($0) }
public static var decoder: (JSON) throws -> Int = { try cast($0) }
}
extension Double: Decodable, DynamicDecodable {
public static var decoder: (Any) throws -> Double = { try cast($0) }
public static var decoder: (JSON) throws -> Double = { try cast($0) }
}
extension Bool: Decodable, DynamicDecodable {
public static var decoder: (Any) throws -> Bool = { try cast($0) }
public static var decoder: (JSON) throws -> Bool = { try cast($0) }
}

private let iso8601DateFormatter: DateFormatter = {
Expand All @@ -63,7 +62,7 @@ private let iso8601DateFormatter: DateFormatter = {

extension Date: Decodable, DynamicDecodable {
/// Default decoder is `Date.decoder(using: iso8601DateFormatter)`
public static var decoder: (Any) throws -> Date = Date.decoder(using: iso8601DateFormatter)
public static var decoder: (JSON) throws -> Date = Date.decoder(using: iso8601DateFormatter)

/// Create a decode closure using a given formatter
///
Expand All @@ -72,12 +71,11 @@ extension Date: Decodable, DynamicDecodable {
/// let formatter = DateFormatter(...)
/// Date.decoder = Date.decoder(using: formatter)
/// ```
public static func decoder(using formatter: DateFormatter) -> (Any) throws -> Date {
return { object in
let string = try String.decode(object)
public static func decoder(using formatter: DateFormatter) -> (JSON) throws -> Date {
return { json in
let string = try String.decode(json)
guard let date = formatter.date(from: string) else {
let metadata = DecodingError.Metadata(object: object)
throw DecodingError.rawRepresentableInitializationError(rawValue: string, metadata)
throw DecodingError.rawRepresentableInitializationError(rawValue: string, json.metadata)
}
return date
}
Expand All @@ -86,26 +84,25 @@ extension Date: Decodable, DynamicDecodable {
}

extension NSDictionary: Decodable {
public static func decode(_ json: Any) throws -> Self {
public static func decode(_ json: JSON) throws -> Self {
return try cast(json)
}
}

extension NSArray: DynamicDecodable {
public static var decoder: (Any) throws -> NSArray = { try cast($0) }
public static func decode(_ json: Any) throws -> NSArray {
public static var decoder: (JSON) throws -> NSArray = { try cast($0) }
public static func decode(_ json: JSON) throws -> NSArray {
return try decoder(json)
}

}


extension URL: DynamicDecodable, Decodable {
public static var decoder: (Any) throws -> URL = { object in
let string = try String.decode(object)
public static var decoder: (JSON) throws -> URL = { json in
let string = try String.decode(json)
guard let url = URL(string: string) else {
let metadata = DecodingError.Metadata(object: object)
throw DecodingError.rawRepresentableInitializationError(rawValue: string, metadata)
throw DecodingError.rawRepresentableInitializationError(rawValue: string, json.metadata)
}
return url
}
Expand Down
15 changes: 11 additions & 4 deletions Sources/Decodable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,18 @@
import Foundation

public protocol Decodable {
static func decode(_ json: Any) throws -> Self
static func decode(_ json: JSON) throws -> Self
}

extension Decodable {
static func decode(_ json: Any) throws -> Self {
return try Self.decode(JSON(value: json))
}
}


extension Dictionary where Key: Decodable, Value: Decodable {
public static func decode(_ j: Any) throws -> Dictionary {
public static func decode(_ j: JSON) throws -> Dictionary {
return try Dictionary.decoder(key: Key.decode, value: Value.decode)(j)
}
}
Expand All @@ -32,7 +38,7 @@ extension Dictionary where Key: Decodable, Value: Any {
*/

extension Array where Element: Decodable {
public static func decode(_ j: Any, ignoreInvalidObjects: Bool = false) throws -> [Element] {
public static func decode(_ j: JSON, ignoreInvalidObjects: Bool = false) throws -> [Element] {
if ignoreInvalidObjects {
return try [Element?].decoder { try? Element.decode($0) }(j).flatMap {$0}
} else {
Expand All @@ -43,7 +49,7 @@ extension Array where Element: Decodable {




/*
// MARK: Helpers

/// Attempt to decode one of multiple objects in order until: A: we get a positive match, B: we throw an exception if the last object does not decode
Expand All @@ -67,3 +73,4 @@ public func decodeArrayAsOneOf(_ json: Any, objectTypes: Decodable.Type...) thro
return try objectTypes.last!.decode($0)
}
}
*/
12 changes: 6 additions & 6 deletions Sources/Decoders.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ extension Optional {
///
/// - parameter wrappedDecoder: A decoder (decode closure) for the wrapped type
/// - returns: A closure takes an JSON object, checks it's `NSNull`, if so returns `nil`, otherwise calls the wrapped decode closure.
static func decoder(_ wrappedDecoder: @escaping (Any) throws -> Wrapped) -> (Any) throws -> Wrapped? {
static func decoder(_ wrappedDecoder: @escaping (JSON) throws -> Wrapped) -> (JSON) throws -> Wrapped? {
return { json in
if json is NSNull {
if json.json is NSNull {
return nil
} else {
return try wrappedDecoder(json)
Expand All @@ -36,9 +36,9 @@ extension Array {
/// - parameter elementDecoder: A decoder (decode closure) for the `Element` type
/// - throws: if `NSArray.decode` throws or any element decode closure throws
/// - returns: A closure that takes an `NSArray` and maps it using the element decode closure
public static func decoder(_ elementDecoder: @escaping (Any) throws -> Element) -> (Any) throws -> Array<Element> {
public static func decoder(_ elementDecoder: @escaping (JSON) throws -> Element) -> (JSON) throws -> Array<Element> {
return { json in
return try NSArray.decode(json).map { try elementDecoder($0) }
return try NSArray.decode(json).map { try elementDecoder(JSON(value: $0, metadata: json.metadata)) }
}
}
}
Expand All @@ -51,11 +51,11 @@ extension Dictionary {
/// - parameter key: A decoder (decode closure) for the `Key` type
/// - parameter value: A decoder (decode closure) for the `Value` type
/// - returns: A closure that takes a `NSDictionary` and "maps" it using key and value decode closures
public static func decoder(key keyDecoder: @escaping (Any) throws -> Key, value valueDecoder: @escaping (Any) throws -> Value) -> (Any) throws -> Dictionary {
public static func decoder(key keyDecoder: @escaping (JSON) throws -> Key, value valueDecoder: @escaping (JSON) throws -> Value) -> (JSON) throws -> Dictionary {
return { json in
var dict = Dictionary()
for (key, value) in try NSDictionary.decode(json) {
try dict[keyDecoder(key)] = valueDecoder(value)
try dict[keyDecoder(json.with(json: key))] = valueDecoder(json.with(json: value))
}
return dict
}
Expand Down
6 changes: 5 additions & 1 deletion Sources/DecodingError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,12 @@ public enum DecodingError: Error, Equatable {
/// The JSON key path to the object that failed to be decoded
public var path: [String]

var file: String?
var line: Int?
var function: String?

/// The JSON object that failed to be decoded
public let object: Any
public var object: Any

/// The root JSON object for which the `path` can be used to find `object`
public var rootObject: Any?
Expand Down
Loading