chore: import upstream snapshot with attribution
container project - merge build / Invoke build (push) Successful in 1s
container project - merge build / Invoke build (push) Successful in 1s
This commit is contained in:
@@ -0,0 +1,120 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Copyright © 2026 Apple Inc. and the container project authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
import Configuration
|
||||
|
||||
/// A decoder that decodes `Decodable` types from a ``ConfigSnapshotReader``.
|
||||
///
|
||||
/// `ConfigSnapshotDecoder` bridges Swift's `Decodable` protocol with the configuration
|
||||
/// provider system, allowing you to decode structured configuration into typed
|
||||
/// Swift values.
|
||||
///
|
||||
/// ```swift
|
||||
/// struct AppConfig: Decodable {
|
||||
/// var host: String
|
||||
/// var port: Int
|
||||
/// var database: DatabaseConfig
|
||||
/// }
|
||||
///
|
||||
/// struct DatabaseConfig: Decodable {
|
||||
/// var connectionString: String
|
||||
/// var maxConnections: Int
|
||||
/// }
|
||||
///
|
||||
/// let reader = ConfigReader(providers: [envProvider, jsonProvider])
|
||||
/// let snapshot = reader.snapshot()
|
||||
/// let config = try ConfigSnapshotDecoder().decode(AppConfig.self, from: snapshot)
|
||||
/// ```
|
||||
///
|
||||
/// Nested structs map to dot-separated key paths. In the example above,
|
||||
/// `database.connectionString` and `database.maxConnections` are looked up
|
||||
/// from the snapshot.
|
||||
public struct ConfigSnapshotDecoder: Sendable {
|
||||
|
||||
public var userInfo: [CodingUserInfoKey: any Sendable]
|
||||
private let typeDecodingStrategies: [ObjectIdentifier: AnyConfigDecodingStrategy]
|
||||
|
||||
public init(decodingStrategies: [any ConfigDecodingStrategy] = [URLConfigDecodingStrategy()]) {
|
||||
self.userInfo = [:]
|
||||
var strategies: [ObjectIdentifier: AnyConfigDecodingStrategy] = [:]
|
||||
for strategy in decodingStrategies {
|
||||
let erased = AnyConfigDecodingStrategy(strategy)
|
||||
strategies[erased.valueTypeID] = erased
|
||||
}
|
||||
self.typeDecodingStrategies = strategies
|
||||
}
|
||||
|
||||
public func decode<T: Decodable>(
|
||||
_ type: T.Type,
|
||||
from snapshot: ConfigSnapshotReader
|
||||
) throws -> T {
|
||||
if type is any UnsupportedDictionaryDecoding.Type {
|
||||
throw DecodingError.typeMismatch(
|
||||
T.self,
|
||||
DecodingError.Context(
|
||||
codingPath: [],
|
||||
debugDescription:
|
||||
"ConfigSnapshotDecoder does not support decoding dictionaries (got \(T.self)). Represent dynamic keys as nested structs with known property names."
|
||||
)
|
||||
)
|
||||
}
|
||||
let decoder = ConfigSnapshotDecoderImpl(
|
||||
snapshot: snapshot,
|
||||
codingPath: [],
|
||||
userInfo: userInfo.mapValues { $0 as Any },
|
||||
typeDecodingStrategies: typeDecodingStrategies
|
||||
)
|
||||
return try T(from: decoder)
|
||||
}
|
||||
}
|
||||
|
||||
struct ConfigSnapshotDecoderImpl: Decoder {
|
||||
let snapshot: ConfigSnapshotReader
|
||||
let codingPath: [any CodingKey]
|
||||
let userInfo: [CodingUserInfoKey: Any]
|
||||
let typeDecodingStrategies: [ObjectIdentifier: AnyConfigDecodingStrategy]
|
||||
|
||||
func container<Key: CodingKey>(
|
||||
keyedBy type: Key.Type
|
||||
) throws -> KeyedDecodingContainer<Key> {
|
||||
KeyedDecodingContainer(
|
||||
KeyedContainer<Key>(
|
||||
snapshot: snapshot,
|
||||
codingPath: codingPath,
|
||||
userInfo: userInfo,
|
||||
typeDecodingStrategies: typeDecodingStrategies
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
func unkeyedContainer() throws -> any UnkeyedDecodingContainer {
|
||||
UnkeyedContainer(
|
||||
snapshot: snapshot,
|
||||
codingPath: codingPath,
|
||||
userInfo: userInfo,
|
||||
typeDecodingStrategies: typeDecodingStrategies
|
||||
)
|
||||
}
|
||||
|
||||
func singleValueContainer() throws -> any SingleValueDecodingContainer {
|
||||
SingleValueContainer(
|
||||
snapshot: snapshot,
|
||||
codingPath: codingPath,
|
||||
userInfo: userInfo,
|
||||
typeDecodingStrategies: typeDecodingStrategies
|
||||
)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user