updates to support encoding configuration

- allows user to configure encoding delimiter with configuration parameter
- does not yet support quote wrapping configuration option
This commit is contained in:
Justin 2019-05-05 10:35:43 -06:00
parent b1aac4d7db
commit 77fb41f605
4 changed files with 26 additions and 15 deletions

View File

@ -33,12 +33,14 @@ public final class CSVEncoder {
///
/// Currently, this decideds how `nil` and `bool` values should be handled.
public var encodingOptions: CSVCodingOptions
public var configuration: Config
/// Creates a new `CSVEncoder` instance.
///
/// - Parameter encodingOptions: The encoding options the use when encoding an object.
public init(encodingOptions: CSVCodingOptions = .default) {
public init(encodingOptions: CSVCodingOptions = .default, configuration: Config = Config()) {
self.encodingOptions = encodingOptions
self.configuration = configuration
}
/// Creates a `CSVSyncEncoder` using the registered encoding options.
@ -46,7 +48,7 @@ public final class CSVEncoder {
/// This encoder is for if you have several objects that you want to encode at
/// a single time into a single document.
public var sync: CSVSyncEncoder {
return CSVSyncEncoder(encodingOptions: self.encodingOptions)
return CSVSyncEncoder(encodingOptions: self.encodingOptions, configuration: self.configuration)
}
/// Creates a new `CSVAsyncEncoder` using the registered encoding options.
@ -59,7 +61,7 @@ public final class CSVEncoder {
/// - Returns: A `CSVAsyncEncoder` instance with the current encoder's encoding
/// options and the `onRow` closure as its callback.
public func async(_ onRow: @escaping ([UInt8]) -> ()) -> CSVAsyncEncoder {
return CSVAsyncEncoder(encodingOptions: self.encodingOptions, onRow: onRow)
return CSVAsyncEncoder(encodingOptions: self.encodingOptions, configuration: self.configuration, onRow: onRow)
}
}
@ -68,9 +70,11 @@ public final class CSVEncoder {
/// You can get an instance of the `CSVSyncEncoder` with the `CSVEncoder.sync` property.
public final class CSVSyncEncoder {
internal var encodingOptions: CSVCodingOptions
internal var configuration: Config
internal init(encodingOptions: CSVCodingOptions) {
internal init(encodingOptions: CSVCodingOptions, configuration: Config = Config()) {
self.encodingOptions = encodingOptions
self.configuration = configuration
}
/// Encodes an array of encodable objects into a single CSV document.
@ -83,7 +87,7 @@ public final class CSVSyncEncoder {
var rows: [[UInt8]] = []
rows.reserveCapacity(objects.count)
let encoder = AsyncEncoder(encodingOptions: self.encodingOptions) { row in
let encoder = AsyncEncoder(encodingOptions: self.encodingOptions, configuration: self.configuration) { row in
rows.append(row)
}
try objects.forEach(encoder.encode)
@ -99,9 +103,9 @@ public final class CSVAsyncEncoder {
internal var encodingOptions: CSVCodingOptions
private var encoder: AsyncEncoder
internal init(encodingOptions: CSVCodingOptions, onRow: @escaping ([UInt8]) -> ()) {
internal init(encodingOptions: CSVCodingOptions, configuration: Config = Config(), onRow: @escaping ([UInt8]) -> ()) {
self.encodingOptions = encodingOptions
self.encoder = AsyncEncoder(encodingOptions: encodingOptions, onRow: onRow)
self.encoder = AsyncEncoder(encodingOptions: encodingOptions, configuration: configuration, onRow: onRow)
}
/// Encodes an `Encodable` object into a row for a CSV document and passes it into

View File

@ -5,18 +5,21 @@ final class AsyncEncoder: Encoder {
let userInfo: [CodingUserInfoKey : Any]
let container: DataContainer
let encodingOptions: CSVCodingOptions
let configuration: Config
let onRow: ([UInt8]) -> ()
init(
path: [CodingKey] = [],
info: [CodingUserInfoKey : Any] = [:],
encodingOptions: CSVCodingOptions,
configuration: Config = Config(),
onRow: @escaping ([UInt8]) -> ()
) {
self.codingPath = path
self.userInfo = info
self.container = DataContainer(section: .header)
self.encodingOptions = encodingOptions
self.configuration = configuration
self.onRow = onRow
}
@ -39,14 +42,14 @@ final class AsyncEncoder: Encoder {
switch self.container.section {
case .header:
try object.encode(to: self)
self.onRow(Array(self.container.cells.joined(separator: [44])))
self.onRow(Array(self.container.cells.joined(separator: [self.configuration.delimiter.asciiValue ?? 44])))
self.container.section = .row
self.container.rowCount += 1
self.container.cells = []
fallthrough
case .row:
try object.encode(to: self)
self.onRow(Array(self.container.cells.joined(separator: [44])))
self.onRow(Array(self.container.cells.joined(separator: [self.configuration.delimiter.asciiValue ?? 44])))
self.container.rowCount += 1
self.container.cells = []
}

View File

@ -41,7 +41,7 @@ final class AsyncKeyedEncoder<K>: KeyedEncodingContainerProtocol where K: Coding
func encode(_ value: String, forKey key: K) throws { self._encode(value.bytes, for: key) }
func encode<T>(_ value: T, forKey key: K) throws where T : Encodable {
let encoder = AsyncEncoder(encodingOptions: self.encoder.encodingOptions, onRow: self.encoder.onRow)
let encoder = AsyncEncoder(encodingOptions: self.encoder.encodingOptions, configuration: self.encoder.configuration, onRow: self.encoder.onRow)
try value.encode(to: encoder)
self._encode(encoder.container.cells[0], for: key)
}

View File

@ -88,16 +88,20 @@ 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) -> Result<Void, ErrorList> where
public mutating func serialize<Data>(_ data: Data, configuration: Config = Config()) -> 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
{
var errors = ErrorList()
guard data.count > 0 else { return errors.result }
guard let delimiterASCII = configuration.delimiter.asciiValue else { return errors.result }
if !self.serializedHeaders {
let headers = data.keys.map { title in Array([[34], title.bytes, [34]].joined()) }
do { try self.onRow(Array(headers.joined(separator: [10]))) }
print(headers)
print(Array(headers.joined(separator: [delimiterASCII])))
do { try self.onRow(Array(headers.joined(separator: [delimiterASCII]))) }
catch let error { errors.errors.append(error) }
self.serializedHeaders = true
}
@ -107,7 +111,7 @@ public struct Serializer {
let cells = data.values.map { column -> [UInt8] in
return Array([[34], column[index].bytes, [34]].joined())
}
do { try onRow(Array(cells.joined(separator: [10]))) }
do { try onRow(Array(cells.joined(separator: [delimiterASCII]))) }
catch let error { errors.errors.append(error) }
}
@ -129,7 +133,7 @@ public struct SyncSerializer {
///
/// - Parameter data: The dictionary (or other object) to parse.
/// - Returns: The serialized CSV data.
public func serialize<Data>(_ data: Data) -> [UInt8] where
public func serialize<Data>(_ data: Data, configuration: Config = Config()) -> [UInt8] where
Data: KeyedCollection, Data.Key: BytesRepresentable, Data.Value: Collection, Data.Value.Element: BytesRepresentable,
Data.Value.Index: Strideable, Data.Value.Index.Stride: SignedInteger
{
@ -137,7 +141,7 @@ public struct SyncSerializer {
rows.reserveCapacity(data.first?.value.count ?? 0)
var serializer = Serializer { row in rows.append(row) }
serializer.serialize(data)
serializer.serialize(data, configuration: configuration)
return Array(rows.joined(separator: [10]))
}