Sources/Confidence/TypeMapper.swift (100 lines of code) (raw):
import Foundation
enum TypeMapper {
static internal func convert(structure: ConfidenceStruct) -> NetworkStruct {
return NetworkStruct(fields: structure.compactMapValues(convert))
}
static internal func convert(structure: NetworkStruct, schema: StructFlagSchema) throws -> ConfidenceStruct {
return Dictionary(uniqueKeysWithValues: try structure.fields.map { field, value in
(field, try convert(value: value, schema: schema.schema[field]))
})
}
// swiftlint:disable:next cyclomatic_complexity
static func convert(value: ConfidenceValue) -> NetworkValue? {
switch value.type() {
case .boolean:
guard let value = value.asBoolean() else {
return nil
}
return NetworkValue.boolean(value)
case .string:
guard let value = value.asString() else {
return nil
}
return NetworkValue.string(value)
case .integer:
guard let value = value.asInteger() else {
return nil
}
return NetworkValue.number(Double(value))
case .double:
guard let value = value.asDouble() else {
return nil
}
return NetworkValue.number(value)
case .date:
let dateFormatter = ISO8601DateFormatter()
dateFormatter.timeZone = TimeZone.current
dateFormatter.formatOptions = [.withFullDate]
guard let value = value.asDateComponents(), let dateString = Calendar.current.date(from: value) else {
return NetworkValue.string("") // This should never happen
}
return NetworkValue.string(dateFormatter.string(from: dateString))
case .timestamp:
guard let value = value.asDate() else {
return nil
}
let timestampFormatter = ISO8601DateFormatter()
timestampFormatter.timeZone = TimeZone.init(identifier: "UTC")
let timestamp = timestampFormatter.string(from: value)
return NetworkValue.string(timestamp)
case .list:
guard let value = value.asList() else {
return nil
}
return NetworkValue.list(value.compactMap(convert))
case .structure:
guard let value = value.asStructure() else {
return nil
}
return NetworkValue.structure(NetworkStruct(fields: value.compactMapValues(convert)))
case .null:
return .null
}
}
// swiftlint:disable:next cyclomatic_complexity
static private func convert(value: NetworkValue, schema: FlagSchema?) throws -> ConfidenceValue {
guard let fieldType = schema else {
throw ConfidenceError.parseError(message: "Mismatch between schema and value")
}
switch value {
case .null:
return .init(null: ())
case .number(let value):
switch fieldType {
case .intSchema:
return .init(integer: Int(value))
case .doubleSchema:
return .init(double: value)
default:
throw ConfidenceError.parseError(message: "Number field must have schema type int or double")
}
case .string(let value):
return .init(string: value)
case .boolean(let value):
return .init(boolean: value)
case .structure(let mapValue):
guard case .structSchema(let structSchema) = fieldType else {
throw ConfidenceError.parseError(message: "Field is struct in schema but something else in value")
}
return .init(structure: Dictionary(
uniqueKeysWithValues: try mapValue.fields.map { field, fieldValue in
return (field, try convert(value: fieldValue, schema: structSchema.schema[field]))
}))
case .list(let values):
guard case .listSchema(let listSchema) = fieldType else {
throw ConfidenceError.parseError(message: "Field is list in schema but something else in value")
}
return ConfidenceValue.init(list: try values.map { fieldValue in
try convert(value: fieldValue, schema: listSchema)
})
}
}
}