Skip to content

Commit eece70e

Browse files
committed
Add configuration for setting escaping strategy
1 parent 3678d3f commit eece70e

3 files changed

Lines changed: 45 additions & 10 deletions

File tree

Sources/Active/Reader/ReaderConfiguration.swift

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ extension CSVReader {
99
public var headerStrategy: Strategy.Header
1010
/// Trims the given characters at the beginning and end of each row, and between fields.
1111
public var trimStrategry: CharacterSet
12+
/// The strategy for escaping quoted fields.
13+
public var escapingStrategy: Strategy.Escaping
1214
/// The encoding used to identify the underlying data or `nil` if you want the CSV reader to try to figure it out.
1315
///
1416
/// If no encoding is provided and the input data doesn't contain a Byte Order Marker (BOM), UTF8 is presumed.
@@ -24,6 +26,7 @@ extension CSVReader {
2426
self.delimiters = (field: ",", row: "\n")
2527
self.headerStrategy = .none
2628
self.trimStrategry = .init()
29+
self.escapingStrategy = .doubleQuote
2730
self.encoding = nil
2831
self.presample = false
2932
}
@@ -38,7 +41,7 @@ extension CSVReader {
3841
/// The characters set to be trimmed at the beginning and ending of each field.
3942
let trimCharacters: CharacterSet
4043
/// The unicode scalar used as encapsulator and escaping character (when printed two times).
41-
let escapingScalar: Unicode.Scalar? = "\""
44+
let escapingScalar: Unicode.Scalar?
4245

4346
/// Creates the inmutable reader settings from the user provided configuration values.
4447
/// - parameter configuration: The configuration values provided by the API user.
@@ -61,6 +64,8 @@ extension CSVReader {
6164
}
6265
// 2. Set the trim characters set.
6366
self.trimCharacters = configuration.trimStrategry
67+
// 3. Set the escaping scalar.
68+
self.escapingScalar = configuration.escapingStrategy.scalar
6469
}
6570
}
6671
}

Sources/Strategy.swift

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,32 @@ public enum Strategy {
1616
}
1717
}
1818

19+
/// The strategy for escaping quoted fields.
20+
public enum Escaping: ExpressibleByNilLiteral, ExpressibleByUnicodeScalarLiteral {
21+
/// CSV delimiters can not be escaped.
22+
case none
23+
/// Ignore delimiter with in a scalar pair.
24+
case scalar(Unicode.Scalar)
25+
26+
/// Escape double quoted values.
27+
public static let doubleQuote: Self = "\""
28+
29+
public init(nilLiteral: ()) { self = .none }
30+
31+
public init(unicodeScalarLiteral value: Unicode.Scalar) {
32+
self = .scalar(value)
33+
}
34+
35+
var scalar: Unicode.Scalar? {
36+
switch self {
37+
case .none:
38+
return nil
39+
case .scalar(let scalar):
40+
return scalar
41+
}
42+
}
43+
}
44+
1945
/// The strategy to use for non-standard floating-point values (IEEE 754 infinity and NaN).
2046
public enum NonConformingFloat {
2147
/// Throw upon encountering non-conforming values. This is the default strategy.

Tests/CodableCSVTests/ActiveTests/ReaderTests.swift

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ extension ReaderTests {
8383
let fieldDelimiters: [Delimiter.Field] = [",", ";", "\t", "|", "||", "|-|"]
8484
let headerStrategy: [Strategy.Header] = [.none, .firstLine, /*.unknown*/]
8585
let trimStrategy: [CharacterSet] = [.init(), .whitespaces]
86+
let escapingStrategy: [Strategy.Escaping] = [.none, .doubleQuote]
8687
let presamples: [Bool] = [true, false]
8788
// The data used for testing.
8889
let (headers, content) = (TestData.headers, TestData.content)
@@ -126,15 +127,18 @@ extension ReaderTests {
126127
var toTrim = t
127128
if f.rawValue.count == 1, t.contains(f.rawValue.first!) { toTrim.remove(f.rawValue.first!) }
128129
if r.rawValue.count == 1, t.contains(r.rawValue.first!) { toTrim.remove(r.rawValue.first!) }
129-
130-
for p in presamples {
131-
var c = CSVReader.Configuration()
132-
c.delimiters = pair
133-
c.headerStrategy = h
134-
c.trimStrategry = toTrim
135-
c.presample = p
136-
137-
XCTAssertNoThrow(try work(c, encoded))
130+
131+
for e in escapingStrategy {
132+
for p in presamples {
133+
var c = CSVReader.Configuration()
134+
c.delimiters = pair
135+
c.headerStrategy = h
136+
c.trimStrategry = toTrim
137+
c.escapingStrategy = e
138+
c.presample = p
139+
140+
XCTAssertNoThrow(try work(c, encoded))
141+
}
138142
}
139143
}
140144
}

0 commit comments

Comments
 (0)