diff --git a/Sources/Haystack/Dict.swift b/Sources/Haystack/Dict.swift index ad393a0..069e280 100644 --- a/Sources/Haystack/Dict.swift +++ b/Sources/Haystack/Dict.swift @@ -105,12 +105,8 @@ public extension Dict { ) } } else { - typeLoop: for type in ValType.allCases { - if let val = try? container.decode(type.type, forKey: key) { - elements[key.stringValue] = val - break typeLoop - } - } + let val = try container.decode(AnyVal.self, forKey: key) + elements[key.stringValue] = val.val } } self.elements = elements diff --git a/Sources/Haystack/List.swift b/Sources/Haystack/List.swift index d43e8db..1b2b2c7 100644 --- a/Sources/Haystack/List.swift +++ b/Sources/Haystack/List.swift @@ -42,12 +42,8 @@ public extension List { var elements = [any Val]() containerLoop: while !container.isAtEnd { - typeLoop: for type in ValType.allCases { - if let val = try? container.decode(type.type) { - elements.append(val) - break typeLoop - } - } + let val = try container.decode(AnyVal.self) + elements.append(val.val) } self.elements = elements } diff --git a/Sources/Haystack/Utils/AnyVal.swift b/Sources/Haystack/Utils/AnyVal.swift new file mode 100644 index 0000000..7aa1a13 --- /dev/null +++ b/Sources/Haystack/Utils/AnyVal.swift @@ -0,0 +1,55 @@ +/// This type-erased struct centralizes general operations that may act across different Haystack value types unknown +/// at compile time like equality, decoding, and comparison. +struct AnyVal: Codable, Hashable, Sendable { + /// The underlying value of the AnyVal, which conforms to the Val protocol. + let val: any Val + + init(_ val: any Val) { + self.val = val + } + + func toZinc() -> String { + return val.toZinc() + } +} + +// AnyVal + Equatable +extension AnyVal { + static func == (lhs: AnyVal, rhs: AnyVal) -> Bool { + return lhs.val.equals(rhs.val) + } +} + +// AnyVal + Codable +extension AnyVal { + /// Read from decodable data + /// See [JSON format](https://project-haystack.org/doc/docHaystack/Json#list) + init(from decoder: Decoder) throws { + typeLoop: for type in ValType.allCases { + if let val = try? type.type.init(from: decoder) { + self = .init(val) + return + } + } + throw DecodingError.typeMismatch( + AnyVal.self, + .init( + codingPath: decoder.codingPath, + debugDescription: "No Val type matched" + ) + ) + } + + /// Write to encodable data + /// See [JSON format](https://project-haystack.org/doc/docHaystack/Json#list) + func encode(to encoder: Encoder) throws { + try val.encode(to: encoder) + } +} + +// AnyVal + Hashable +extension AnyVal { + func hash(into hasher: inout Hasher) { + hasher.combine(val) + } +}