-
Notifications
You must be signed in to change notification settings - Fork 75
Expand file tree
/
Copy pathKeyedDecodingContainer.swift
More file actions
325 lines (290 loc) · 13.4 KB
/
KeyedDecodingContainer.swift
File metadata and controls
325 lines (290 loc) · 13.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
import Foundation
extension ShadowDecoder {
/// Keyed container for the CSV shadow decoder.
///
/// This container lets you randomly access all the CSV rows or all the fields within a single rows.
struct KeyedContainer<Key>: KeyedDecodingContainerProtocol where Key:CodingKey {
/// The representation of the decoding process point-in-time.
private let _decoder: ShadowDecoder
/// The container's target (or level).
private let _focus: _Focus
/// Fast initializer that doesn't perform any checks on the coding path (assuming it is valid).
/// - parameter decoder: The `Decoder` instance in charge of decoding the CSV data.
/// - parameter rowIndex: The CSV row targeted for decoding.
init(unsafeDecoder decoder: ShadowDecoder, rowIndex: Int) {
self._decoder = decoder
self._focus = .row(rowIndex)
}
/// Creates a keyed container only if the passed decoder's coding path is valid.
///
/// This initializer only allows the creation of a container when the decoder's coding path:
/// - is empty (implying a keyed container traversing the CSV file),
/// - has a single coding key with an integer value (implying a keyed container traversing a single CSV row).
/// - parameter decoder: The `Decoder` instance in charge of decoding the CSV data.
init(decoder: ShadowDecoder) throws {
switch decoder.codingPath.count {
case 0:
self._focus = .file
case 1:
let key = decoder.codingPath[0]
let r = try key.intValue ?> CSVDecoder.Error._invalidRowKey(forKey: key, codingPath: decoder.codingPath)
self._focus = .row(r)
default:
throw CSVDecoder.Error._invalidContainerRequest(forKey: decoder.codingPath.last!, codingPath: decoder.codingPath)
}
self._decoder = decoder
}
var codingPath: [CodingKey] {
self._decoder.codingPath
}
var allKeys: [Key] {
self._decoder.source._withUnsafeGuaranteedRef { [focus = self._focus] in
switch focus {
case .file:
guard let numRows = $0.numRows, numRows > 0 else { return [] }
return (0..<numRows).compactMap { Key(intValue: $0) }
case .row:
let numFields = $0.numExpectedFields
guard numFields > 0 else { return [] }
let numberKeys = (0..<numFields).compactMap { Key(intValue: $0) }
guard numberKeys.isEmpty else { return numberKeys }
return $0.headers.compactMap { Key(stringValue: $0) }
}
}
}
func contains(_ key: Key) -> Bool {
self._decoder.source._withUnsafeGuaranteedRef { [focus = self._focus] in
switch focus {
case .file:
guard let index = key.intValue else { return false }
return $0.contains(rowIndex: index)
case .row:
if let index = key.intValue {
return index >= 0 && index < $0.numExpectedFields
} else {
return $0.headers.contains(key.stringValue)
}
}
}
}
}
}
extension ShadowDecoder.KeyedContainer {
func nestedContainer<NestedKey>(keyedBy type: NestedKey.Type, forKey key: Key) throws -> KeyedDecodingContainer<NestedKey> where NestedKey : CodingKey {
switch self._focus {
case .file:
guard let rowIndex = key.intValue else { throw CSVDecoder.Error._invalidRowKey(forKey: key, codingPath: self.codingPath + [key]) }
var codingPath = self._decoder.codingPath; codingPath.append(IndexKey(rowIndex))
let decoder = ShadowDecoder(source: self._decoder.source, codingPath: codingPath)
return KeyedDecodingContainer(ShadowDecoder.KeyedContainer<NestedKey>(unsafeDecoder: decoder, rowIndex: rowIndex))
case .row: throw CSVDecoder.Error._invalidContainerRequest(forKey: key, codingPath: self.codingPath)
}
}
func nestedUnkeyedContainer(forKey key: Key) throws -> UnkeyedDecodingContainer {
switch self._focus {
case .file:
guard let rowIndex = key.intValue else { throw CSVDecoder.Error._invalidRowKey(forKey: key, codingPath: self.codingPath + [key]) }
var codingPath = self._decoder.codingPath; codingPath.append(IndexKey(rowIndex))
let decoder = ShadowDecoder(source: self._decoder.source, codingPath: codingPath)
return ShadowDecoder.UnkeyedContainer(unsafeDecoder: decoder, rowIndex: rowIndex)
case .row: throw CSVDecoder.Error._invalidContainerRequest(forKey: key, codingPath: self.codingPath)
}
}
func superDecoder(forKey key: Key) throws -> Decoder {
switch self._focus {
case .file:
guard let rowIndex = key.intValue else { throw CSVDecoder.Error._invalidRowKey(forKey: key, codingPath: self.codingPath + [key]) }
var codingPath = self._decoder.codingPath; codingPath.append(IndexKey(rowIndex))
return ShadowDecoder(source: self._decoder.source, codingPath: codingPath)
case .row: throw CSVDecoder.Error._invalidContainerRequest(forKey: key, codingPath: self.codingPath)
}
}
func superDecoder() throws -> Decoder {
switch self._focus {
case .file:
var codingPath = self._decoder.codingPath; codingPath.append(IndexKey(0))
return ShadowDecoder(source: self._decoder.source, codingPath: codingPath)
case .row: throw CSVDecoder.Error._invalidContainerRequest(forKey: NameKey(index: 0, name: "super"), codingPath: self.codingPath)
}
}
}
extension ShadowDecoder.KeyedContainer {
func decode(_ type: String.Type, forKey key: Key) throws -> String {
try self._fieldContainer(forKey: key).decode(String.self)
}
func decodeNil(forKey key: Key) throws -> Bool {
try self._fieldContainer(forKey: key).decodeNil()
}
func decode(_ type: Bool.Type, forKey key: Key) throws -> Bool {
try self._fieldContainer(forKey: key).decode(Bool.self)
}
func decode(_ type: Int.Type, forKey key: Key) throws -> Int {
try self._fieldContainer(forKey: key).decode(Int.self)
}
func decode(_ type: Int8.Type, forKey key: Key) throws -> Int8 {
try self._fieldContainer(forKey: key).decode(Int8.self)
}
func decode(_ type: Int16.Type, forKey key: Key) throws -> Int16 {
try self._fieldContainer(forKey: key).decode(Int16.self)
}
func decode(_ type: Int32.Type, forKey key: Key) throws -> Int32 {
try self._fieldContainer(forKey: key).decode(Int32.self)
}
func decode(_ type: Int64.Type, forKey key: Key) throws -> Int64 {
try self._fieldContainer(forKey: key).decode(Int64.self)
}
func decode(_ type: UInt.Type, forKey key: Key) throws -> UInt {
try self._fieldContainer(forKey: key).decode(UInt.self)
}
func decode(_ type: UInt8.Type, forKey key: Key) throws -> UInt8 {
try self._fieldContainer(forKey: key).decode(UInt8.self)
}
func decode(_ type: UInt16.Type, forKey key: Key) throws -> UInt16 {
try self._fieldContainer(forKey: key).decode(UInt16.self)
}
func decode(_ type: UInt32.Type, forKey key: Key) throws -> UInt32 {
try self._fieldContainer(forKey: key).decode(UInt32.self)
}
func decode(_ type: UInt64.Type, forKey key: Key) throws -> UInt64 {
try self._fieldContainer(forKey: key).decode(UInt64.self)
}
func decode(_ type: Float.Type, forKey key: Key) throws -> Float {
try self._fieldContainer(forKey: key).decode(Float.self)
}
func decode(_ type: Double.Type, forKey key: Key) throws -> Double {
try self._fieldContainer(forKey: key).decode(Double.self)
}
func decode<T>(_ type: T.Type, forKey key: Key) throws -> T where T:Decodable {
if T.self == Date.self {
return try self._fieldContainer(forKey: key).decode(Date.self) as! T
} else if T.self == TimeZone.self {
return try self._fieldContainer(forKey: key).decode(TimeZone.self) as! T
} else if T.self == Data.self {
return try self._fieldContainer(forKey: key).decode(Data.self) as! T
} else if T.self == Decimal.self {
return try self._fieldContainer(forKey: key).decode(Decimal.self) as! T
} else if T.self == URL.self {
return try self._fieldContainer(forKey: key).decode(URL.self) as! T
} else {
var codingPath = self._decoder.codingPath; codingPath.append(key)
return try T(from: ShadowDecoder(source: self._decoder.source, codingPath: codingPath))
}
}
}
//extension ShadowDecoder.KeyedContainer {
// func decodeIfPresent(_ type: String.Type, forKey key: Key) throws -> String? {
// try? self.decode(String.self, forKey: key)
// }
//
// func decodeIfPresent(_ type: Bool.Type, forKey key: Key) throws -> Bool? {
// try? self.decode(Bool.self, forKey: key)
// }
//
// func decodeIfPresent(_ type: Int.Type, forKey key: Key) throws -> Int? {
// try? self.decode(Int.self, forKey: key)
// }
//
// func decodeIfPresent(_ type: Int8.Type, forKey key: Key) throws -> Int8? {
// try? self.decode(Int8.self, forKey: key)
// }
//
// func decodeIfPresent(_ type: Int16.Type, forKey key: Key) throws -> Int16? {
// try? self.decode(Int16.self, forKey: key)
// }
//
// func decodeIfPresent(_ type: Int32.Type, forKey key: Key) throws -> Int32? {
// try? self.decode(Int32.self, forKey: key)
// }
//
// func decodeIfPresent(_ type: Int64.Type, forKey key: Key) throws -> Int64? {
// try? self.decode(Int64.self, forKey: key)
// }
//
// func decodeIfPresent(_ type: UInt.Type, forKey key: Key) throws -> UInt? {
// try? self.decode(UInt.self, forKey: key)
// }
//
// func decodeIfPresent(_ type: UInt8.Type, forKey key: Key) throws -> UInt8? {
// try? self.decode(UInt8.self, forKey: key)
// }
//
// func decodeIfPresent(_ type: UInt16.Type, forKey key: Key) throws -> UInt16? {
// try? self.decode(UInt16.self, forKey: key)
// }
//
// func decodeIfPresent(_ type: UInt32.Type, forKey key: Key) throws -> UInt32? {
// try? self.decode(UInt32.self, forKey: key)
// }
//
// func decodeIfPresent(_ type: UInt64.Type, forKey key: Key) throws -> UInt64? {
// try? self.decode(UInt64.self, forKey: key)
// }
//
// func decodeIfPresent(_ type: Float.Type, forKey key: Key) throws -> Float? {
// try? self.decode(Float.self, forKey: key)
// }
//
// func decodeIfPresent(_ type: Double.Type, forKey key: Key) throws -> Double? {
// try? self.decode(Double.self, forKey: key)
// }
//
// func decodeIfPresent<T>(_ type: T.Type, forKey key: Key) throws -> T? where T:Decodable {
// try? self.decode(T.self, forKey: key)
// }
//}
// MARK: -
private extension ShadowDecoder.KeyedContainer {
/// CSV keyed container focus (i.e. where the container is able to operate on).
enum _Focus {
/// The container represents the whole CSV file and each decoding operation outputs a row/record.
case file
/// The container represents a CSV row and each decoding operation outputs a field.
case row(Int)
}
/// Returns a single value container to decode a single field within a row.
/// - parameter key: The coding key under which the `String` value is located.
/// - returns: The single value container holding the field decoding functionality.
func _fieldContainer(forKey key: Key) throws -> ShadowDecoder.SingleValueContainer {
let index: (row: Int, field: Int)
var codingPath = self._decoder.codingPath
codingPath.append(key)
switch self._focus {
case .row(let rowIndex):
index = (rowIndex, try self._decoder.source._withUnsafeGuaranteedRef { try $0.fieldIndex(forKey: key, codingPath: self.codingPath) })
case .file:
guard let rowIndex = key.intValue else { throw CSVDecoder.Error._invalidRowKey(forKey: key, codingPath: codingPath) }
// Values are only allowed to be decoded directly from a nested container in "file level" if the CSV rows have a single column.
guard self._decoder.source._withUnsafeGuaranteedRef({ $0.numExpectedFields == 1 }) else { throw CSVDecoder.Error._invalidNestedRequired(codingPath: self.codingPath) }
index = (rowIndex, 0)
codingPath.append(IndexKey(index.field))
}
let decoder = ShadowDecoder(source: self._decoder.source, codingPath: codingPath)
return ShadowDecoder.SingleValueContainer(unsafeDecoder: decoder, rowIndex: index.row, fieldIndex: index.field)
}
}
fileprivate extension CSVDecoder.Error {
/// Error raised when a coding key representing a row within the CSV file cannot be transformed into an integer value.
/// - parameter codingPath: The full decoding chain.
static func _invalidRowKey(forKey key: CodingKey, codingPath: [CodingKey]) -> CSVError<CSVDecoder> {
CSVError(.invalidPath,
reason: "The coding key identifying a CSV row couldn't be transformed into an integer value.",
help: "The provided coding key identifying a CSV row must implement `intValue`.",
userInfo: ["Coding path": codingPath, "Key": key])
}
/// Error raised when a keyed container is requested on an invalid coding path.
/// - parameter codingPath: The full decoding chain.
static func _invalidContainerRequest(forKey key: CodingKey, codingPath: [CodingKey]) -> CSVError<CSVDecoder> {
CSVError(.invalidPath,
reason: "A CSV doesn't support more than two nested decoding container.",
help: "Don't ask for a nested container on the targeted key for this coding path.",
userInfo: ["Coding path": codingPath, "Key": key])
}
/// Error raised when a value is decoded, but a container was expected by the decoder.
/// - parameter codingPath: The full decoding chain.
static func _invalidNestedRequired(codingPath: [CodingKey]) -> CSVError<CSVDecoder> {
CSVError(.invalidPath,
reason: "A nested container is needed to decode CSV row values",
help: "Request a nested container instead of trying to decode a value directly.",
userInfo: ["Coding path": codingPath])
}
}