updates to pass configuration through initializer

This commit is contained in:
Justin 2019-05-11 11:53:08 -06:00
parent 953871b9a6
commit 36992d6efa
4 changed files with 56 additions and 33 deletions

View File

@ -146,19 +146,25 @@ public final class CSVDecoder {
///
/// This is currently used to specify how `nil` and `Bool` values should be handled.
public var decodingOptions: CSVCodingOptions
/// The CSV configuration to use when decoding or encoding
///
/// This is used to specify if cells are wrapped in quotes and what the delimiter is (comma or tab, etc.)
public var configuration: Config
/// Creates a new `CSVDecoder` instance.
///
/// - Parameter decodingOptions: The decoding options to use when decoding data to an object.
public init(decodingOptions: CSVCodingOptions = .default) {
public init(decodingOptions: CSVCodingOptions = .default, configuration: Config = Config()) {
self.decodingOptions = decodingOptions
self.configuration = configuration
}
/// Creates a `CSVSyncDecoder` with the registered encoding options.
///
/// This decoder is for if you have whole CSV document you want to decode at once.
public var sync: CSVSyncDecoder {
return CSVSyncDecoder(decodingOptions: self.decodingOptions)
return CSVSyncDecoder(decodingOptions: self.decodingOptions, configuration: self.configuration)
}
/// Creates a `CSVAsyncDecoder` instance with the registered encoding options.
@ -180,7 +186,8 @@ public final class CSVDecoder {
decoding: D.self,
onInstance: onInstance,
length: length,
decodingOptions: self.decodingOptions
decodingOptions: self.decodingOptions,
configuration: self.configuration
)
}
}
@ -190,9 +197,11 @@ public final class CSVDecoder {
/// You can get an instance of `CSVSyncDecoder` from the `CSVDecoder.sync` property.
public final class CSVSyncDecoder {
internal var decodingOptions: CSVCodingOptions
internal var configuration: Config
internal init(decodingOptions: CSVCodingOptions) {
internal init(decodingOptions: CSVCodingOptions, configuration: Config = Config()) {
self.decodingOptions = decodingOptions
self.configuration = configuration
}
/// Decodes a whole CSV document into an array of a specified `Decodable` type.
@ -203,7 +212,7 @@ public final class CSVSyncDecoder {
/// - Returns: An array of `D` instances, decoded from the data passed in.
///
/// - Throws: Errors that occur during the decoding proccess.
public func decode<D>(_ type: D.Type = D.self, from data: Data, configuration: Config = Config())throws -> [D] where D: Decodable {
public func decode<D>(_ type: D.Type = D.self, from data: Data)throws -> [D] where D: Decodable {
var result: [D] = []
result.reserveCapacity(data.lazy.split(separator: "\n").count)
@ -215,7 +224,7 @@ public final class CSVSyncDecoder {
result.append(typed)
}
try decoder.decode(Array(data), length: data.count, configuration: configuration)
try decoder.decode(Array(data), length: data.count)
return result
}
@ -228,9 +237,10 @@ public final class CSVAsyncDecoder {
internal var length: Int
internal var decoding: Decodable.Type
internal var decodingOptions: CSVCodingOptions
internal var configuration: Config
private var rowDecoder: AsyncDecoder
internal init<D>(decoding: D.Type, onInstance: @escaping (D) -> (), length: Int, decodingOptions: CSVCodingOptions)
internal init<D>(decoding: D.Type, onInstance: @escaping (D) -> (), length: Int, decodingOptions: CSVCodingOptions, configuration: Config = Config())
where D: Decodable
{
let callback = { (decoded: Decodable) in
@ -244,10 +254,12 @@ public final class CSVAsyncDecoder {
self.length = length
self.decoding = decoding
self.decodingOptions = decodingOptions
self.configuration = configuration
self.rowDecoder = AsyncDecoder(
decoding: D.self,
path: [],
decodingOptions: decodingOptions,
configuration: configuration,
onInstance: callback
)
@ -264,7 +276,7 @@ public final class CSVAsyncDecoder {
///
/// - Parameter data: A section of the CSV document to decode.
/// - Throws: Errors that occur during the decoding process.
public func decode<C>(_ data: C, configuration: Config = Config()) throws where C: Collection, C.Element == UInt8 {
try self.rowDecoder.decode(Array(data), length: self.length, configuration: configuration)
public func decode<C>(_ data: C) throws where C: Collection, C.Element == UInt8 {
try self.rowDecoder.decode(Array(data), length: self.length)
}
}

View File

@ -20,12 +20,13 @@ internal final class AsyncDecoder: Decoder {
info: [CodingUserInfoKey : Any] = [:],
data: Storage = .none,
decodingOptions: CSVCodingOptions,
configuration: Config = Config(),
onInstance: @escaping (Decodable)throws -> ()
) {
self.codingPath = path
self.userInfo = info
self.decoding = decoding
self.handler = AsyncDecoderHandler { _ in return }
self.handler = AsyncDecoderHandler(configuration: configuration){ _ in return }
self.decodingOptions = decodingOptions
self.onInstance = onInstance
self.data = data
@ -69,8 +70,8 @@ internal final class AsyncDecoder: Decoder {
return try AsyncSingleValueDecoder(path: self.codingPath, decoder: self)
}
func decode(_ data: [UInt8], length: Int, configuration: Config = Config())throws {
try self.handler.parse(data, length: length, configuration: configuration).get()
func decode(_ data: [UInt8], length: Int)throws {
try self.handler.parse(data, length: length).get()
}
}
@ -82,8 +83,8 @@ internal final class AsyncDecoderHandler {
private var columnCount: Int
private var currentColumn: Int
init(onRow: @escaping ([String: [UInt8]])throws -> ()) {
self.parser = Parser()
init(configuration: Config = Config(), onRow: @escaping ([String: [UInt8]])throws -> ()) {
self.parser = Parser(configuration: configuration)
self.currentRow = [:]
self.onRow = onRow
self.columnCount = 0
@ -101,7 +102,7 @@ internal final class AsyncDecoderHandler {
}
}
func parse(_ bytes: [UInt8], length: Int, configuration: Config = Config()) -> Result<Void, ErrorList> {
return self.parser.parse(bytes, length: length, configuration: configuration)
func parse(_ bytes: [UInt8], length: Int) -> Result<Void, ErrorList> {
return self.parser.parse(bytes, length: length)
}
}

View File

@ -85,6 +85,8 @@ public struct Parser {
/// The callback that is called when a cell is parsed.
public var onCell: CellHandler?
public var configuration: Config
private var state: State
@ -97,9 +99,10 @@ public struct Parser {
/// - Parameters:
/// - onHeader: The callback that will be called when a header is parsed.
/// - onCell: The callback that will be called when a cell is parsed.
public init(onHeader: HeaderHandler? = nil, onCell: CellHandler? = nil) {
public init(onHeader: HeaderHandler? = nil, onCell: CellHandler? = nil, configuration: Config = Config()) {
self.onHeader = onHeader
self.onCell = onCell
self.configuration = configuration
self.state = State()
}
@ -119,7 +122,7 @@ public struct Parser {
/// - Returns: A `Result` instance that will have a `.failure` case with all the errors thrown from
/// the registered callbacks. If there are no errors, then the result will be a `.success` case.
@discardableResult
public mutating func parse(_ data: [UInt8], length: Int? = nil, configuration: Config = Config()) -> Result<Void, ErrorList> {
public mutating func parse(_ data: [UInt8], length: Int? = nil) -> Result<Void, ErrorList> {
var currentCell: [UInt8] = self.state.store
var index = data.startIndex
var updateState = false
@ -208,15 +211,17 @@ public struct Parser {
/// A synchronous wrapper for the `Parser` type for parsing whole CSV documents at once.
public final class SyncParser {
public var configuration: Config
/// Creates a new `SyncParser` instance
public init() {}
public init(configuration: Config = Config() ) { self.configuration = configuration }
/// Parses a whole CSV document at once.
///
/// - Parameter data: The CSV data to parse.
/// - Returns: A dictionary containing the parsed CSV data. The keys are the column names
/// and the values are the column cells. A `nil` value is an empty cell.
public func parse(_ data: [UInt8], configuration: Config = Config()) -> [[UInt8]: [[UInt8]?]] {
public func parse(_ data: [UInt8]) -> [[UInt8]: [[UInt8]?]] {
var results: [[UInt8]: [[UInt8]?]] = [:]
var parser = Parser(
onHeader: { header in
@ -224,10 +229,11 @@ public final class SyncParser {
},
onCell: { header, cell in
results[header, default: []].append(cell.count > 0 ? cell : nil)
}
},
configuration: configuration
)
parser.parse(data, configuration: configuration)
parser.parse(data)
return results
}
@ -236,7 +242,7 @@ public final class SyncParser {
/// - Parameter data: The CSV data to parse.
/// - Returns: A dictionary containing the parsed CSV data. The keys are the column names
/// and the values are the column cells. A `nil` value is an empty cell.
public func parse(_ data: String, configuration: Config = Config()) -> [String: [String?]] {
public func parse(_ data: String) -> [String: [String?]] {
var results: [String: [String?]] = [:]
var parser = Parser(
onHeader: { header in
@ -246,10 +252,11 @@ public final class SyncParser {
let title = String(decoding: header, as: UTF8.self)
let contents = String(decoding: cell, as: UTF8.self)
results[title, default: []].append(cell.count > 0 ? contents : nil)
}
},
configuration: configuration
)
parser.parse(Array(data.utf8), configuration: configuration)
parser.parse(Array(data.utf8))
return results
}
}

View File

@ -63,6 +63,7 @@ extension Dictionary: KeyedCollection { }
/// - Note: You should create a new `Serializer` dictionary you serialize.
public struct Serializer {
private var serializedHeaders: Bool
var configuration: Config
/// The callback that will be called with each row that is serialized.
public var onRow: ([UInt8])throws -> ()
@ -70,8 +71,9 @@ public struct Serializer {
/// Creates a new `Serializer` instance.
///
/// - Parameter onRow: The callback that will be called with each row that is serialized.
public init(onRow: @escaping ([UInt8])throws -> ()) {
public init(configuration: Config = Config(), onRow: @escaping ([UInt8])throws -> ()) {
self.serializedHeaders = false
self.configuration = configuration
self.onRow = onRow
}
@ -88,7 +90,7 @@ public struct Serializer {
/// - Returns: A `Result` instance with a `.failure` case with all the errors from the the `.onRow` callback calls.
/// If there are no errors, the result will be a `.success` case.
@discardableResult
public mutating func serialize<Data>(_ data: Data, configuration: Config = Config()) -> Result<Void, ErrorList> where
public mutating func serialize<Data>(_ data: Data) -> Result<Void, ErrorList> where
Data: KeyedCollection, Data.Key: BytesRepresentable, Data.Value: Collection, Data.Value.Element: BytesRepresentable,
Data.Value.Index: Strideable, Data.Value.Index.Stride: SignedInteger
{
@ -99,7 +101,7 @@ public struct Serializer {
if !self.serializedHeaders {
let headers = data.keys.map { title -> [UInt8] in
if configuration.inQuotes {
if self.configuration.inQuotes {
return Array([[34], title.bytes, [34]].joined())
}else {
return Array(title.bytes)
@ -115,7 +117,7 @@ public struct Serializer {
guard let first = data.first?.value else { return errors.result }
(first.startIndex..<first.endIndex).forEach { index in
let cells = data.values.map { column -> [UInt8] in
if configuration.inQuotes {
if self.configuration.inQuotes {
return Array([[34], column[index].bytes, [34]].joined())
}else {
return Array(column[index].bytes)
@ -131,9 +133,10 @@ public struct Serializer {
/// A synchronous wrapper for the `Serializer` struct for parsing a whole CSV document.
public struct SyncSerializer {
var configuration: Config
/// Creates a new `SyncSerializer` instance.
public init () { }
public init (configuration: Config = Config()) { self.configuration = configuration}
/// Serializes a dictionary to CSV document data. Usually this will be a dictionary of type
/// `[BytesRepresentable: [BytesRepresentable]], but it can be any type you conform to the proper protocols.
@ -143,15 +146,15 @@ public struct SyncSerializer {
///
/// - Parameter data: The dictionary (or other object) to parse.
/// - Returns: The serialized CSV data.
public func serialize<Data>(_ data: Data, configuration: Config = Config()) -> [UInt8] where
public func serialize<Data>(_ data: Data) -> [UInt8] where
Data: KeyedCollection, Data.Key: BytesRepresentable, Data.Value: Collection, Data.Value.Element: BytesRepresentable,
Data.Value.Index: Strideable, Data.Value.Index.Stride: SignedInteger
{
var rows: [[UInt8]] = []
rows.reserveCapacity(data.first?.value.count ?? 0)
var serializer = Serializer { row in rows.append(row) }
serializer.serialize(data, configuration: configuration)
var serializer = Serializer(configuration: self.configuration) { row in rows.append(row) }
serializer.serialize(data)
return Array(rows.joined(separator: [10]))
}