Skip to content

Commit 6dc7b55

Browse files
committed
CodecKey lifted and FailContainer added
1 parent f9355b2 commit 6dc7b55

File tree

12 files changed

+242
-61
lines changed

12 files changed

+242
-61
lines changed

sources/Codable/Decodable/DecodingKey.swift renamed to sources/Codable/CodecKey.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/// The coding key used to identify encoding/decoding containers.
2-
internal struct DecodingKey: CodingKey {
2+
internal struct CodecKey: CodingKey {
33
/// The integer value of the coding key.
44
let index: Int
55
/// Designated initializer.

sources/Codable/Decodable/Containers/DecodingKeyed.swift

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,14 @@ extension ShadowDecoder {
2626
/// - parameter decoder: The `Decoder` instance in charge of decoding the CSV data.
2727
init(decoder: ShadowDecoder) throws {
2828
switch decoder.codingPath.count {
29-
case 0: self.focus = .file
30-
case 1: let key = decoder.codingPath[0]
31-
let r = try key.intValue ?! DecodingError.invalidKey(forRow: key, codingPath: decoder.codingPath)
32-
self.focus = .row(r)
33-
default: throw DecodingError.invalidContainerRequest(codingPath: decoder.codingPath)
29+
case 0:
30+
self.focus = .file
31+
case 1:
32+
let key = decoder.codingPath[0]
33+
let r = try key.intValue ?! DecodingError.invalidKey(forRow: key, codingPath: decoder.codingPath)
34+
self.focus = .row(r)
35+
default:
36+
throw DecodingError.invalidContainerRequest(codingPath: decoder.codingPath)
3437
}
3538
self.decoder = decoder
3639
}
@@ -76,7 +79,7 @@ extension ShadowDecoder.KeyedContainer {
7679
switch self.focus {
7780
case .file:
7881
guard let rowIndex = key.intValue else { throw DecodingError.invalidKey(forRow: key, codingPath: self.codingPath + [key]) }
79-
var codingPath = self.decoder.codingPath; codingPath.append(DecodingKey(rowIndex))
82+
var codingPath = self.decoder.codingPath; codingPath.append(CodecKey(rowIndex))
8083
let decoder = ShadowDecoder(source: self.decoder.source, codingPath: codingPath)
8184
return KeyedDecodingContainer(ShadowDecoder.KeyedContainer<NestedKey>(unsafeDecoder: decoder, rowIndex: rowIndex))
8285
case .row: throw DecodingError.invalidContainerRequest(codingPath: self.codingPath)
@@ -87,7 +90,7 @@ extension ShadowDecoder.KeyedContainer {
8790
switch self.focus {
8891
case .file:
8992
guard let rowIndex = key.intValue else { throw DecodingError.invalidKey(forRow: key, codingPath: self.codingPath + [key]) }
90-
var codingPath = self.decoder.codingPath; codingPath.append(DecodingKey(rowIndex))
93+
var codingPath = self.decoder.codingPath; codingPath.append(CodecKey(rowIndex))
9194
let decoder = ShadowDecoder(source: self.decoder.source, codingPath: codingPath)
9295
return ShadowDecoder.UnkeyedContainer(unsafeDecoder: decoder, rowIndex: rowIndex)
9396
case .row: throw DecodingError.invalidContainerRequest(codingPath: self.codingPath)
@@ -98,7 +101,7 @@ extension ShadowDecoder.KeyedContainer {
98101
switch self.focus {
99102
case .file:
100103
guard let rowIndex = key.intValue else { throw DecodingError.invalidKey(forRow: key, codingPath: self.codingPath + [key]) }
101-
var codingPath = self.decoder.codingPath; codingPath.append(DecodingKey(rowIndex))
104+
var codingPath = self.decoder.codingPath; codingPath.append(CodecKey(rowIndex))
102105
return ShadowDecoder(source: self.decoder.source, codingPath: codingPath)
103106
case .row: throw DecodingError.invalidContainerRequest(codingPath: self.codingPath)
104107
}
@@ -107,7 +110,7 @@ extension ShadowDecoder.KeyedContainer {
107110
func superDecoder() throws -> Decoder {
108111
switch self.focus {
109112
case .file:
110-
var codingPath = self.decoder.codingPath; codingPath.append(DecodingKey(0))
113+
var codingPath = self.decoder.codingPath; codingPath.append(CodecKey(0))
111114
return ShadowDecoder(source: self.decoder.source, codingPath: codingPath)
112115
case .row: throw DecodingError.invalidContainerRequest(codingPath: self.codingPath)
113116
}
@@ -276,7 +279,7 @@ extension ShadowDecoder.KeyedContainer {
276279
switch self.focus {
277280
case .row(let rowIndex):
278281
index = (rowIndex, try self.decoder.source.fieldIndex(forKey: key, codingPath: self.codingPath))
279-
var codingPath = self.decoder.codingPath; codingPath.append(DecodingKey(index.field))
282+
var codingPath = self.decoder.codingPath; codingPath.append(CodecKey(index.field))
280283
decoder = ShadowDecoder(source: self.decoder.source, codingPath: codingPath)
281284
case .file:
282285
guard let rowIndex = key.intValue else {
@@ -289,8 +292,8 @@ extension ShadowDecoder.KeyedContainer {
289292

290293
index = (rowIndex, 0)
291294
var codingPath = self.decoder.codingPath
292-
codingPath.append(DecodingKey(index.row))
293-
codingPath.append(DecodingKey(index.field))
295+
codingPath.append(CodecKey(index.row))
296+
codingPath.append(CodecKey(index.field))
294297
decoder = ShadowDecoder(source: self.decoder.source, codingPath: codingPath)
295298
}
296299

sources/Codable/Decodable/Containers/DecodingUnkeyed.swift

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,14 @@ extension ShadowDecoder {
2828
/// - has a single coding key with an integer value (impliying a unkeyed container traversing a single CSV row).
2929
init(decoder: ShadowDecoder) throws {
3030
switch decoder.codingPath.count {
31-
case 0: self.focus = .file
32-
case 1: let key = decoder.codingPath[0]
33-
let r = try key.intValue ?! DecodingError.invalidKey(forRow: key, codingPath: decoder.codingPath)
31+
case 0:
32+
self.focus = .file
33+
case 1:
34+
let key = decoder.codingPath[0]
35+
let r = try key.intValue ?! DecodingError.invalidKey(forRow: key, codingPath: decoder.codingPath)
3436
self.focus = .row(r)
35-
default: throw DecodingError.invalidContainerRequest(codingPath: decoder.codingPath)
37+
default:
38+
throw DecodingError.invalidContainerRequest(codingPath: decoder.codingPath)
3639
}
3740
self.currentIndex = 0
3841
self.decoder = decoder
@@ -63,7 +66,7 @@ extension ShadowDecoder.UnkeyedContainer {
6366
switch self.focus {
6467
case .file:
6568
let rowIndex = self.currentIndex
66-
var codingPath = self.decoder.codingPath; codingPath.append(DecodingKey(rowIndex))
69+
var codingPath = self.decoder.codingPath; codingPath.append(CodecKey(rowIndex))
6770
let decoder = ShadowDecoder(source: self.decoder.source, codingPath: codingPath)
6871
self.currentIndex += 1
6972
return KeyedDecodingContainer(ShadowDecoder.KeyedContainer<NestedKey>(unsafeDecoder: decoder, rowIndex: rowIndex))
@@ -75,7 +78,7 @@ extension ShadowDecoder.UnkeyedContainer {
7578
switch self.focus {
7679
case .file:
7780
let rowIndex = self.currentIndex
78-
var codingPath = self.decoder.codingPath; codingPath.append(DecodingKey(rowIndex))
81+
var codingPath = self.decoder.codingPath; codingPath.append(CodecKey(rowIndex))
7982
let decoder = ShadowDecoder(source: self.decoder.source, codingPath: codingPath)
8083
self.currentIndex += 1
8184
return Self(unsafeDecoder: decoder, rowIndex: rowIndex)
@@ -86,7 +89,7 @@ extension ShadowDecoder.UnkeyedContainer {
8689
mutating func superDecoder() throws -> Decoder {
8790
switch self.focus {
8891
case .file:
89-
var codingPath = self.decoder.codingPath; codingPath.append(DecodingKey(self.currentIndex))
92+
var codingPath = self.decoder.codingPath; codingPath.append(CodecKey(self.currentIndex))
9093
let result = ShadowDecoder(source: self.decoder.source, codingPath: codingPath)
9194
self.currentIndex += 1
9295
return result
@@ -200,7 +203,7 @@ extension ShadowDecoder.UnkeyedContainer {
200203
} else if T.self == URL.self {
201204
result = try self.fieldContainer().decode(URL.self) as! T
202205
} else {
203-
var codingPath = self.decoder.codingPath; codingPath.append(DecodingKey(self.currentIndex))
206+
var codingPath = self.decoder.codingPath; codingPath.append(CodecKey(self.currentIndex))
204207
let decoder = ShadowDecoder(source: self.decoder.source, codingPath: codingPath)
205208
result = try T(from: decoder)
206209
}
@@ -292,7 +295,7 @@ extension ShadowDecoder.UnkeyedContainer {
292295
switch self.focus {
293296
case .row(let rowIndex):
294297
index = (rowIndex, self.currentIndex)
295-
var codingPath = self.decoder.codingPath; codingPath.append(DecodingKey(index.field))
298+
var codingPath = self.decoder.codingPath; codingPath.append(CodecKey(index.field))
296299
decoder = ShadowDecoder(source: self.decoder.source, codingPath: codingPath)
297300
case .file:
298301
// Values are only allowed to be decoded directly from a nested container in "file level" if the CSV rows have a single column.
@@ -301,8 +304,8 @@ extension ShadowDecoder.UnkeyedContainer {
301304
}
302305
index = (self.currentIndex, 0)
303306
var codingPath = self.decoder.codingPath
304-
codingPath.append(DecodingKey(index.row))
305-
codingPath.append(DecodingKey(index.field))
307+
codingPath.append(CodecKey(index.row))
308+
codingPath.append(CodecKey(index.field))
306309
decoder = ShadowDecoder(source: self.decoder.source, codingPath: codingPath)
307310
}
308311

sources/Codable/Decodable/Containers/DecodingValue.swift

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -27,15 +27,19 @@ extension ShadowDecoder {
2727
/// - throws: `DecodingError` exclusively.
2828
init(decoder: ShadowDecoder) throws {
2929
switch decoder.codingPath.count {
30-
case 2: let key = (row: decoder.codingPath[0], field: decoder.codingPath[1])
31-
let r = try key.row.intValue ?! DecodingError.invalidKey(forRow: key.row, codingPath: decoder.codingPath)
32-
let f = try decoder.source.fieldIndex(forKey: key.field, codingPath: decoder.codingPath)
30+
case 2:
31+
let key = (row: decoder.codingPath[0], field: decoder.codingPath[1])
32+
let r = try key.row.intValue ?! DecodingError.invalidKey(forRow: key.row, codingPath: decoder.codingPath)
33+
let f = try decoder.source.fieldIndex(forKey: key.field, codingPath: decoder.codingPath)
3334
self.focus = .field(r, f)
34-
case 1: let key = decoder.codingPath[0]
35-
let r = try key.intValue ?! DecodingError.invalidKey(forRow: key, codingPath: decoder.codingPath)
36-
self.focus = .row(r)
37-
case 0: self.focus = .file
38-
default: throw DecodingError.invalidContainerRequest(codingPath: decoder.codingPath)
35+
case 1:
36+
let key = decoder.codingPath[0]
37+
let r = try key.intValue ?! DecodingError.invalidKey(forRow: key, codingPath: decoder.codingPath)
38+
self.focus = .row(r)
39+
case 0:
40+
self.focus = .file
41+
default:
42+
throw DecodingError.invalidContainerRequest(codingPath: decoder.codingPath)
3943
}
4044
self.decoder = decoder
4145
}
@@ -243,12 +247,12 @@ private extension ShadowDecoder.SingleValueContainer {
243247
// Values are only allowed to be decoded directly from a single value container in "row level" if the CSV has single column rows.
244248
guard source.numFields == 1 else { throw DecodingError.invalidNestedRequired(codingPath: self.codingPath) }
245249
let string = try source.field(at: rowIndex, 0)
246-
return try transform(string) ?! DecodingError.invalid(type: T.self, string: string, codingPath: self.codingPath + [DecodingKey(0)])
250+
return try transform(string) ?! DecodingError.invalid(type: T.self, string: string, codingPath: self.codingPath + [CodecKey(0)])
247251
case .file:
248252
// Values are only allowed to be decoded directly from a single value container in "file level" if the CSV file has a single row with a single column.
249253
if source.isRowAtEnd(index: 1), source.numFields == 1 {
250254
let string = try self.decoder.source.field(at: 0, 0)
251-
return try transform(string) ?! DecodingError.invalid(type: T.self, string: string, codingPath: self.codingPath + [DecodingKey(0), DecodingKey(0)])
255+
return try transform(string) ?! DecodingError.invalid(type: T.self, string: string, codingPath: self.codingPath + [CodecKey(0), CodecKey(0)])
252256
} else {
253257
throw DecodingError.invalidNestedRequired(codingPath: self.codingPath)
254258
}

sources/Codable/Decodable/Shadow/DecodingSource.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -148,20 +148,20 @@ fileprivate extension DecodingError {
148148
///
149149
/// If the buffer strategy is too restrictive, the previosly decoded rows are being discarded.
150150
static func expiredCache(rowIndex: Int, fieldIndex: Int) -> DecodingError {
151-
let fieldKey = DecodingKey(fieldIndex)
152-
return DecodingError.keyNotFound(fieldKey, .init(codingPath: [DecodingKey(rowIndex), fieldKey],
151+
let fieldKey = CodecKey(fieldIndex)
152+
return DecodingError.keyNotFound(fieldKey, .init(codingPath: [CodecKey(rowIndex), fieldKey],
153153
debugDescription: "A previously decoded row has been discarded. Change the decoder's buffering strategy and try again."))
154154
}
155155
/// Error raised when a row is queried, which is outside the CSV number of rows.
156156
static func rowOutOfBounds(rowIndex: Int, rowCount: Int) -> DecodingError {
157-
let rowKey = DecodingKey(rowIndex)
157+
let rowKey = CodecKey(rowIndex)
158158
return DecodingError.keyNotFound(rowKey, .init(codingPath: [rowKey],
159159
debugDescription: "The reader reached the end of the CSV file (num rows: \(rowCount)). Therefore the requested row at position '\(rowIndex)' didn't exist."))
160160
}
161161
/// Error raised when a given field index is out of bounds.
162162
static func fieldOutOfBounds(rowIndex: Int, fieldIndex: Int, fieldCount: Int) -> DecodingError {
163-
let fieldKey = DecodingKey(fieldIndex)
164-
return DecodingError.keyNotFound(fieldKey, .init(codingPath: [DecodingKey(rowIndex), fieldKey],
163+
let fieldKey = CodecKey(fieldIndex)
164+
return DecodingError.keyNotFound(fieldKey, .init(codingPath: [CodecKey(rowIndex), fieldKey],
165165
debugDescription: "The provided field index is out of bounds."))
166166
}
167167
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
extension ShadowEncoder {
2+
/// An encoding container that always fail.
3+
///
4+
/// This container is created to circumvent the *non-throwing* `Encoder` API.
5+
struct FailContainer<Key:CodingKey>: SingleValueEncodingContainer, UnkeyedEncodingContainer, KeyedEncodingContainerProtocol {
6+
/// The error to throw at all times.
7+
let error: Swift.Error
8+
/// The encoder containing the coding path.
9+
let encoder: ShadowEncoder
10+
11+
init(error: Swift.Error, encoder: ShadowEncoder) {
12+
self.error = error
13+
self.encoder = encoder
14+
}
15+
16+
var count: Int { 0 }
17+
var codingPath: [CodingKey] { self.encoder.codingPath }
18+
19+
mutating func nestedContainer<NestedKey:CodingKey>(keyedBy keyType: NestedKey.Type) -> KeyedEncodingContainer<NestedKey> {
20+
.init(FailContainer<NestedKey>(error: self.error, encoder: self.encoder))
21+
}
22+
mutating func nestedContainer<NestedKey:CodingKey>(keyedBy keyType: NestedKey.Type, forKey key: Key) -> KeyedEncodingContainer<NestedKey> {
23+
self.nestedContainer(keyedBy: keyType)
24+
}
25+
26+
mutating func nestedUnkeyedContainer() -> UnkeyedEncodingContainer { self }
27+
mutating func nestedUnkeyedContainer(forKey key: Key) -> UnkeyedEncodingContainer { self }
28+
mutating func superEncoder() -> Encoder { self.encoder }
29+
mutating func superEncoder(forKey key: Key) -> Encoder { self.encoder }
30+
31+
mutating func encodeNil(forKey key: Key) throws { throw self.error }
32+
mutating func encodeNil() throws { throw self.error }
33+
mutating func encode<T:Encodable>(_ value: T, forKey key: Key) throws { throw self.error }
34+
mutating func encode<T:Encodable>(_ value: T) throws { throw self.error }
35+
mutating func encode<T>(contentsOf sequence: T) throws where T:Sequence, T.Element:Encodable { throw self.error }
36+
mutating func encodeConditional<T>(_ object: T) throws where T:AnyObject, T:Encodable { throw self.error }
37+
}
38+
}

sources/Codable/Encodable/Containers/EncodingKeyed.swift

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,20 @@ extension ShadowEncoder {
1616
self.focus = .row(rowIndex)
1717
}
1818

19-
/// Creates a unkeyed container only if the passed encoder's coding path is valid.
19+
/// Creates a keyed container only if the passed encoder's coding path is valid.
2020
/// - parameter encoder: The `Encoder` instance in charge of encoding CSV data.
21-
init(encoder: ShadowEncoder) {
22-
fatalError()
21+
init(encoder: ShadowEncoder) throws {
22+
switch encoder.codingPath.count {
23+
case 0:
24+
self.focus = .file
25+
case 1:
26+
let key = encoder.codingPath[0]
27+
let r = try key.intValue ?! DecodingError.invalidKey(forRow: key, codingPath: encoder.codingPath)
28+
self.focus = .row(r)
29+
default:
30+
throw DecodingError.invalidContainerRequest(codingPath: encoder.codingPath)
31+
}
32+
self.encoder = encoder
2333
}
2434

2535
var codingPath: [CodingKey] {
@@ -189,3 +199,27 @@ extension ShadowEncoder.KeyedContainer {
189199
case row(Int)
190200
}
191201
}
202+
203+
fileprivate extension DecodingError {
204+
/// Error raised when a coding key representing a row within the CSV file cannot be transformed into an integer value.
205+
/// - parameter codingPath: The whole coding path, including the invalid row key.
206+
static func invalidKey(forRow key: CodingKey, codingPath: [CodingKey]) -> DecodingError {
207+
DecodingError.keyNotFound(key, .init(
208+
codingPath: codingPath,
209+
debugDescription: "The coding key identifying a CSV row couldn't be transformed into an integer value."))
210+
}
211+
/// Error raised when a single value container is requested on an invalid coding path.
212+
/// - parameter codingPath: The full chain of containers which generated this error.
213+
static func invalidContainerRequest(codingPath: [CodingKey]) -> DecodingError {
214+
DecodingError.dataCorrupted(
215+
Context(codingPath: codingPath,
216+
debugDescription: "CSV doesn't support more than two nested decoding container.")
217+
)
218+
}
219+
// /// Error raised when a value is decoded, but a container was expected by the decoder.
220+
// static func invalidNestedRequired(codingPath: [CodingKey]) -> DecodingError {
221+
// DecodingError.dataCorrupted(.init(
222+
// codingPath: codingPath,
223+
// debugDescription: "A nested container is needed to decode CSV row values"))
224+
// }
225+
}

0 commit comments

Comments
 (0)