Skip to content

Commit f7f96ca

Browse files
committed
Transform most errors to CSVError
1 parent 2cc834d commit f7f96ca

25 files changed

Lines changed: 333 additions & 291 deletions

sources/Delimiter.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ extension Delimiter {
3434
}
3535

3636
extension Delimiter {
37-
/// The separator to use between rows.
37+
/// The delimiter between rows.
3838
public struct Row: ExpressibleByNilLiteral, ExpressibleByStringLiteral, RawRepresentable {
3939
public let rawValue: String.UnicodeScalarView
4040

sources/Utils.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,15 +46,15 @@ internal extension DateFormatter {
4646
}()
4747
}
4848

49-
infix operator ?!
49+
infix operator ?>
5050

5151
internal extension Optional {
5252
/// Checks whether the value exists. If so, it returns the value; if not, it throws the given error.
5353
/// - parameter lhs: Optional value to check for existance.
5454
/// - parameter rhs: Swift error to throw in case of no value.
5555
/// - returns: The value (non-optional) passed as parameter.
5656
/// - throws: The Swift error returned on the right hand-side autoclosure.
57-
@_transparent static func ?!(lhs: Self, rhs: @autoclosure ()->Swift.Error) throws -> Wrapped {
57+
@_transparent static func ?>(lhs: Self, rhs: @autoclosure ()->Swift.Error) throws -> Wrapped {
5858
switch lhs {
5959
case .some(let v): return v
6060
case .none: throw rhs()

sources/declarative/decodable/Decoder.swift

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ extension CSVDecoder {
3434
/// Returns a value of the type you specify, decoded from a CSV file (given as a `Data` blob).
3535
/// - parameter type: The type of the value to decode from the supplied file.
3636
/// - parameter data: The data blob representing a CSV file.
37-
/// - throws: `DecodingError`, or `CSVError<CSVReader>`, or the error raised by your custom types.
3837
open func decode<T:Decodable>(_ type: T.Type, from data: Data) throws -> T {
3938
let reader = try CSVReader(input: data, configuration: self._configuration.readerConfiguration)
4039
let source = ShadowDecoder.Source(reader: reader, configuration: self._configuration, userInfo: self.userInfo)
@@ -44,22 +43,29 @@ extension CSVDecoder {
4443
/// Returns a value of the type you specify, decoded from a CSV file (given as a `String`).
4544
/// - parameter type: The type of the value to decode from the supplied file.
4645
/// - parameter string: A Swift string representing a CSV file.
47-
/// - throws: `DecodingError`, or `CSVError<CSVReader>`, or the error raised by your custom types.
4846
open func decode<T:Decodable>(_ type: T.Type, from string: String) throws -> T {
4947
let reader = try CSVReader(input: string, configuration: self._configuration.readerConfiguration)
5048
let source = ShadowDecoder.Source(reader: reader, configuration: self._configuration, userInfo: self.userInfo)
5149
return try T(from: ShadowDecoder(source: source, codingPath: []))
5250
}
5351

54-
/// Returns a value of the type you specify, decoded from a CSV file (being pointed by `url`).
52+
/// Returns a value of the type you specify, decoded from a CSV file (being pointed by the url).
5553
/// - parameter type: The type of the value to decode from the supplied file.
5654
/// - parameter url: The URL pointing to the file to decode.
57-
/// - throws: `DecodingError`, or `CSVError<CSVReader>`, or the error raised by your custom types.
5855
open func decode<T:Decodable>(_ type: T.Type, from url: URL) throws -> T {
5956
let reader = try CSVReader(input: url, configuration: self._configuration.readerConfiguration)
6057
let source = ShadowDecoder.Source(reader: reader, configuration: self._configuration, userInfo: self.userInfo)
6158
return try T(from: ShadowDecoder(source: source, codingPath: []))
6259
}
60+
61+
/// Returns a value of the type you specify, decoded from a CSV file (provided by the input stream).
62+
/// - parameter type: The type of the value to decode from the supplied file.
63+
/// - parameter stream: The input stream providing the raw bytes.
64+
open func decode<T:Decodable>(_ type: T.Type, from stream: InputStream) throws -> T {
65+
let reader = try CSVReader(input: stream, configuration: self._configuration.readerConfiguration)
66+
let source = ShadowDecoder.Source(reader: reader, configuration: self._configuration, userInfo: self.userInfo)
67+
return try T(from: ShadowDecoder(source: source, codingPath: []))
68+
}
6369
}
6470

6571
extension CSVDecoder {

sources/declarative/decodable/DecoderConfiguration.swift

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ extension CSVDecoder {
44
/// Configuration for how to read CSV data.
55
@dynamicMemberLookup public struct Configuration {
66
/// The underlying `CSVReader` configurations.
7-
@usableFromInline private(set) internal var readerConfiguration: CSVReader.Configuration
7+
@usableFromInline internal private(set) var readerConfiguration: CSVReader.Configuration
88
/// The strategy to use when decoding a `nil` representation.
99
public var nilStrategy: Strategy.NilDecoding
1010
/// The strategy to use when decoding Boolean values.
@@ -41,8 +41,6 @@ extension CSVDecoder {
4141
}
4242
}
4343

44-
// MARK: -
45-
4644
extension Strategy {
4745
/// The strategy to use for decoding `nil` representations.
4846
public enum NilDecoding {
@@ -149,3 +147,29 @@ extension Strategy {
149147
case sequential
150148
}
151149
}
150+
151+
// MARK: -
152+
153+
extension CSVDecoder: Failable {
154+
/// The type of error raised by the CSV decoder.
155+
public enum Error: Int {
156+
/// Some of the configuration values provided are invalid.
157+
case invalidConfiguration = 1
158+
/// The decoding coding path is invalid.
159+
case invalidPath = 2
160+
/// An error occurred on the encoder buffer.
161+
case bufferFailure = 4
162+
}
163+
164+
public static var errorDomain: String {
165+
"Decoder"
166+
}
167+
168+
public static func errorDescription(for failure: Error) -> String {
169+
switch failure {
170+
case .invalidConfiguration: return "Invalid configuration"
171+
case .invalidPath: return "Invalid path"
172+
case .bufferFailure: return "Invalid buffer state"
173+
}
174+
}
175+
}

sources/declarative/decodable/DecoderLazy.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ extension CSVDecoder.LazySequence {
3838

3939
/// Returns a value of the type you specify, decoded from CSV row.
4040
/// - parameter type: The type of the value to decode from the supplied file.
41-
/// - throws: `DecodingError`, or `CSVError<CSVReader>`, or the error raised by your custom types.
4241
@inline(__always) public func decode<T:Decodable>(_ type: T.Type) throws -> T {
4342
return try T(from: self._decoder)
4443
}

sources/declarative/decodable/containers/KeyedDecodingContainer.swift

Lines changed: 30 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,10 @@ extension ShadowDecoder {
3030
self._focus = .file
3131
case 1:
3232
let key = decoder.codingPath[0]
33-
let r = try key.intValue ?! DecodingError._invalidKey(forRow: key, codingPath: decoder.codingPath)
33+
let r = try key.intValue ?> CSVDecoder.Error._invalidRowKey(forKey: key, codingPath: decoder.codingPath)
3434
self._focus = .row(r)
3535
default:
36-
throw DecodingError._invalidContainerRequest(codingPath: decoder.codingPath)
36+
throw CSVDecoder.Error._invalidContainerRequest(forKey: decoder.codingPath.last!, codingPath: decoder.codingPath)
3737
}
3838
self._decoder = decoder
3939
}
@@ -78,32 +78,32 @@ extension ShadowDecoder.KeyedContainer {
7878
func nestedContainer<NestedKey>(keyedBy type: NestedKey.Type, forKey key: Key) throws -> KeyedDecodingContainer<NestedKey> where NestedKey : CodingKey {
7979
switch self._focus {
8080
case .file:
81-
guard let rowIndex = key.intValue else { throw DecodingError._invalidKey(forRow: key, codingPath: self.codingPath + [key]) }
81+
guard let rowIndex = key.intValue else { throw CSVDecoder.Error._invalidRowKey(forKey: key, codingPath: self.codingPath + [key]) }
8282
var codingPath = self._decoder.codingPath; codingPath.append(IndexKey(rowIndex))
8383
let decoder = ShadowDecoder(source: self._decoder.source, codingPath: codingPath)
8484
return KeyedDecodingContainer(ShadowDecoder.KeyedContainer<NestedKey>(unsafeDecoder: decoder, rowIndex: rowIndex))
85-
case .row: throw DecodingError._invalidContainerRequest(codingPath: self.codingPath)
85+
case .row: throw CSVDecoder.Error._invalidContainerRequest(forKey: key, codingPath: self.codingPath)
8686
}
8787
}
8888

8989
func nestedUnkeyedContainer(forKey key: Key) throws -> UnkeyedDecodingContainer {
9090
switch self._focus {
9191
case .file:
92-
guard let rowIndex = key.intValue else { throw DecodingError._invalidKey(forRow: key, codingPath: self.codingPath + [key]) }
92+
guard let rowIndex = key.intValue else { throw CSVDecoder.Error._invalidRowKey(forKey: key, codingPath: self.codingPath + [key]) }
9393
var codingPath = self._decoder.codingPath; codingPath.append(IndexKey(rowIndex))
9494
let decoder = ShadowDecoder(source: self._decoder.source, codingPath: codingPath)
9595
return ShadowDecoder.UnkeyedContainer(unsafeDecoder: decoder, rowIndex: rowIndex)
96-
case .row: throw DecodingError._invalidContainerRequest(codingPath: self.codingPath)
96+
case .row: throw CSVDecoder.Error._invalidContainerRequest(forKey: key, codingPath: self.codingPath)
9797
}
9898
}
9999

100100
func superDecoder(forKey key: Key) throws -> Decoder {
101101
switch self._focus {
102102
case .file:
103-
guard let rowIndex = key.intValue else { throw DecodingError._invalidKey(forRow: key, codingPath: self.codingPath + [key]) }
103+
guard let rowIndex = key.intValue else { throw CSVDecoder.Error._invalidRowKey(forKey: key, codingPath: self.codingPath + [key]) }
104104
var codingPath = self._decoder.codingPath; codingPath.append(IndexKey(rowIndex))
105105
return ShadowDecoder(source: self._decoder.source, codingPath: codingPath)
106-
case .row: throw DecodingError._invalidContainerRequest(codingPath: self.codingPath)
106+
case .row: throw CSVDecoder.Error._invalidContainerRequest(forKey: key, codingPath: self.codingPath)
107107
}
108108
}
109109

@@ -112,13 +112,11 @@ extension ShadowDecoder.KeyedContainer {
112112
case .file:
113113
var codingPath = self._decoder.codingPath; codingPath.append(IndexKey(0))
114114
return ShadowDecoder(source: self._decoder.source, codingPath: codingPath)
115-
case .row: throw DecodingError._invalidContainerRequest(codingPath: self.codingPath)
115+
case .row: throw CSVDecoder.Error._invalidContainerRequest(forKey: NameKey(index: 0, name: "super"), codingPath: self.codingPath)
116116
}
117117
}
118118
}
119119

120-
// MARK: -
121-
122120
extension ShadowDecoder.KeyedContainer {
123121
func decode(_ type: String.Type, forKey key: Key) throws -> String {
124122
try self._fieldContainer(forKey: key).decode(String.self)
@@ -281,9 +279,9 @@ private extension ShadowDecoder.KeyedContainer {
281279
case .row(let rowIndex):
282280
index = (rowIndex, try self._decoder.source.fieldIndex(forKey: key, codingPath: self.codingPath))
283281
case .file:
284-
guard let rowIndex = key.intValue else { throw DecodingError._invalidKey(forRow: key, codingPath: codingPath) }
282+
guard let rowIndex = key.intValue else { throw CSVDecoder.Error._invalidRowKey(forKey: key, codingPath: codingPath) }
285283
// Values are only allowed to be decoded directly from a nested container in "file level" if the CSV rows have a single column.
286-
guard self._decoder.source.numExpectedFields == 1 else { throw DecodingError._invalidNestedRequired(codingPath: self.codingPath) }
284+
guard self._decoder.source.numExpectedFields == 1 else { throw CSVDecoder.Error._invalidNestedRequired(codingPath: self.codingPath) }
287285
index = (rowIndex, 0)
288286
codingPath.append(IndexKey(index.field))
289287
}
@@ -293,26 +291,29 @@ private extension ShadowDecoder.KeyedContainer {
293291
}
294292
}
295293

296-
fileprivate extension DecodingError {
294+
fileprivate extension CSVDecoder.Error {
297295
/// Error raised when a coding key representing a row within the CSV file cannot be transformed into an integer value.
298-
/// - parameter codingPath: The whole coding path, including the invalid row key.
299-
static func _invalidKey(forRow key: CodingKey, codingPath: [CodingKey]) -> DecodingError {
300-
DecodingError.keyNotFound(key, .init(
301-
codingPath: codingPath,
302-
debugDescription: "The coding key identifying a CSV row couldn't be transformed into an integer value."))
296+
/// - parameter codingPath: The full decoding chain.
297+
static func _invalidRowKey(forKey key: CodingKey, codingPath: [CodingKey]) -> CSVError<CSVDecoder> {
298+
.init(.invalidPath,
299+
reason: "The coding key identifying a CSV row couldn't be transformed into an integer value.",
300+
help: "The provided coding key identifying a CSV row must implement `intValue`.",
301+
userInfo: ["Coding path": codingPath, "Key": key])
303302
}
304303
/// Error raised when a keyed container is requested on an invalid coding path.
305-
/// - parameter codingPath: The full chain of containers which generated this error.
306-
static func _invalidContainerRequest(codingPath: [CodingKey]) -> DecodingError {
307-
DecodingError.dataCorrupted(
308-
Context(codingPath: codingPath,
309-
debugDescription: "CSV doesn't support more than two nested decoding container.")
310-
)
304+
/// - parameter codingPath: The full decoding chain.
305+
static func _invalidContainerRequest(forKey key: CodingKey, codingPath: [CodingKey]) -> CSVError<CSVDecoder> {
306+
.init(.invalidPath,
307+
reason: "A CSV doesn't support more than two nested decoding container.",
308+
help: "Don't ask for a nested container on the targeted key for this coding path.",
309+
userInfo: ["Coding path": codingPath, "Key": key])
311310
}
312311
/// Error raised when a value is decoded, but a container was expected by the decoder.
313-
static func _invalidNestedRequired(codingPath: [CodingKey]) -> DecodingError {
314-
DecodingError.dataCorrupted(.init(
315-
codingPath: codingPath,
316-
debugDescription: "A nested container is needed to decode CSV row values"))
312+
/// - parameter codingPath: The full decoding chain.
313+
static func _invalidNestedRequired(codingPath: [CodingKey]) -> CSVError<CSVDecoder> {
314+
.init(.invalidPath,
315+
reason: "A nested container is needed to decode CSV row values",
316+
help: "Request a nested container instead of trying to decode a value directly.",
317+
userInfo: ["Coding path": codingPath])
317318
}
318319
}

0 commit comments

Comments
 (0)