updates to pass configuration through initializer
This commit is contained in:
parent
953871b9a6
commit
36992d6efa
|
@ -147,18 +147,24 @@ 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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -86,6 +86,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
|
||||
|
||||
internal var currentHeader: [UInt8] {
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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]))
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue