Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Sources/OpenAPIRuntime/Conversion/CodableExtensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ extension Encoder {
}

/// A freeform String coding key for decoding undocumented values.
private struct StringKey: CodingKey, Hashable, Comparable {
struct StringKey: CodingKey, Hashable, Comparable {
Comment thread
czechboy0 marked this conversation as resolved.
Outdated

var stringValue: String
var intValue: Int? {
Expand Down
78 changes: 69 additions & 9 deletions Sources/OpenAPIRuntime/Conversion/ErrorExtensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,19 @@ extension DecodingError {
/// occurred.
/// - codingPath: The coding path to the decoder that attempted to decode
/// the type.
/// - errors: The errors encountered when decoding individual cases.
/// - Returns: A decoding error.
static func failedToDecodeAnySchema(
type: Any.Type,
codingPath: [any CodingKey]
codingPath: [any CodingKey],
errors: [any Error]
) -> Self {
DecodingError.valueNotFound(
type,
DecodingError.Context.init(
codingPath: codingPath,
debugDescription: "The anyOf structure did not decode into any child schema."
debugDescription: "The anyOf structure did not decode into any child schema.",
underlyingError: MultiError(errors: errors)
)
)
}
Expand All @@ -43,24 +46,49 @@ extension DecodingError {
/// occurred.
/// - codingPath: The coding path to the decoder that attempted to decode
/// the type.
/// - errors: The errors encountered when decoding individual cases.
/// - Returns: A decoding error.
@_spi(Generated)
public static func failedToDecodeOneOfSchema(
type: Any.Type,
codingPath: [any CodingKey]
codingPath: [any CodingKey],
errors: [any Error]
) -> Self {
DecodingError.valueNotFound(
type,
DecodingError.Context.init(
codingPath: codingPath,
debugDescription: "The oneOf structure did not decode into any child schema."
debugDescription: "The oneOf structure did not decode into any child schema.",
underlyingError: MultiError(errors: errors)
)
)
}
}

@_spi(Generated)
extension DecodingError {
/// Returns a decoding error used by the oneOf decoder when
/// the discriminator property contains an unknown schema name.
/// - Parameters:
/// - type: The type representing the oneOf schema in which the decoding
/// occurred.
/// - discriminatorKey: The discriminator coding key.
/// - discriminatorValue: The unknown value of the discriminator.
/// - codingPath: The coding path to the decoder that attempted to decode
/// the type, with the discriminator value as the last component.
/// - Returns: A decoding error.
@_spi(Generated)
public static func unknownOneOfDiscriminator(
discriminatorKey: any CodingKey,
discriminatorValue: String,
codingPath: [any CodingKey]
) -> Self {
return DecodingError.keyNotFound(
discriminatorKey,
DecodingError.Context.init(
codingPath: codingPath,
debugDescription:
"The oneOf structure does not contain the provided discriminator value '\(discriminatorValue)'."
)
)
}

/// Verifies that the anyOf decoder successfully decoded at least one
/// child schema, and throws an error otherwise.
Expand All @@ -70,17 +98,49 @@ extension DecodingError {
/// occurred.
/// - codingPath: The coding path to the decoder that attempted to decode
/// the type.
/// - errors: The errors encountered when decoding individual cases.
/// - Throws: An error of type `DecodingError.failedToDecodeAnySchema` if none of the child schemas were successfully decoded.
@_spi(Generated)
public static func verifyAtLeastOneSchemaIsNotNil(
_ values: [Any?],
type: Any.Type,
codingPath: [any CodingKey]
codingPath: [any CodingKey],
errors: [any Error]
) throws {
guard values.contains(where: { $0 != nil }) else {
throw DecodingError.failedToDecodeAnySchema(
type: type,
codingPath: codingPath
codingPath: codingPath,
errors: errors
)
}
}
}

/// A wrapper of multiple errors, for example collected during a parallelized
/// operation from the individual subtasks.
struct MultiError: Swift.Error, LocalizedError, CustomStringConvertible {
Comment thread
czechboy0 marked this conversation as resolved.

/// The multiple underlying errors.
var errors: [any Error]

var description: String {
let combinedDescription =
errors
.map { error in
guard let error = error as? (any PrettyStringConvertible) else {
return error.localizedDescription
}
return error.prettyDescription
}
.enumerated()
.map { ($0.offset + 1, $0.element) }
.map { "Error \($0.0): [\($0.1)]" }
.joined(separator: ", ")
return "MultiError (contains \(errors.count) error\(errors.count == 1 ? "" : "s")): \(combinedDescription)"
}

var errorDescription: String? {
description
}
}
71 changes: 71 additions & 0 deletions Sources/OpenAPIRuntime/Deprecated/Deprecated.swift
Original file line number Diff line number Diff line change
Expand Up @@ -98,3 +98,74 @@ extension ServerError {
)
}
}

extension DecodingError {
/// Returns a decoding error used by the oneOf decoder when not a single
/// child schema decodes the received payload.
/// - Parameters:
/// - type: The type representing the oneOf schema in which the decoding
/// occurred.
/// - codingPath: The coding path to the decoder that attempted to decode
/// the type.
/// - Returns: A decoding error.
@_spi(Generated)
@available(*, deprecated)
public static func failedToDecodeOneOfSchema(
type: Any.Type,
codingPath: [any CodingKey]
) -> Self {
DecodingError.valueNotFound(
type,
DecodingError.Context.init(
codingPath: codingPath,
debugDescription: "The oneOf structure did not decode into any child schema."
)
)
}

/// Returns a decoding error used by the anyOf decoder when not a single
/// child schema decodes the received payload.
/// - Parameters:
/// - type: The type representing the anyOf schema in which the decoding
/// occurred.
/// - codingPath: The coding path to the decoder that attempted to decode
/// the type.
/// - Returns: A decoding error.
@available(*, deprecated)
static func failedToDecodeAnySchema(
type: Any.Type,
codingPath: [any CodingKey]
) -> Self {
DecodingError.valueNotFound(
type,
DecodingError.Context.init(
codingPath: codingPath,
debugDescription: "The anyOf structure did not decode into any child schema."
)
)
}

/// Verifies that the anyOf decoder successfully decoded at least one
/// child schema, and throws an error otherwise.
/// - Parameters:
/// - values: An array of optional values to check.
/// - type: The type representing the anyOf schema in which the decoding
/// occurred.
/// - codingPath: The coding path to the decoder that attempted to decode
/// the type.
/// - Throws: An error of type `DecodingError.failedToDecodeAnySchema` if none of the child schemas were successfully decoded.
@_spi(Generated)
@available(*, deprecated)
public static func verifyAtLeastOneSchemaIsNotNil(
_ values: [Any?],
type: Any.Type,
codingPath: [any CodingKey]
) throws {
guard values.contains(where: { $0 != nil }) else {
throw DecodingError.failedToDecodeAnySchema(
type: type,
codingPath: codingPath
)
}
}
}
16 changes: 12 additions & 4 deletions Tests/OpenAPIRuntimeTests/URICoder/Test_URICodingRoundtrip.swift
Original file line number Diff line number Diff line change
Expand Up @@ -50,22 +50,30 @@ final class Test_URICodingRoundtrip: Test_Runtime {
self.value3 = value3
}
init(from decoder: any Decoder) throws {
var errors: [any Error] = []
do {
let container = try decoder.singleValueContainer()
value1 = try? container.decode(Foundation.Date.self)
value1 = try container.decode(Foundation.Date.self)
} catch {
errors.append(error)
}
do {
let container = try decoder.singleValueContainer()
value2 = try? container.decode(SimpleEnum.self)
value2 = try container.decode(SimpleEnum.self)
} catch {
errors.append(error)
}
do {
let container = try decoder.singleValueContainer()
value3 = try? container.decode(TrivialStruct.self)
value3 = try container.decode(TrivialStruct.self)
} catch {
errors.append(error)
}
try DecodingError.verifyAtLeastOneSchemaIsNotNil(
[value1, value2, value3],
type: Self.self,
codingPath: decoder.codingPath
codingPath: decoder.codingPath,
errors: errors
)
}
func encode(to encoder: any Encoder) throws {
Expand Down