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. /// Currently, this decideds how `nil` and `bool` values should be handled.
public var encodingOptions: CSVCodingOptions public var encodingOptions: CSVCodingOptions
public var configuration: Config
/// Creates a new `CSVEncoder` instance. /// Creates a new `CSVEncoder` instance.
/// ///
/// - Parameter encodingOptions: The encoding options the use when encoding an object. /// - 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.encodingOptions = encodingOptions
self.configuration = configuration
} }
/// Creates a `CSVSyncEncoder` using the registered encoding options. /// 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 /// This encoder is for if you have several objects that you want to encode at
/// a single time into a single document. /// a single time into a single document.
public var sync: CSVSyncEncoder { 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. /// 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 /// - Returns: A `CSVAsyncEncoder` instance with the current encoder's encoding
/// options and the `onRow` closure as its callback. /// options and the `onRow` closure as its callback.
public func async(_ onRow: @escaping ([UInt8]) -> ()) -> CSVAsyncEncoder { 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. /// You can get an instance of the `CSVSyncEncoder` with the `CSVEncoder.sync` property.
public final class CSVSyncEncoder { public final class CSVSyncEncoder {
internal var encodingOptions: CSVCodingOptions internal var encodingOptions: CSVCodingOptions
internal var configuration: Config
internal init(encodingOptions: CSVCodingOptions) { internal init(encodingOptions: CSVCodingOptions, configuration: Config = Config()) {
self.encodingOptions = encodingOptions self.encodingOptions = encodingOptions
self.configuration = configuration
} }
/// Encodes an array of encodable objects into a single CSV document. /// Encodes an array of encodable objects into a single CSV document.
@ -83,7 +87,7 @@ public final class CSVSyncEncoder {
var rows: [[UInt8]] = [] var rows: [[UInt8]] = []
rows.reserveCapacity(objects.count) 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) rows.append(row)
} }
try objects.forEach(encoder.encode) try objects.forEach(encoder.encode)
@ -99,9 +103,9 @@ public final class CSVAsyncEncoder {
internal var encodingOptions: CSVCodingOptions internal var encodingOptions: CSVCodingOptions
private var encoder: AsyncEncoder 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.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 /// 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 userInfo: [CodingUserInfoKey : Any]
let container: DataContainer let container: DataContainer
let encodingOptions: CSVCodingOptions let encodingOptions: CSVCodingOptions
let configuration: Config
let onRow: ([UInt8]) -> () let onRow: ([UInt8]) -> ()
init( init(
path: [CodingKey] = [], path: [CodingKey] = [],
info: [CodingUserInfoKey : Any] = [:], info: [CodingUserInfoKey : Any] = [:],
encodingOptions: CSVCodingOptions, encodingOptions: CSVCodingOptions,
configuration: Config = Config(),
onRow: @escaping ([UInt8]) -> () onRow: @escaping ([UInt8]) -> ()
) { ) {
self.codingPath = path self.codingPath = path
self.userInfo = info self.userInfo = info
self.container = DataContainer(section: .header) self.container = DataContainer(section: .header)
self.encodingOptions = encodingOptions self.encodingOptions = encodingOptions
self.configuration = configuration
self.onRow = onRow self.onRow = onRow
} }
@ -39,14 +42,14 @@ final class AsyncEncoder: Encoder {
switch self.container.section { switch self.container.section {
case .header: case .header:
try object.encode(to: self) 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.section = .row
self.container.rowCount += 1 self.container.rowCount += 1
self.container.cells = [] self.container.cells = []
fallthrough fallthrough
case .row: case .row:
try object.encode(to: self) 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.rowCount += 1
self.container.cells = [] 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(_ value: String, forKey key: K) throws { self._encode(value.bytes, for: key) }
func encode<T>(_ value: T, forKey key: K) throws where T : Encodable { 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) try value.encode(to: encoder)
self._encode(encoder.container.cells[0], for: key) 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. /// - 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. /// If there are no errors, the result will be a `.success` case.
@discardableResult @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: KeyedCollection, Data.Key: BytesRepresentable, Data.Value: Collection, Data.Value.Element: BytesRepresentable,
Data.Value.Index: Strideable, Data.Value.Index.Stride: SignedInteger Data.Value.Index: Strideable, Data.Value.Index.Stride: SignedInteger
{ {
var errors = ErrorList() var errors = ErrorList()
guard data.count > 0 else { return errors.result } guard data.count > 0 else { return errors.result }
guard let delimiterASCII = configuration.delimiter.asciiValue else { return errors.result }
if !self.serializedHeaders { if !self.serializedHeaders {
let headers = data.keys.map { title in Array([[34], title.bytes, [34]].joined()) } 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) } catch let error { errors.errors.append(error) }
self.serializedHeaders = true self.serializedHeaders = true
} }
@ -107,7 +111,7 @@ public struct Serializer {
let cells = data.values.map { column -> [UInt8] in let cells = data.values.map { column -> [UInt8] in
return Array([[34], column[index].bytes, [34]].joined()) 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) } catch let error { errors.errors.append(error) }
} }
@ -129,7 +133,7 @@ public struct SyncSerializer {
/// ///
/// - Parameter data: The dictionary (or other object) to parse. /// - Parameter data: The dictionary (or other object) to parse.
/// - Returns: The serialized CSV data. /// - 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: KeyedCollection, Data.Key: BytesRepresentable, Data.Value: Collection, Data.Value.Element: BytesRepresentable,
Data.Value.Index: Strideable, Data.Value.Index.Stride: SignedInteger Data.Value.Index: Strideable, Data.Value.Index.Stride: SignedInteger
{ {
@ -137,7 +141,7 @@ public struct SyncSerializer {
rows.reserveCapacity(data.first?.value.count ?? 0) rows.reserveCapacity(data.first?.value.count ?? 0)
var serializer = Serializer { row in rows.append(row) } var serializer = Serializer { row in rows.append(row) }
serializer.serialize(data) serializer.serialize(data, configuration: configuration)
return Array(rows.joined(separator: [10])) return Array(rows.joined(separator: [10]))
} }