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:
parent
b1aac4d7db
commit
77fb41f605
|
@ -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
|
||||||
|
|
|
@ -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 = []
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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]))
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue