Sources/Confidence/ConfidenceValueHash.swift (84 lines of code) (raw):
import CryptoKit
import Foundation
public extension ConfidenceStruct {
func hash() -> String {
hashConfidenceValue(context: self)
}
}
func hashConfidenceValue(context: ConfidenceStruct) -> String {
var hasher = SHA256()
context.sorted { $0.key < $1.key }.forEach { key, value in
hasher.update(data: key.data)
hashValue(value: value, hasher: &hasher)
}
let digest = hasher.finalize()
return digest.map { String(format: "%02hhx", $0) }.joined()
}
// swiftlint:disable:next cyclomatic_complexity
func hashValue(value: ConfidenceValue, hasher: inout some HashFunction) {
switch value.type() {
case .boolean:
if let booleanData = value.asBoolean()?.data {
hasher.update(data: booleanData)
}
case .string:
if let stringData = value.asString()?.data {
hasher.update(data: stringData)
}
case .integer:
if let integerData = value.asInteger()?.data {
hasher.update(data: integerData)
}
case .double:
if let doubleData = value.asDouble()?.data {
hasher.update(data: doubleData)
}
case .date:
if let dateData = value.asDateComponents()?.date?.data {
hasher.update(data: dateData)
}
case .list:
value.asList()?.forEach { listValue in
hashValue(value: listValue, hasher: &hasher)
}
case .timestamp:
if let timestampData = value.asDate()?.data {
hasher.update(data: timestampData)
}
case .structure:
value.asStructure()?.sorted { $0.key < $1.key }.forEach { key, structureValue in
hasher.update(data: key.data)
hashValue(value: structureValue, hasher: &hasher)
}
case .null:
hasher.update(data: UInt8(0).data)
}
}
extension StringProtocol {
var data: Data { .init(utf8) }
}
extension FixedWidthInteger {
var data: Data {
var source = self
return .init(bytes: &source, count: MemoryLayout<Self>.size)
}
}
extension Float {
var data: Data {
var source = self
return .init(bytes: &source, count: MemoryLayout<Self>.size)
}
}
extension Double {
var data: Data {
var source = self
return .init(bytes: &source, count: MemoryLayout<Self>.size)
}
}
extension Bool {
var data: Data { UInt8(self ? 1 : 0).data }
}
extension Date {
var data: Data {
self.timeIntervalSince1970.data
}
}