PureYAML/Sources/Decoding/Decoding.KeyedContainer.swift

161 lines
5.1 KiB
Swift

struct KeyedDecodingContainerImpl<Key: CodingKey>: KeyedDecodingContainerProtocol {
let mapping: PureYAML.Model.Mapping
let codingPath: [any CodingKey]
let userInfo: [CodingUserInfoKey: Any]
let validatesInput: Bool
var allKeys: [Key] {
mapping.pairs.compactMap { pair in
pair.keyNode.stringValue.flatMap(Key.init(stringValue:))
}
}
func contains(_ key: Key) -> Bool {
mapping[key.stringValue] != nil
}
func decodeNil(forKey key: Key) throws -> Bool {
try value(for: key) == .null
}
func decode(_: Bool.Type, forKey key: Key) throws -> Bool {
try singleValue(for: key).decode(Bool.self)
}
func decode(_: String.Type, forKey key: Key) throws -> String {
try singleValue(for: key).decode(String.self)
}
func decode(_: Double.Type, forKey key: Key) throws -> Double {
try singleValue(for: key).decode(Double.self)
}
func decode(_: Float.Type, forKey key: Key) throws -> Float {
try singleValue(for: key).decode(Float.self)
}
func decode(_: Int.Type, forKey key: Key) throws -> Int {
try singleValue(for: key).decode(Int.self)
}
func decode(_: Int8.Type, forKey key: Key) throws -> Int8 {
try singleValue(for: key).decode(Int8.self)
}
func decode(_: Int16.Type, forKey key: Key) throws -> Int16 {
try singleValue(for: key).decode(Int16.self)
}
func decode(_: Int32.Type, forKey key: Key) throws -> Int32 {
try singleValue(for: key).decode(Int32.self)
}
func decode(_: Int64.Type, forKey key: Key) throws -> Int64 {
try singleValue(for: key).decode(Int64.self)
}
func decode(_: UInt.Type, forKey key: Key) throws -> UInt {
try singleValue(for: key).decode(UInt.self)
}
func decode(_: UInt8.Type, forKey key: Key) throws -> UInt8 {
try singleValue(for: key).decode(UInt8.self)
}
func decode(_: UInt16.Type, forKey key: Key) throws -> UInt16 {
try singleValue(for: key).decode(UInt16.self)
}
func decode(_: UInt32.Type, forKey key: Key) throws -> UInt32 {
try singleValue(for: key).decode(UInt32.self)
}
func decode(_: UInt64.Type, forKey key: Key) throws -> UInt64 {
try singleValue(for: key).decode(UInt64.self)
}
func decode<Value: Decodable>(_: Value.Type, forKey key: Key) throws -> Value {
try Value(from: decoder(for: key))
}
func nestedContainer<NestedKey: CodingKey>(
keyedBy _: NestedKey.Type,
forKey key: Key,
) throws -> KeyedDecodingContainer<NestedKey> {
let value = try value(for: key)
guard case let .mapping(mapping) = value else {
throw PureYAML.Decoding.Error.typeMismatch(
expected: "mapping",
actual: value.kindDescription,
path: PureYAML.Validation.Path(codingPath: codingPath + [key]),
)
}
return KeyedDecodingContainer(KeyedDecodingContainerImpl<NestedKey>(
mapping: mapping,
codingPath: codingPath + [key],
userInfo: userInfo,
validatesInput: validatesInput,
))
}
func nestedUnkeyedContainer(forKey key: Key) throws -> any UnkeyedDecodingContainer {
let value = try value(for: key)
try validateIfNeeded(value, forKey: key)
guard case let .sequence(values) = value else {
throw PureYAML.Decoding.Error.typeMismatch(
expected: "sequence",
actual: value.kindDescription,
path: PureYAML.Validation.Path(codingPath: codingPath + [key]),
)
}
return UnkeyedDecodingContainerImpl(
values: values,
codingPath: codingPath + [key],
userInfo: userInfo,
validatesInput: validatesInput,
)
}
func superDecoder() throws -> any Swift.Decoder {
try decoder(for: SuperCodingKey())
}
func superDecoder(forKey key: Key) throws -> any Swift.Decoder {
try decoder(for: key)
}
private func singleValue(for key: any CodingKey) throws -> any SingleValueDecodingContainer {
try decoder(for: key).singleValueContainer()
}
private func decoder(for key: any CodingKey) throws -> PureYAML.Decoding.Decoder {
try PureYAML.Decoding.Decoder(
value: value(for: key),
codingPath: codingPath + [key],
userInfo: userInfo,
validatesInput: validatesInput,
)
}
private func value(for key: any CodingKey) throws -> PureYAML.Model.Value {
guard let value = mapping[key.stringValue] else {
throw PureYAML.Decoding.Error.keyNotFound(
key: key.stringValue,
path: PureYAML.Validation.Path(codingPath: codingPath + [key]),
)
}
return value
}
private func validateIfNeeded(
_ value: PureYAML.Model.Value,
forKey key: any CodingKey,
) throws {
if validatesInput {
try PureYAML.Decoding.validate(value, codingPath: codingPath + [key])
}
}
}