Error handling #10

This commit is contained in:
Yasuhiro Hatta 2016-10-24 01:22:23 +09:00
parent 28c54df853
commit 597272db9b
7 changed files with 67 additions and 40 deletions

View File

@ -16,6 +16,8 @@ internal let defaultWhitespaces = CharacterSet.whitespaces
/// No overview available.
public struct CSVConfiguration {
public var errorHandler: ((Error, Int, Int) -> Void)? = nil
/// `true` if the CSV has a header row, otherwise `false`. Default: `false`.
public let hasHeaderRow: Bool
/// No overview available.

View File

@ -164,21 +164,23 @@ internal class BinaryReader {
extension BinaryReader {
internal struct UInt8Iterator: Sequence, IteratorProtocol {
internal class UInt8Iterator: Sequence, IteratorProtocol {
private let reader: BinaryReader
internal var errorHandler: ((Error) -> Void)? = nil
fileprivate init(reader: BinaryReader) {
self.reader = reader
}
internal mutating func next() -> UInt8? {
internal func next() -> UInt8? {
if !reader.hasBytesAvailable {
return nil
}
do {
return try reader.readUInt8()
} catch {
errorHandler?(error)
return nil
}
}
@ -193,21 +195,23 @@ extension BinaryReader {
extension BinaryReader {
internal struct UInt16Iterator: Sequence, IteratorProtocol {
internal class UInt16Iterator: Sequence, IteratorProtocol {
private let reader: BinaryReader
internal var errorHandler: ((Error) -> Void)? = nil
fileprivate init(reader: BinaryReader) {
self.reader = reader
}
internal mutating func next() -> UInt16? {
internal func next() -> UInt16? {
if !reader.hasBytesAvailable {
return nil
}
do {
return try reader.readUInt16()
} catch {
errorHandler?(error)
return nil
}
}
@ -222,21 +226,23 @@ extension BinaryReader {
extension BinaryReader {
internal struct UInt32Iterator: Sequence, IteratorProtocol {
internal class UInt32Iterator: Sequence, IteratorProtocol {
private let reader: BinaryReader
internal var errorHandler: ((Error) -> Void)? = nil
fileprivate init(reader: BinaryReader) {
self.reader = reader
}
internal mutating func next() -> UInt32? {
internal func next() -> UInt32? {
if !reader.hasBytesAvailable {
return nil
}
do {
return try reader.readUInt32()
} catch {
errorHandler?(error)
return nil
}
}

View File

@ -15,7 +15,7 @@ extension CSV {
/// - parameter stream: An `InputStream` object. If the stream is not open,
/// initializer opens automatically.
/// - parameter config: CSV configuration.
public init(
public convenience init(
stream: InputStream,
config: CSVConfiguration = CSVConfiguration()) throws {
@ -30,7 +30,7 @@ extension CSV {
///
/// - parameter string: An CSV string.
/// - parameter config: CSV configuration.
public init(
public convenience init(
string: String,
config: CSVConfiguration = CSVConfiguration()) throws {

View File

@ -9,7 +9,7 @@
extension CSV: IteratorProtocol, Sequence {
/// No overview available.
public mutating func next() -> Row? {
public func next() -> Row? {
guard let row = readRow() else {
return nil
}

View File

@ -13,13 +13,16 @@ private let CR = UnicodeScalar(UInt8(0x0d)) // "\r"
private let DQUOTE = UnicodeScalar(UInt8(0x22)) // "\""
/// No overview available.
public struct CSV {
public class CSV {
private var iterator: AnyIterator<UnicodeScalar>
private let config: CSVConfiguration
private var back: UnicodeScalar? = nil
private var currentRowIndex: Int = 0
private var currentFieldIndex: Int = 0
/// CSV header row. To set a value for this property,
/// you set `true` to `hasHeaerRow` in initializer.
public private(set) var headerRow: [String]? = nil
@ -46,18 +49,18 @@ public struct CSV {
/// initializer opens automatically.
/// - parameter codecType: A `UnicodeCodec` type for `stream`.
/// - parameter config: CSV configuration.
public init<T: UnicodeCodec>(
public convenience init<T: UnicodeCodec>(
stream: InputStream,
codecType: T.Type,
config: CSVConfiguration = CSVConfiguration()
) throws where T.CodeUnit == UInt8 {
let reader = try BinaryReader(stream: stream, endian: .unknown, closeOnDeinit: true)
let iterator = UnicodeIterator(
input: reader.makeUInt8Iterator(),
inputEncodingType: codecType
)
let input = reader.makeUInt8Iterator()
let iterator = UnicodeIterator(input: input, inputEncodingType: codecType)
try self.init(iterator: iterator, config: config)
input.errorHandler = self.errorHandler
iterator.errorHandler = self.errorHandler
}
/// Create an instance with `InputStream`.
@ -67,7 +70,7 @@ public struct CSV {
/// - parameter codecType: A `UnicodeCodec` type for `stream`.
/// - parameter endian: Endian to use when reading a stream. Default: `.big`.
/// - parameter config: CSV configuration.
public init<T: UnicodeCodec>(
public convenience init<T: UnicodeCodec>(
stream: InputStream,
codecType: T.Type,
endian: Endian = .big,
@ -75,11 +78,11 @@ public struct CSV {
) throws where T.CodeUnit == UInt16 {
let reader = try BinaryReader(stream: stream, endian: endian, closeOnDeinit: true)
let iterator = UnicodeIterator(
input: reader.makeUInt16Iterator(),
inputEncodingType: codecType
)
let input = reader.makeUInt16Iterator()
let iterator = UnicodeIterator(input: input, inputEncodingType: codecType)
try self.init(iterator: iterator, config: config)
input.errorHandler = self.errorHandler
iterator.errorHandler = self.errorHandler
}
/// Create an instance with `InputStream`.
@ -89,7 +92,7 @@ public struct CSV {
/// - parameter codecType: A `UnicodeCodec` type for `stream`.
/// - parameter endian: Endian to use when reading a stream. Default: `.big`.
/// - parameter config: CSV configuration.
public init<T: UnicodeCodec>(
public convenience init<T: UnicodeCodec>(
stream: InputStream,
codecType: T.Type,
endian: Endian = .big,
@ -97,16 +100,18 @@ public struct CSV {
) throws where T.CodeUnit == UInt32 {
let reader = try BinaryReader(stream: stream, endian: endian, closeOnDeinit: true)
let iterator = UnicodeIterator(
input: reader.makeUInt32Iterator(),
inputEncodingType: codecType
)
let input = reader.makeUInt32Iterator()
let iterator = UnicodeIterator(input: input, inputEncodingType: codecType)
try self.init(iterator: iterator, config: config)
input.errorHandler = self.errorHandler
iterator.errorHandler = self.errorHandler
}
// MARK: - Parse CSV
internal mutating func readRow() -> [String]? {
internal func readRow() -> [String]? {
currentFieldIndex = 0
var c = moveNext()
if c == nil {
return nil
@ -140,13 +145,18 @@ public struct CSV {
if end {
break
}
currentFieldIndex += 1
c = moveNext()
}
currentRowIndex += 1
return row
}
private mutating func readField(quoted: Bool) -> (String, Bool) {
private func readField(quoted: Bool) -> (String, Bool) {
var field = ""
while let c = moveNext() {
@ -206,7 +216,7 @@ public struct CSV {
return (field, true)
}
private mutating func moveNext() -> UnicodeScalar? {
private func moveNext() -> UnicodeScalar? {
if back != nil {
defer {
back = nil
@ -216,13 +226,15 @@ public struct CSV {
return iterator.next()
}
}
private func errorHandler(error: Error) {
config.errorHandler?(error, currentRowIndex, currentFieldIndex)
}
extension CSV {
// MARK: - deprecated
/// Unavailable.
@available(*, unavailable, message: "Use init(stream:codecType:config:) instead")
public init<T: UnicodeCodec>(
public convenience init<T: UnicodeCodec>(
stream: InputStream,
codecType: T.Type,
hasHeaderRow: Bool = defaultHasHeaderRow,
@ -245,7 +257,7 @@ extension CSV {
/// Unavailable.
@available(*, unavailable, message: "Use init(stream:codecType:endian:config:) instead")
public init<T: UnicodeCodec>(
public convenience init<T: UnicodeCodec>(
stream: InputStream,
codecType: T.Type,
endian: Endian = .big,
@ -269,7 +281,7 @@ extension CSV {
/// Unavailable.
@available(*, unavailable, message: "Use init(stream:codecType:endian:config:) instead")
public init<T: UnicodeCodec>(
public convenience init<T: UnicodeCodec>(
stream: InputStream,
codecType: T.Type,
endian: Endian = .big,
@ -293,7 +305,7 @@ extension CSV {
/// Unavailable.
@available(*, unavailable, message: "Use init(stream:config:) instead")
public init(
public convenience init(
stream: InputStream,
hasHeaderRow: Bool = defaultHasHeaderRow,
trimFields: Bool = defaultTrimFields,
@ -309,7 +321,7 @@ extension CSV {
/// Unavailable.
@available(*, unavailable, message: "Use init(string:config:) instead")
public init(
public convenience init(
string: String,
hasHeaderRow: Bool = defaultHasHeaderRow,
trimFields: Bool = defaultTrimFields,

View File

@ -16,6 +16,8 @@ public enum CSVError: Error {
/// No overview available.
case streamErrorHasOccurred(error: Error)
/// No overview available.
case unicodeDecoding
/// No overview available.
case cannotReadHeaderRow
/// No overview available.
case stringEncodingMismatch

View File

@ -6,7 +6,7 @@
// Copyright © 2016 yaslab. All rights reserved.
//
internal struct UnicodeIterator<
internal class UnicodeIterator<
Input: IteratorProtocol,
InputEncoding: UnicodeCodec>
: IteratorProtocol
@ -14,17 +14,22 @@ internal struct UnicodeIterator<
private var input: Input
private var inputEncoding: InputEncoding
internal var errorHandler: ((Error) -> Void)? = nil
internal init(input: Input, inputEncodingType: InputEncoding.Type) {
self.input = input
self.inputEncoding = inputEncodingType.init()
}
internal mutating func next() -> UnicodeScalar? {
internal func next() -> UnicodeScalar? {
switch inputEncoding.decode(&input) {
case .scalarValue(let c): return c
case .emptyInput: return nil
case .error: return nil
case .scalarValue(let c):
return c
case .emptyInput:
return nil
case .error:
errorHandler?(CSVError.unicodeDecoding)
return nil
}
}