// // Copyright Amazon.com Inc. or its affiliates. // All Rights Reserved. // // SPDX-License-Identifier: Apache-2.0 // import Foundation /// - Warning: Although this has `public` access, it is intended for internal use and should not be used directly /// by host applications. The behavior of this may change without warning. public struct ModelRegistry { private static let concurrencyQueue = DispatchQueue(label: "com.amazonaws.ModelRegistry.concurrency", target: DispatchQueue.global()) /// ModelDecoders are used to decode untyped model data, looking up by model name private typealias ModelDecoder = (String, JSONDecoder?) throws -> Model private static var modelTypes = [ModelName: Model.Type]() private static var modelDecoders = [ModelName: ModelDecoder]() private static var modelSchemaMapping = [ModelName: ModelSchema]() public static var models: [Model.Type] { concurrencyQueue.sync { Array(modelTypes.values) } } public static var modelSchemas: [ModelSchema] { concurrencyQueue.sync { Array(modelSchemaMapping.values) } } public static func register(modelType: Model.Type) { register(modelType: modelType, modelSchema: modelType.schema) { (jsonString, jsonDecoder) -> Model in let model = try modelType.from(json: jsonString, decoder: jsonDecoder) return model } } public static func register(modelType: Model.Type, modelSchema: ModelSchema, jsonDecoder: @escaping (String, JSONDecoder?) throws -> Model) { concurrencyQueue.sync { let modelDecoder: ModelDecoder = { jsonString, decoder in return try jsonDecoder(jsonString, decoder) } let modelName = modelSchema.name modelSchemaMapping[modelName] = modelSchema modelTypes[modelName] = modelType modelDecoders[modelName] = modelDecoder } } public static func modelType(from name: ModelName) -> Model.Type? { concurrencyQueue.sync { modelTypes[name] } } @available(*, deprecated, message: """ Retrieving model schema using Model.Type is deprecated, instead retrieve using model name. """) public static func modelSchema(from modelType: Model.Type) -> ModelSchema? { return modelSchema(from: modelType.modelName) } public static func modelSchema(from name: ModelName) -> ModelSchema? { concurrencyQueue.sync { modelSchemaMapping[name] } } public static func decode(modelName: ModelName, from jsonString: String, jsonDecoder: JSONDecoder? = nil) throws -> Model { try concurrencyQueue.sync { guard let decoder = modelDecoders[modelName] else { throw DataStoreError.decodingError( "No decoder found for model named \(modelName)", """ There is no decoder registered for the model named \(modelName). \ Register models with `ModelRegistry.register(modelName:)` at startup. """) } return try decoder(jsonString, jsonDecoder) } } } extension ModelRegistry { static func reset() { concurrencyQueue.sync { modelTypes = [:] modelDecoders = [:] modelSchemaMapping = [:] } } }