Skip to content

Commit

Permalink
Simplify and finalize ConfidenceValue
Browse files Browse the repository at this point in the history
  • Loading branch information
fabriziodemaria committed Apr 3, 2024
1 parent 97198c4 commit 32daa61
Show file tree
Hide file tree
Showing 2 changed files with 83 additions and 115 deletions.
93 changes: 31 additions & 62 deletions Sources/Confidence/ConfidenceValue.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,48 +8,12 @@ public enum ConfidenceValue: Equatable, Codable {
case string(String)
case integer(Int64)
case double(Double)
case date(Date)
case date(DateComponents)
case timestamp(Date)
case list([ConfidenceValue])
case structure([String: ConfidenceValue])
case null

public static func of<T>(_ value: T) -> ConfidenceValue {
if let value = value as? Bool {
return .boolean(value)
} else if let value = value as? String {
return .string(value)
} else if let value = value as? Int64 {
return .integer(value)
} else if let value = value as? Double {
return .double(value)
} else if let value = value as? Date {
return .date(value)
} else if let value = value as? Date {
return .timestamp(value)
} else {
return .null
}
}

public func getTyped<T>() -> T? {
if let value = self as? T {
return value
}

switch self {
case .boolean(let value): return value as? T
case .string(let value): return value as? T
case .integer(let value): return value as? T
case .double(let value): return value as? T
case .date(let value): return value as? T
case .timestamp(let value): return value as? T
case .list(let value): return value as? T
case .structure(let value): return value as? T
case .null: return nil
}
}

public func asBoolean() -> Bool? {
if case let .boolean(bool) = self {
return bool
Expand Down Expand Up @@ -82,15 +46,15 @@ public enum ConfidenceValue: Equatable, Codable {
return nil
}

public func asDate() -> Date? {
if case let .date(date) = self {
return date
public func asDateComponents() -> DateComponents? {
if case let .date(dateComponents) = self {
return dateComponents
}

return nil
}

public func asTimestamp() -> Date? {
public func asDate() -> Date? {
if case let .timestamp(date) = self {
return date
}
Expand Down Expand Up @@ -149,31 +113,36 @@ extension ConfidenceValue: CustomStringConvertible {
}

extension ConfidenceValue {
public func decode<T: Decodable>() throws -> T {
let data = try JSONSerialization.data(withJSONObject: toJson(value: self))
return try JSONDecoder().decode(T.self, from: data)
}
public func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()

func toJson(value: ConfidenceValue) throws -> Any {
switch value {
case .boolean(let bool):
return bool
case .string(let string):
return string
case .integer(let int64):
return int64
switch self {
case .null:
try container.encodeNil()
case .integer(let integer):
try container.encode(integer)
case .double(let double):
return double
case .date(let date):
return date.timeIntervalSinceReferenceDate
try container.encode(double)
case .string(let string):
try container.encode(string)
case .boolean(let boolean):
try container.encode(boolean)
case .date(let dateComponents):
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "dd-MM-yyyy"
if let date = Calendar.current.date(from: dateComponents) {
try container.encode(dateFormatter.string(from: date))
} else {
throw ConfidenceError.internalError(message: "Could not create date from components")
}
case .timestamp(let date):
return date.timeIntervalSinceReferenceDate
case .list(let list):
return try list.map(self.toJson)
let isoFormatter = ISO8601DateFormatter()
let isoString = isoFormatter.string(from: date)
try container.encode(isoString)
case .structure(let structure):
return try structure.mapValues(self.toJson)
case .null:
return NSNull()
try container.encode(structure)
case .list(let list):
try container.encode(list)
}
}
}
105 changes: 52 additions & 53 deletions Tests/ConfidenceTests/ConfidenceValueTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,20 @@ final class ConfidenceConfidenceValueTests: XCTestCase {
XCTAssertEqual(value.asString(), "test")
}

func testStringShouldConvertToDate() throws {
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
let date = try XCTUnwrap(formatter.date(from: "2022-01-01 12:00:00"))
let value: ConfidenceValue = .timestamp(date)
XCTAssertEqual(value.asDate(), date)
}

func testStringShouldConvertToDateComponents() {
let dateComponents = DateComponents(year: 2024, month: 4, day: 3)
let value: ConfidenceValue = .date(dateComponents)
XCTAssertEqual(value.asDateComponents(), dateComponents)
}

func testListShouldConvertToList() {
let value: ConfidenceValue = .list([.integer(3), .integer(4)])
XCTAssertEqual(value.asList(), [.integer(3), .integer(4)])
Expand All @@ -42,70 +56,55 @@ final class ConfidenceConfidenceValueTests: XCTestCase {
XCTAssertEqual(value.asList(), [])
}

func testEncodeDecode() throws {
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
let date = try XCTUnwrap(formatter.date(from: "2022-01-01 12:00:00"))

let value: ConfidenceValue = .structure([
"null": .null,
"bool": .boolean(true),
"int": .integer(3),
"double": .double(4.5),
"date": .date(date),
"timestamp": .timestamp(date),
"list": .list([.boolean(false), .integer(4)]),
"structure": .structure(["int": .integer(5)]),
])

let result = try JSONEncoder().encode(value)
let decodedConfidenceValue = try JSONDecoder().decode(ConfidenceValue.self, from: result)
func testWrongTypeDoesntThrow() {
let value = ConfidenceValue.null
XCTAssertNil(value.asList())
XCTAssertNil(value.asDouble())
XCTAssertNil(value.asString())
XCTAssertNil(value.asBoolean())
XCTAssertNil(value.asInteger())
XCTAssertNil(value.asStructure())
XCTAssertNil(value.asDate())
XCTAssertNil(value.asDateComponents())
}

XCTAssertEqual(value, decodedConfidenceValue)
func testIsNotNull() {
let value = ConfidenceValue.string("Test")
XCTAssertFalse(value.isNull())
}

func testDecodeConfidenceValue() throws {
func testEncodeDecode() throws {
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
let date = try XCTUnwrap(formatter.date(from: "2022-01-01 12:00:00"))
let dateComponents = DateComponents(year: 2024, month: 4, day: 3)

let value: ConfidenceValue = .structure([
"null": .null,
"bool": .boolean(true),
"int": .integer(3),
"date": .date(dateComponents),
"double": .double(4.5),
"date": .date(date),
"int": .integer(3),
"list": .list([.boolean(false), .integer(4)]),
"null": .null,
"string": .string("value"),
"structure": .structure(["int": .integer(5)]),
"timestamp": .timestamp(date),
"list": .list([.integer(3), .integer(5)]),
"structure": .structure(["field1": .string("test"), "field2": .integer(12)]),
])
let expected = TestConfidenceValue(
bool: true,
int: 3,
double: 4.5,
date: date,
timestamp: date,
list: [3, 5],
structure: .init(field1: "test", field2: 12))

let decodedConfidenceValue: TestConfidenceValue = try value.decode()

XCTAssertEqual(decodedConfidenceValue, expected)
}

struct TestConfidenceValue: Codable, Equatable {
var null: Bool?
var bool: Bool
var int: Int64
var double: Double
var date: Date
var timestamp: Date
var list: [Int64]
var structure: TestSubConfidenceValue
}

struct TestSubConfidenceValue: Codable, Equatable {
var field1: String
var field2: Int64
let encoder = JSONEncoder()
encoder.outputFormatting = .sortedKeys
let resultString = String(data: try encoder.encode(value), encoding: .utf8)
let expectedString = """
{\"bool\":true,
\"date\":\"03-04-2024\",
\"double\":4.5,
\"int\":3,
\"list\":[false,4],
\"null\":null,
\"string\":\"value\",
\"structure\":{\"int\":5},
\"timestamp\":\"2022-01-01T11:00:00Z\"}
""".replacingOccurrences(of: "\n", with: "") // Newlines were added for readability

XCTAssertEqual(resultString, expectedString)
}
}

0 comments on commit 32daa61

Please sign in to comment.