Skip to content

Instantly share code, notes, and snippets.

@H2CO3
Last active September 16, 2025 18:42
Show Gist options
  • Save H2CO3/fe72493d06e4ce93fafc7cfd309e4a91 to your computer and use it in GitHub Desktop.
Save H2CO3/fe72493d06e4ce93fafc7cfd309e4a91 to your computer and use it in GitHub Desktop.
Complete, self-contained code example for http://narnium.com/posts/2025-09-16-002-type-safe-mappings.html
{
"mrlStaticMulti": {
"backend": "fastembed",
"path": "/path/to/MRLStaticMultilingual"
},
"potionRetrieval32M": {
"backend": "model2vec",
"path": "/path/to/PotionRetrieval32M"
}
}
import Foundation
enum EmbeddingModelBackend: String, Codable {
case fastembed
case model2vec
}
struct EmbeddingModelDescriptor: Codable {
let backend: EmbeddingModelBackend
let path: String
}
struct EmbeddingModelCollection: Codable {
var mrlStaticMulti: EmbeddingModelDescriptor
var potionRetrieval32M: EmbeddingModelDescriptor
enum CodingKeys: String, TotalCodingKey {
case mrlStaticMulti
case potionRetrieval32M
}
// notice that this is infallible - it returns a descriptor unconditionally, not just an `Optional`
subscript(codingKey: CodingKeys) -> EmbeddingModelDescriptor {
get { self[keyPath: codingKey.keyPath] }
set { self[keyPath: codingKey.keyPath] = newValue }
}
}
extension EmbeddingModelCollection.CodingKeys {
var keyPath: WritableKeyPath<EmbeddingModelCollection, EmbeddingModelDescriptor> {
switch self {
case .mrlStaticMulti: \.mrlStaticMulti
case .potionRetrieval32M: \.potionRetrieval32M
}
}
}
let jsonData = try! Data(contentsOf: URL(filePath: "models.json"))
let wrapper = try! JSONDecoder().decode(
TotalDecodable<EmbeddingModelCollection, EmbeddingModelCollection.CodingKeys>.self,
from: jsonData,
)
print(wrapper.inner)
////////
public protocol TotalCodingKey: Sendable, Equatable, Hashable, CaseIterable, CodingKey {
}
public struct TotalDecodable<Collection, CodingKey> {
var inner: Collection
}
extension TotalDecodable: Decodable
where
Collection: Decodable,
CodingKey: TotalCodingKey
{
public init(from decoder: any Decoder) throws {
let keyedContainer = try decoder.container(keyedBy: AnyCodingKey.self)
let extraKeys = Set(keyedContainer.allKeys.map { $0.stringValue }).subtracting(CodingKey.allCases.map { $0.stringValue })
guard extraKeys.isEmpty else {
throw ExtraKeysError(extraKeys: extraKeys.sorted())
}
self.inner = try Collection.init(from: decoder)
}
}
private struct AnyCodingKey: CodingKey, Sendable, Equatable, Hashable {
let stringValue: String
let intValue: Int? = nil
init?(intValue: Int) {
return nil
}
init?(stringValue: String) {
self.stringValue = stringValue
}
}
public struct ExtraKeysError: Error {
public let extraKeys: [String]
}
extension ExtraKeysError: CustomStringConvertible {
public var description: String {
"extra unused keys \(extraKeys)"
}
}
extension ExtraKeysError: CustomDebugStringConvertible {
public var debugDescription: String {
"ExtraKeysError: \(self.description)"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment