Skip to content

Commit 82f5aa2

Browse files
authored
Merge pull request #31 from Kirow/feature/numeric-bool
Numeric Bool
2 parents ce65188 + e17839d commit 82f5aa2

File tree

5 files changed

+103
-0
lines changed

5 files changed

+103
-0
lines changed

sources/declarative/decodable/DecoderConfiguration.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ extension Strategy {
6565
///
6666
/// The value: `True`, `TRUE`, `TruE` or `YES`are accepted.
6767
case insensitive
68+
/// Decodes the `Bool` from an underlying `0`/`1`
69+
case numeric
6870
/// Decodes the `Bool` as a custom value decoded by the given closure.
6971
///
7072
/// If the closure fails to decode a value from the given decoder, the error will be bubled up.

sources/declarative/decodable/containers/SingleValueDecodingContainer.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,14 @@ extension ShadowDecoder.SingleValueContainer {
7474
default: return nil
7575
}
7676
}
77+
case .numeric:
78+
return try self._lowlevelDecode {
79+
switch $0 {
80+
case "1": return true
81+
case "0": return false
82+
default: return nil
83+
}
84+
}
7785
case .custom(let closure):
7886
return try closure(self._decoder)
7987
}

sources/declarative/encodable/EncoderConfiguration.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@ extension Strategy {
5858
public enum BoolEncoding {
5959
/// Defers to `String`'s initializer.
6060
case deferredToString
61+
/// Encode the `Bool` as `0` or `1`
62+
case numeric
6163
/// Encode the `Bool` as a custom value encoded by the given closure.
6264
///
6365
/// If the closure fails to encode a value into the given encoder, the error will be bubled up.

sources/declarative/encodable/containers/SingleValueEncodingContainer.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ extension ShadowEncoder.SingleValueContainer {
5959
mutating func encode(_ value: Bool) throws {
6060
switch self._encoder.sink._withUnsafeGuaranteedRef({ $0.configuration.boolStrategy }) {
6161
case .deferredToString: try self._lowlevelEncoding { String(value) }
62+
case .numeric: try self._lowlevelEncoding { value ? "1" : "0" }
6263
case .custom(let closure): return try closure(value, self._encoder)
6364
}
6465
}
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import XCTest
2+
import CodableCSV
3+
4+
/// Tests checking support for encoding/decoding numeric booleans.
5+
final class CodableNumericBoolTests: XCTestCase {
6+
override func setUp() {
7+
self.continueAfterFailure = false
8+
}
9+
}
10+
11+
extension CodableNumericBoolTests {
12+
private struct _Object: Codable, Equatable {
13+
var value1: Bool
14+
var value2: Bool
15+
var value3: Bool?
16+
}
17+
18+
private static let list: [_Object] = [
19+
.init(value1: true, value2: false, value3: nil),
20+
.init(value1: true, value2: true, value3: false),
21+
.init(value1: false, value2: false, value3: true)
22+
]
23+
24+
private static let csvOKHeader1 =
25+
"""
26+
value1,value2,value3
27+
1,0,
28+
1,1,0
29+
0,0,1
30+
31+
"""
32+
private static let csvOKHeader2 =
33+
"""
34+
value3,value1,value2
35+
,1,0
36+
0,1,1
37+
1,0,0
38+
39+
"""
40+
}
41+
42+
extension CodableNumericBoolTests {
43+
/// Test the regular numeric bool coding
44+
func testRegularUsage() throws {
45+
guard let data = Self.csvOKHeader1.data(using: .utf8) else {
46+
fatalError()
47+
}
48+
let decoder = CSVDecoder {
49+
$0.headerStrategy = .firstLine
50+
$0.boolStrategy = .numeric
51+
}
52+
var result = try decoder.decode([_Object].self, from: data)
53+
XCTAssertEqual(Self.list, result)
54+
55+
guard let data2 = Self.csvOKHeader2.data(using: .utf8) else {
56+
fatalError()
57+
}
58+
59+
result = try decoder.decode([_Object].self, from: data2)
60+
XCTAssertEqual(Self.list, result)
61+
62+
let encoder = CSVEncoder {
63+
$0.headers = ["value1", "value2", "value3"]
64+
$0.boolStrategy = .numeric
65+
}
66+
67+
let csv = try encoder.encode(result, into: String.self)
68+
XCTAssertEqual(Self.csvOKHeader1, csv)
69+
70+
}
71+
72+
/// Test nil failure
73+
func testThrows() throws {
74+
// value2 = nil on 2nd row, must throw an exception
75+
guard let data = """
76+
value1,value2,value3
77+
1,0,
78+
1,,0
79+
""".data(using: .utf8) else {
80+
fatalError()
81+
}
82+
let decoder = CSVDecoder {
83+
$0.headerStrategy = .firstLine
84+
$0.boolStrategy = .numeric
85+
}
86+
XCTAssertThrowsError(try decoder.decode([_Object].self, from: data))
87+
}
88+
89+
}
90+

0 commit comments

Comments
 (0)