diff --git a/.swift-format b/.swift-format index 2afa7389..7efc7847 100644 --- a/.swift-format +++ b/.swift-format @@ -16,7 +16,7 @@ "prioritizeKeepingFunctionOutputTogether" : false, "respectsExistingLineBreaks" : true, "rules" : { - "AllPublicDeclarationsHaveDocumentation" : false, + "AllPublicDeclarationsHaveDocumentation" : true, "AlwaysUseLowerCamelCase" : false, "AmbiguousTrailingClosureOverload" : true, "BeginDocumentationCommentWithOneLineSummary" : false, @@ -50,7 +50,7 @@ "UseSynthesizedInitializer" : false, "UseTripleSlashForDocumentationComments" : true, "UseWhereClausesInForLoops" : false, - "ValidateDocumentationComments" : false + "ValidateDocumentationComments" : true }, "spacesAroundRangeFormationOperators" : false, "tabWidth" : 8, diff --git a/Sources/OpenAPIRuntime/Base/Acceptable.swift b/Sources/OpenAPIRuntime/Base/Acceptable.swift index f4afb72a..217d9f7f 100644 --- a/Sources/OpenAPIRuntime/Base/Acceptable.swift +++ b/Sources/OpenAPIRuntime/Base/Acceptable.swift @@ -35,7 +35,8 @@ public struct QualityValue: Sendable, Hashable { /// Creates a new quality value from the provided floating-point number. /// - /// - Precondition: The value must be between 0.0 and 1.0, inclusive. + /// - Parameter doubleValue: The floating-point number representing the quality value. + /// - Precondition: The `doubleValue` must be between 0.0 and 1.0, inclusive. public init(doubleValue: Double) { precondition( doubleValue >= 0.0 && doubleValue <= 1.0, @@ -51,6 +52,9 @@ public struct QualityValue: Sendable, Hashable { } extension QualityValue: RawRepresentable { + /// Creates a new `QualityValue` instance from a raw string value. + /// + /// - Parameter rawValue: A string representing the quality value. public init?(rawValue: String) { guard let doubleValue = Double(rawValue) else { return nil @@ -58,12 +62,17 @@ extension QualityValue: RawRepresentable { self.init(doubleValue: doubleValue) } + /// The raw string representation of the `QualityValue`. public var rawValue: String { String(format: "%0.3f", doubleValue) } } extension QualityValue: ExpressibleByIntegerLiteral { + /// Creates a new `QualityValue` instance from an integer literal value. + /// + /// - Parameter value: An integer literal value representing the quality value. + /// - Precondition: The `integerLiteral` must be between 0.0 and 1.0, inclusive. public init(integerLiteral value: UInt16) { precondition( value >= 0 && value <= 1, @@ -74,6 +83,9 @@ extension QualityValue: ExpressibleByIntegerLiteral { } extension QualityValue: ExpressibleByFloatLiteral { + /// Creates a new `QualityValue` instance from a floating-point literal value. + /// + /// - Parameter value: A floating-point literal value representing the quality value. public init(floatLiteral value: Double) { self.init(doubleValue: value) } @@ -106,10 +118,10 @@ public struct AcceptHeaderContentType: Sendable public var quality: QualityValue /// Creates a new content type from the provided parameters. + /// /// - Parameters: - /// - value: The value representing the content type. + /// - contentType: The value representing the content type. /// - quality: The quality of the content type, between 0.0 and 1.0. - /// - Precondition: Quality must be in the range 0.0 and 1.0 inclusive. public init(contentType: ContentType, quality: QualityValue = 1.0) { self.quality = quality self.contentType = contentType @@ -123,6 +135,9 @@ public struct AcceptHeaderContentType: Sendable } extension AcceptHeaderContentType: RawRepresentable { + /// Initializes an `AcceptHeaderContentType` instance from its raw string value. + /// + /// - Parameter rawValue: The raw string value representing the content type. public init?(rawValue: String) { guard let validMimeType = OpenAPIMIMEType(rawValue) else { // Invalid MIME type. @@ -145,6 +160,7 @@ extension AcceptHeaderContentType: RawRepresentable { self.init(contentType: typeAndSubtype, quality: quality) } + /// The raw representation of the content negotiation as a MIME type string. public var rawValue: String { contentType.rawValue + (quality.isDefault ? "" : "; q=\(quality.rawValue)") } diff --git a/Sources/OpenAPIRuntime/Base/Base64EncodedData.swift b/Sources/OpenAPIRuntime/Base/Base64EncodedData.swift index 8dbb13cf..3dab18e6 100644 --- a/Sources/OpenAPIRuntime/Base/Base64EncodedData.swift +++ b/Sources/OpenAPIRuntime/Base/Base64EncodedData.swift @@ -62,6 +62,10 @@ public struct Base64EncodedData: Sendable, Hashable { } extension Base64EncodedData: Codable { + /// Initializes a `Base64EncodedData` instance by decoding a base64-encoded string. + /// + /// - Parameter decoder: The decoder from which to decode the base64-encoded string. + /// - Throws: `RuntimeError.invalidBase64String`: If the provided string could not be successfully decoded as base64 data. public init(from decoder: any Decoder) throws { let container = try decoder.singleValueContainer() let base64EncodedString = try container.decode(String.self) @@ -75,6 +79,10 @@ extension Base64EncodedData: Codable { self.init(data: ArraySlice(data)) } + /// Encodes the binary data as a base64-encoded string. + /// + /// - Parameter encoder: The encoder to which the base64-encoded string is written. + /// - Throws: An error if the binary data cannot be successfully encoded as a base64 string. public func encode(to encoder: any Encoder) throws { var container = encoder.singleValueContainer() diff --git a/Sources/OpenAPIRuntime/Base/CopyOnWriteBox.swift b/Sources/OpenAPIRuntime/Base/CopyOnWriteBox.swift new file mode 100644 index 00000000..cbfc8f01 --- /dev/null +++ b/Sources/OpenAPIRuntime/Base/CopyOnWriteBox.swift @@ -0,0 +1,203 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the SwiftOpenAPIGenerator open source project +// +// Copyright (c) 2023 Apple Inc. and the SwiftOpenAPIGenerator project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of SwiftOpenAPIGenerator project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +/// A type that wraps a value and enforces copy-on-write semantics. +/// +/// It also enables recursive types by introducing a "box" into the cycle, which +/// allows the owning type to have a finite size. +@_spi(Generated) +public struct CopyOnWriteBox { + + /// The reference type storage for the box. + @usableFromInline + internal final class Storage { + + /// The stored value. + @usableFromInline + var value: Wrapped + + /// Creates a new storage with the provided initial value. + /// - Parameter value: The initial value to store in the box. + @inlinable + init(value: Wrapped) { + self.value = value + } + } + + /// The internal storage of the box. + @usableFromInline + internal var storage: Storage + + /// Creates a new box. + /// - Parameter value: The value to store in the box. + @inlinable + public init(value: Wrapped) { + self.storage = .init(value: value) + } + + /// The stored value whose accessors enforce copy-on-write semantics. + @inlinable + public var value: Wrapped { + get { + storage.value + } + _modify { + if !isKnownUniquelyReferenced(&storage) { + storage = Storage(value: storage.value) + } + yield &storage.value + } + } +} + +extension CopyOnWriteBox: Encodable where Wrapped: Encodable { + + /// Encodes this value into the given encoder. + /// + /// If the value fails to encode anything, `encoder` will encode an empty + /// keyed container in its place. + /// + /// This function throws an error if any values are invalid for the given + /// encoder's format. + /// + /// - Parameter encoder: The encoder to write data to. + /// - Throws: On an encoding error. + @inlinable + public func encode(to encoder: any Encoder) throws { + try value.encode(to: encoder) + } +} + +extension CopyOnWriteBox: Decodable where Wrapped: Decodable { + + /// Creates a new instance by decoding from the given decoder. + /// + /// This initializer throws an error if reading from the decoder fails, or + /// if the data read is corrupted or otherwise invalid. + /// + /// - Parameter decoder: The decoder to read data from. + /// - Throws: On a decoding error. + @inlinable + public init(from decoder: any Decoder) throws { + let value = try Wrapped(from: decoder) + self.init(value: value) + } +} + +extension CopyOnWriteBox: Equatable where Wrapped: Equatable { + + /// Returns a Boolean value indicating whether two values are equal. + /// + /// Equality is the inverse of inequality. For any values `a` and `b`, + /// `a == b` implies that `a != b` is `false`. + /// + /// - Parameters: + /// - lhs: A value to compare. + /// - rhs: Another value to compare. + /// - Returns: A Boolean value indicating whether the values are equal. + @inlinable + public static func == ( + lhs: CopyOnWriteBox, + rhs: CopyOnWriteBox + ) -> Bool { + lhs.value == rhs.value + } +} + +extension CopyOnWriteBox: Hashable where Wrapped: Hashable { + + /// Hashes the essential components of this value by feeding them into the + /// given hasher. + /// + /// Implement this method to conform to the `Hashable` protocol. The + /// components used for hashing must be the same as the components compared + /// in your type's `==` operator implementation. Call `hasher.combine(_:)` + /// with each of these components. + /// + /// - Important: In your implementation of `hash(into:)`, + /// don't call `finalize()` on the `hasher` instance provided, + /// or replace it with a different instance. + /// Doing so may become a compile-time error in the future. + /// + /// - Parameter hasher: The hasher to use when combining the components + /// of this instance. + @inlinable + public func hash(into hasher: inout Hasher) { + hasher.combine(value) + } +} + +extension CopyOnWriteBox: CustomStringConvertible where Wrapped: CustomStringConvertible { + + /// A textual representation of this instance. + /// + /// Calling this property directly is discouraged. Instead, convert an + /// instance of any type to a string by using the `String(describing:)` + /// initializer. This initializer works with any type, and uses the custom + /// `description` property for types that conform to + /// `CustomStringConvertible`: + /// + /// struct Point: CustomStringConvertible { + /// let x: Int, y: Int + /// + /// var description: String { + /// return "(\(x), \(y))" + /// } + /// } + /// + /// let p = Point(x: 21, y: 30) + /// let s = String(describing: p) + /// print(s) + /// // Prints "(21, 30)" + /// + /// The conversion of `p` to a string in the assignment to `s` uses the + /// `Point` type's `description` property. + @inlinable + public var description: String { + value.description + } +} + +extension CopyOnWriteBox: CustomDebugStringConvertible where Wrapped: CustomDebugStringConvertible { + + /// A textual representation of this instance, suitable for debugging. + /// + /// Calling this property directly is discouraged. Instead, convert an + /// instance of any type to a string by using the `String(reflecting:)` + /// initializer. This initializer works with any type, and uses the custom + /// `debugDescription` property for types that conform to + /// `CustomDebugStringConvertible`: + /// + /// struct Point: CustomDebugStringConvertible { + /// let x: Int, y: Int + /// + /// var debugDescription: String { + /// return "(\(x), \(y))" + /// } + /// } + /// + /// let p = Point(x: 21, y: 30) + /// let s = String(reflecting: p) + /// print(s) + /// // Prints "(21, 30)" + /// + /// The conversion of `p` to a string in the assignment to `s` uses the + /// `Point` type's `debugDescription` property. + @inlinable + public var debugDescription: String { + value.debugDescription + } +} + +extension CopyOnWriteBox: @unchecked Sendable where Wrapped: Sendable {} diff --git a/Sources/OpenAPIRuntime/Base/OpenAPIMIMEType.swift b/Sources/OpenAPIRuntime/Base/OpenAPIMIMEType.swift index cc79ffd0..7766aa2f 100644 --- a/Sources/OpenAPIRuntime/Base/OpenAPIMIMEType.swift +++ b/Sources/OpenAPIRuntime/Base/OpenAPIMIMEType.swift @@ -29,6 +29,13 @@ public struct OpenAPIMIMEType: Equatable { /// A concrete value, spelled as `type/subtype`. case concrete(type: String, subtype: String) + /// Compares two MIME type kinds for equality. + /// + /// - Parameters: + /// - lhs: The left-hand side MIME type kind. + /// - rhs: The right-hand side MIME type kind. + /// + /// - Returns: `true` if the MIME type kinds are equal, otherwise `false`. public static func == (lhs: Kind, rhs: Kind) -> Bool { switch (lhs, rhs) { case (.any, .any): @@ -59,6 +66,13 @@ public struct OpenAPIMIMEType: Equatable { self.parameters = parameters } + /// Compares two MIME types for equality. + /// + /// - Parameters: + /// - lhs: The left-hand side MIME type. + /// - rhs: The right-hand side MIME type. + /// + /// - Returns: `true` if the MIME types are equal, otherwise `false`. public static func == (lhs: OpenAPIMIMEType, rhs: OpenAPIMIMEType) -> Bool { guard lhs.kind == rhs.kind else { return false @@ -85,6 +99,9 @@ public struct OpenAPIMIMEType: Equatable { } extension OpenAPIMIMEType.Kind: LosslessStringConvertible { + /// Initializes a MIME type kind from a string description. + /// + /// - Parameter description: A string description of the MIME type kind. public init?(_ description: String) { let typeAndSubtype = description @@ -106,6 +123,7 @@ extension OpenAPIMIMEType.Kind: LosslessStringConvertible { } } + /// A textual representation of the MIME type kind. public var description: String { switch self { case .any: @@ -119,6 +137,9 @@ extension OpenAPIMIMEType.Kind: LosslessStringConvertible { } extension OpenAPIMIMEType: LosslessStringConvertible { + /// Initializes an `OpenAPIMIMEType` instance based on a string description. + /// + /// - Parameter description: A string description of the MIME. public init?(_ description: String) { var components = description @@ -157,6 +178,7 @@ extension OpenAPIMIMEType: LosslessStringConvertible { ) } + /// A string description of the MIME type. public var description: String { ([kind.description] + parameters diff --git a/Sources/OpenAPIRuntime/Base/OpenAPIValue.swift b/Sources/OpenAPIRuntime/Base/OpenAPIValue.swift index 9266dae0..d7b395b8 100644 --- a/Sources/OpenAPIRuntime/Base/OpenAPIValue.swift +++ b/Sources/OpenAPIRuntime/Base/OpenAPIValue.swift @@ -98,6 +98,10 @@ public struct OpenAPIValueContainer: Codable, Hashable, Sendable { // MARK: Decodable + /// Initializes an `OpenAPIValueContainer` by decoding it from a decoder. + /// + /// - Parameter decoder: The decoder to read data from. + /// - Throws: An error if the decoding process encounters issues or if the data is corrupted. public init(from decoder: any Decoder) throws { let container = try decoder.singleValueContainer() if container.decodeNil() { @@ -124,6 +128,10 @@ public struct OpenAPIValueContainer: Codable, Hashable, Sendable { // MARK: Encodable + /// Encodes the `OpenAPIValueContainer` and writes it to an encoder. + /// + /// - Parameter encoder: The encoder to which the value should be encoded. + /// - Throws: An error if the encoding process encounters issues or if the value is invalid. public func encode(to encoder: any Encoder) throws { var container = encoder.singleValueContainer() guard let value = value else { @@ -153,6 +161,12 @@ public struct OpenAPIValueContainer: Codable, Hashable, Sendable { // MARK: Equatable + /// Compares two `OpenAPIValueContainer` instances for equality. + /// + /// - Parameters: + /// - lhs: The left-hand side `OpenAPIValueContainer` to compare. + /// - rhs: The right-hand side `OpenAPIValueContainer` to compare. + /// - Returns: `true` if the two instances are equal, `false` otherwise. public static func == (lhs: OpenAPIValueContainer, rhs: OpenAPIValueContainer) -> Bool { switch (lhs.value, rhs.value) { case (nil, nil), is (Void, Void): @@ -201,6 +215,9 @@ public struct OpenAPIValueContainer: Codable, Hashable, Sendable { // MARK: Hashable + /// Hashes the `OpenAPIValueContainer` instance into a hasher. + /// + /// - Parameter hasher: The hasher used to compute the hash value. public func hash(into hasher: inout Hasher) { switch value { case let value as Bool: @@ -227,30 +244,45 @@ public struct OpenAPIValueContainer: Codable, Hashable, Sendable { } extension OpenAPIValueContainer: ExpressibleByBooleanLiteral { + /// Creates an `OpenAPIValueContainer` with the provided boolean value. + /// + /// - Parameter value: The boolean value to store in the container. public init(booleanLiteral value: BooleanLiteralType) { self.init(validatedValue: value) } } extension OpenAPIValueContainer: ExpressibleByStringLiteral { + /// Creates an `OpenAPIValueContainer` with the provided string value. + /// + /// - Parameter value: The string value to store in the container. public init(stringLiteral value: String) { self.init(validatedValue: value) } } extension OpenAPIValueContainer: ExpressibleByNilLiteral { + /// Creates an `OpenAPIValueContainer` with a `nil` value. + /// + /// - Parameter nilLiteral: The `nil` literal. public init(nilLiteral: ()) { self.init(validatedValue: nil) } } extension OpenAPIValueContainer: ExpressibleByIntegerLiteral { + /// Creates an `OpenAPIValueContainer` with the provided integer value. + /// + /// - Parameter value: The integer value to store in the container. public init(integerLiteral value: Int) { self.init(validatedValue: value) } } extension OpenAPIValueContainer: ExpressibleByFloatLiteral { + /// Creates an `OpenAPIValueContainer` with the provided floating-point value. + /// + /// - Parameter value: The floating-point value to store in the container. public init(floatLiteral value: Double) { self.init(validatedValue: value) } @@ -317,6 +349,10 @@ public struct OpenAPIObjectContainer: Codable, Hashable, Sendable { // MARK: Decodable + /// Creates an `OpenAPIValueContainer` by decoding it from a single-value container in a given decoder. + /// + /// - Parameter decoder: The decoder used to decode the container. + /// - Throws: An error if the decoding process encounters an issue or if the data does not match the expected format. public init(from decoder: any Decoder) throws { let container = try decoder.singleValueContainer() let item = try container.decode([String: OpenAPIValueContainer].self) @@ -325,6 +361,10 @@ public struct OpenAPIObjectContainer: Codable, Hashable, Sendable { // MARK: Encodable + /// Encodes the `OpenAPIValueContainer` into a format that can be stored or transmitted via the given encoder. + /// + /// - Parameter encoder: The encoder used to perform the encoding. + /// - Throws: An error if the encoding process encounters an issue or if the data does not match the expected format. public func encode(to encoder: any Encoder) throws { var container = encoder.singleValueContainer() try container.encode(value.mapValues(OpenAPIValueContainer.init(validatedValue:))) @@ -332,6 +372,13 @@ public struct OpenAPIObjectContainer: Codable, Hashable, Sendable { // MARK: Equatable + /// Compares two `OpenAPIObjectContainer` instances for equality by comparing their inner key-value dictionaries. + /// + /// - Parameters: + /// - lhs: The left-hand side `OpenAPIObjectContainer` to compare. + /// - rhs: The right-hand side `OpenAPIObjectContainer` to compare. + /// + /// - Returns: `true` if the `OpenAPIObjectContainer` instances are equal, `false` otherwise. public static func == (lhs: OpenAPIObjectContainer, rhs: OpenAPIObjectContainer) -> Bool { let lv = lhs.value let rv = rhs.value @@ -352,6 +399,9 @@ public struct OpenAPIObjectContainer: Codable, Hashable, Sendable { // MARK: Hashable + /// Hashes the `OpenAPIObjectContainer` instance into the provided `Hasher`. + /// + /// - Parameter hasher: The `Hasher` into which the hash value is combined. public func hash(into hasher: inout Hasher) { for (key, itemValue) in value { hasher.combine(key) @@ -414,12 +464,17 @@ public struct OpenAPIArrayContainer: Codable, Hashable, Sendable { /// Returns the specified value cast to an array of supported values. /// - Parameter value: An array with untyped values. /// - Returns: A cast value if values are supported, nil otherwise. + /// - Throws: An error if casting to supported values fails for any element. static func tryCast(_ value: [(any Sendable)?]) throws -> [(any Sendable)?] { return try value.map(OpenAPIValueContainer.tryCast(_:)) } // MARK: Decodable + /// Initializes a new instance by decoding a validated array of values from a decoder. + /// + /// - Parameter decoder: The decoder to use for decoding the array of values. + /// - Throws: An error if the decoding process fails or if the decoded values cannot be validated. public init(from decoder: any Decoder) throws { let container = try decoder.singleValueContainer() let item = try container.decode([OpenAPIValueContainer].self) @@ -428,6 +483,10 @@ public struct OpenAPIArrayContainer: Codable, Hashable, Sendable { // MARK: Encodable + /// Encodes the array of validated values and stores the result in the given encoder. + /// + /// - Parameter encoder: The encoder to use for encoding the array of values. + /// - Throws: An error if the encoding process fails. public func encode(to encoder: any Encoder) throws { var container = encoder.singleValueContainer() try container.encode(value.map(OpenAPIValueContainer.init(validatedValue:))) @@ -435,6 +494,12 @@ public struct OpenAPIArrayContainer: Codable, Hashable, Sendable { // MARK: Equatable + /// Compares two `OpenAPIArrayContainer` instances for equality. + /// + /// - Parameters: + /// - lhs: The left-hand side `OpenAPIArrayContainer` to compare. + /// - rhs: The right-hand side `OpenAPIArrayContainer` to compare. + /// - Returns: `true` if the two `OpenAPIArrayContainer` instances are equal, `false` otherwise. public static func == (lhs: OpenAPIArrayContainer, rhs: OpenAPIArrayContainer) -> Bool { let lv = lhs.value let rv = rhs.value @@ -449,6 +514,9 @@ public struct OpenAPIArrayContainer: Codable, Hashable, Sendable { // MARK: Hashable + /// Hashes the `OpenAPIArrayContainer` instance into a hasher. + /// + /// - Parameter hasher: The hasher used to compute the hash value. public func hash(into hasher: inout Hasher) { for item in value { hasher.combine(OpenAPIValueContainer(validatedValue: item)) diff --git a/Sources/OpenAPIRuntime/Base/WarningSuppressingAnnotations.swift b/Sources/OpenAPIRuntime/Base/WarningSuppressingAnnotations.swift index 1dfbce7b..d40d07d2 100644 --- a/Sources/OpenAPIRuntime/Base/WarningSuppressingAnnotations.swift +++ b/Sources/OpenAPIRuntime/Base/WarningSuppressingAnnotations.swift @@ -23,6 +23,7 @@ /// /// There should be no runtime impact in release builds, as the function is inlined and /// has no executable code. +/// - Parameter value: The value for which you want to suppress "variable was never mutated, change to let" warnings. @_spi(Generated) @inline(__always) public func suppressMutabilityWarning(_ value: inout T) {} @@ -38,6 +39,7 @@ public func suppressMutabilityWarning(_ value: inout T) {} /// /// There should be no runtime impact in release builds, as the function is inlined and /// has no executable code. +/// - Parameter value: The value for which you want to suppress "variable unused" warnings. @_spi(Generated) @inline(__always) public func suppressUnusedWarning(_ value: T) {} diff --git a/Sources/OpenAPIRuntime/Conversion/CodableExtensions.swift b/Sources/OpenAPIRuntime/Conversion/CodableExtensions.swift index 13a78a48..ef35e83a 100644 --- a/Sources/OpenAPIRuntime/Conversion/CodableExtensions.swift +++ b/Sources/OpenAPIRuntime/Conversion/CodableExtensions.swift @@ -20,8 +20,7 @@ extension Decoder { /// Validates that no undocumented keys are present. /// /// - Throws: When at least one undocumented key is found. - /// - Parameters: - /// - knownKeys: A set of known and already decoded keys. + /// - Parameter knownKeys: A set of known and already decoded keys. public func ensureNoAdditionalProperties(knownKeys: Set) throws { let (unknownKeys, container) = try unknownKeysAndContainer( knownKeys: knownKeys @@ -41,9 +40,9 @@ extension Decoder { /// /// The included properties are those still present in the decoder but /// not already decoded and passed in as known keys. - /// - Parameters: - /// - knownKeys: Known and already decoded keys. + /// - Parameter knownKeys: Known and already decoded keys. /// - Returns: A container with the decoded undocumented properties. + /// - Throws: An error if decoding additional properties fails. public func decodeAdditionalProperties( knownKeys: Set ) throws -> OpenAPIObjectContainer { @@ -72,9 +71,9 @@ extension Decoder { /// /// The included properties are those still present in the decoder but /// not already decoded and passed in as known keys. - /// - Parameters: - /// - knownKeys: Known and already decoded keys. + /// - Parameter knownKeys: Known and already decoded keys. /// - Returns: A container with the decoded undocumented properties. + /// - Throws: An error if there are issues with decoding the additional properties. public func decodeAdditionalProperties( knownKeys: Set ) throws -> [String: T] { @@ -93,6 +92,7 @@ extension Decoder { /// Returns the decoded value by using a single value container. /// - Parameter type: The type to decode. /// - Returns: The decoded value. + /// - Throws: An error if there are issues with decoding the value from the single value container. public func decodeFromSingleValueContainer( _ type: T.Type = T.self ) throws -> T { @@ -106,6 +106,11 @@ extension Decoder { /// in the `knownKeys` set. /// /// This is used to implement the `additionalProperties` feature. + /// - Parameter knownKeys: A set of known keys that have already been decoded. + /// - Returns: A tuple containing two values: a set of unknown keys and a keyed decoding container + /// for further decoding of the unknown properties. + /// - Throws: An error if there are issues with creating the decoding container or identifying + /// the unknown keys. private func unknownKeysAndContainer( knownKeys: Set ) throws -> (Set, KeyedDecodingContainer) { @@ -122,8 +127,8 @@ extension Encoder { /// /// The properties are encoded directly into the encoder, rather that /// into a nested container. - /// - Parameters: - /// - additionalProperties: A container of additional properties. + /// - Parameter additionalProperties: A container of additional properties. + /// - Throws: An error if there are issues with encoding the additional properties. public func encodeAdditionalProperties( _ additionalProperties: OpenAPIObjectContainer ) throws { @@ -143,8 +148,8 @@ extension Encoder { /// /// The properties are encoded directly into the encoder, rather that /// into a nested container. - /// - Parameters: - /// - additionalProperties: A container of additional properties. + /// - Parameter additionalProperties: A container of additional properties. + /// - Throws: An error if there are issues with encoding the additional properties. public func encodeAdditionalProperties( _ additionalProperties: [String: T] ) throws { @@ -159,6 +164,7 @@ extension Encoder { /// Encodes the value into the encoder using a single value container. /// - Parameter value: The value to encode. + /// - Throws: An error if there are issues with encoding the value. public func encodeToSingleValueContainer( _ value: T ) throws { @@ -169,6 +175,7 @@ extension Encoder { /// Encodes the first non-nil value from the provided array into /// the encoder using a single value container. /// - Parameter values: An array of optional values. + /// - Throws: An error if there are issues with encoding the value. public func encodeFirstNonNilValueToSingleValueContainer( _ values: [(any Encodable)?] ) throws { diff --git a/Sources/OpenAPIRuntime/Conversion/Configuration.swift b/Sources/OpenAPIRuntime/Conversion/Configuration.swift index 45cdcf99..439b424b 100644 --- a/Sources/OpenAPIRuntime/Conversion/Configuration.swift +++ b/Sources/OpenAPIRuntime/Conversion/Configuration.swift @@ -83,8 +83,7 @@ public struct Configuration: Sendable { /// Creates a new configuration with the specified values. /// - /// - Parameters: - /// - dateTranscoder: The transcoder to use when converting between date + /// - Parameter dateTranscoder: The transcoder to use when converting between date /// and string values. public init( dateTranscoder: any DateTranscoder = .iso8601 diff --git a/Sources/OpenAPIRuntime/Conversion/Converter+Client.swift b/Sources/OpenAPIRuntime/Conversion/Converter+Client.swift index 40fd3e86..4c6950b2 100644 --- a/Sources/OpenAPIRuntime/Conversion/Converter+Client.swift +++ b/Sources/OpenAPIRuntime/Conversion/Converter+Client.swift @@ -27,7 +27,15 @@ extension Converter { headerFields[.accept] = contentTypes.map(\.rawValue).joined(separator: ", ") } - // | client | set | request path | URI | required | renderedPath | + /// Renders the path template with the specified parameters to construct a URI. + /// + /// - Parameters: + /// - template: The URI path template with placeholders for parameters. + /// - parameters: An array of encodable parameters used to populate the placeholders. + /// + /// - Returns: A URI path string with placeholders replaced by the provided parameters. + /// + /// - Throws: An error if rendering the path fails. public func renderedPath( template: String, parameters: [any Encodable] @@ -54,7 +62,16 @@ extension Converter { return renderedString } - // | client | set | request query | URI | both | setQueryItemAsURI | + /// Sets a query item with the specified name and value in the HTTP request's query parameters, treating the value as a URI component. + /// + /// - Parameters: + /// - request: The HTTP request to which the query item is added. + /// - style: The parameter style to apply when encoding the value. + /// - explode: A Boolean indicating whether to explode values. + /// - name: The name of the query item. + /// - value: The value to be treated as a URI component. + /// + /// - Throws: An error of if setting the query item as a URI component fails. public func setQueryItemAsURI( in request: inout HTTPRequest, style: ParameterStyle?, @@ -80,7 +97,16 @@ extension Converter { ) } - // | client | set | request body | JSON | optional | setOptionalRequestBodyAsJSON | + /// Sets an optional request body as JSON in the specified header fields and returns an `HTTPBody`. + /// + /// - Parameters: + /// - value: The optional value to be set as the request body. + /// - headerFields: The header fields in which to set the content type. + /// - contentType: The content type to be set in the header fields. + /// + /// - Returns: An `HTTPBody` representing the JSON-encoded request body, or `nil` if the `value` is `nil`. + /// + /// - Throws: An error if setting the request body as JSON fails. public func setOptionalRequestBodyAsJSON( _ value: T?, headerFields: inout HTTPFields, @@ -94,7 +120,16 @@ extension Converter { ) } - // | client | set | request body | JSON | required | setRequiredRequestBodyAsJSON | + /// Sets a required request body as JSON in the specified header fields and returns an `HTTPBody`. + /// + /// - Parameters: + /// - value: The value to be set as the request body. + /// - headerFields: The header fields in which to set the content type. + /// - contentType: The content type to be set in the header fields. + /// + /// - Returns: An `HTTPBody` representing the JSON-encoded request body. + /// + /// - Throws: An error if setting the request body as JSON fails. public func setRequiredRequestBodyAsJSON( _ value: T, headerFields: inout HTTPFields, @@ -108,7 +143,16 @@ extension Converter { ) } - // | client | set | request body | binary | optional | setOptionalRequestBodyAsBinary | + /// Sets an optional request body as binary in the specified header fields and returns an `HTTPBody`. + /// + /// - Parameters: + /// - value: The optional `HTTPBody` to be set as the request body. + /// - headerFields: The header fields in which to set the content type. + /// - contentType: The content type to be set in the header fields. + /// + /// - Returns: An `HTTPBody` representing the binary request body, or `nil` if the `value` is `nil`. + /// + /// - Throws: An error if setting the request body as binary fails. public func setOptionalRequestBodyAsBinary( _ value: HTTPBody?, headerFields: inout HTTPFields, @@ -122,7 +166,16 @@ extension Converter { ) } - // | client | set | request body | binary | required | setRequiredRequestBodyAsBinary | + /// Sets a required request body as binary in the specified header fields and returns an `HTTPBody`. + /// + /// - Parameters: + /// - value: The `HTTPBody` to be set as the request body. + /// - headerFields: The header fields in which to set the content type. + /// - contentType: The content type to be set in the header fields. + /// + /// - Returns: An `HTTPBody` representing the binary request body. + /// + /// - Throws: An error if setting the request body as binary fails. public func setRequiredRequestBodyAsBinary( _ value: HTTPBody, headerFields: inout HTTPFields, @@ -136,7 +189,16 @@ extension Converter { ) } - // | client | set | request body | urlEncodedForm | codable | optional | setOptionalRequestBodyAsURLEncodedForm | + /// Sets an optional request body as URL-encoded form data in the specified header fields and returns an `HTTPBody`. + /// + /// - Parameters: + /// - value: The optional value to be set as the request body. + /// - headerFields: The header fields in which to set the content type. + /// - contentType: The content type to be set in the header fields. + /// + /// - Returns: An `HTTPBody` representing the URL-encoded form data request body, or `nil` if the `value` is `nil`. + /// + /// - Throws: An error if setting the request body as URL-encoded form data fails. public func setOptionalRequestBodyAsURLEncodedForm( _ value: T, headerFields: inout HTTPFields, @@ -150,7 +212,16 @@ extension Converter { ) } - // | client | set | request body | urlEncodedForm | codable | required | setRequiredRequestBodyAsURLEncodedForm | + /// Sets a required request body as URL-encoded form data in the specified header fields and returns an `HTTPBody`. + /// + /// - Parameters: + /// - value: The value to be set as the request body. + /// - headerFields: The header fields in which to set the content type. + /// - contentType: The content type to be set in the header fields. + /// + /// - Returns: An `HTTPBody` representing the URL-encoded form data request body. + /// + /// - Throws: An error if setting the request body as URL-encoded form data fails. public func setRequiredRequestBodyAsURLEncodedForm( _ value: T, headerFields: inout HTTPFields, @@ -164,7 +235,16 @@ extension Converter { ) } - // | client | get | response body | JSON | required | getResponseBodyAsJSON | + /// Retrieves the response body as JSON and transforms it into a specified type. + /// + /// - Parameters: + /// - type: The type to decode the JSON into. + /// - data: The HTTP body data containing the JSON. + /// - transform: A transformation function to apply to the decoded JSON. + /// + /// - Returns: The transformed result of type `C`. + /// + /// - Throws: An error if retrieving or transforming the response body fails. public func getResponseBodyAsJSON( _ type: T.Type, from data: HTTPBody?, @@ -181,7 +261,16 @@ extension Converter { ) } - // | client | get | response body | binary | required | getResponseBodyAsBinary | + /// Retrieves the response body as binary data and transforms it into a specified type. + /// + /// - Parameters: + /// - type: The type representing the response body. + /// - data: The HTTP body data to transform. + /// - transform: A transformation function to apply to the binary data. + /// + /// - Returns: The transformed result of type `C`. + /// + /// - Throws: An error if retrieving or transforming the response body fails. public func getResponseBodyAsBinary( _ type: HTTPBody.Type, from data: HTTPBody?, diff --git a/Sources/OpenAPIRuntime/Conversion/Converter+Common.swift b/Sources/OpenAPIRuntime/Conversion/Converter+Common.swift index fc9235a4..1123d1ff 100644 --- a/Sources/OpenAPIRuntime/Conversion/Converter+Common.swift +++ b/Sources/OpenAPIRuntime/Conversion/Converter+Common.swift @@ -65,13 +65,20 @@ extension Converter { /// Returns an error to be thrown when an unexpected content type is /// received. /// - Parameter contentType: The content type that was received. + /// - Returns: An error representing an unexpected content type. public func makeUnexpectedContentTypeError(contentType: OpenAPIMIMEType?) -> any Error { RuntimeError.unexpectedContentTypeHeader(contentType?.description ?? "") } // MARK: - Converter helper methods - // | common | set | header field | URI | both | setHeaderFieldAsURI | + /// Sets a header field with an optional value, encoding it as a URI component if not nil. + /// + /// - Parameters: + /// - headerFields: The HTTP header fields dictionary where the field will be set. + /// - name: The name of the header field. + /// - value: The optional value to be encoded as a URI component if not nil. + /// - Throws: An error if there's an issue with encoding the value as a URI component. public func setHeaderFieldAsURI( in headerFields: inout HTTPFields, name: String, @@ -96,7 +103,13 @@ extension Converter { ) } - // | common | set | header field | JSON | both | setHeaderFieldAsJSON | + /// Sets a header field with an optional value, encoding it as a JSON component if not nil. + /// + /// - Parameters: + /// - headerFields: The HTTP header fields dictionary where the field will be set. + /// - name: The name of the header field. + /// - value: The optional value to be encoded as a JSON component if not nil. + /// - Throws: An error if there's an issue with encoding the value as a JSON component. public func setHeaderFieldAsJSON( in headerFields: inout HTTPFields, name: String, @@ -110,7 +123,15 @@ extension Converter { ) } - // | common | get | header field | URI | optional | getOptionalHeaderFieldAsURI | + /// Attempts to retrieve an optional header field value and decodes it as a URI component, returning it as the specified type. + /// + /// - Parameters: + /// - headerFields: The HTTP header fields dictionary where the field is expected. + /// - name: The name of the header field to retrieve. + /// - type: The expected type of the decoded value. + /// - Returns: The decoded header field value as the specified type, or `nil` if the field is not present. + /// - Throws: An error if there's an issue with decoding the URI component or + /// if the field is present but cannot be decoded as the specified type. public func getOptionalHeaderFieldAsURI( in headerFields: HTTPFields, name: String, @@ -132,7 +153,15 @@ extension Converter { ) } - // | common | get | header field | URI | required | getRequiredHeaderFieldAsURI | + /// Attempts to retrieve a required header field value and decodes it as a URI component, returning it as the specified type. + /// + /// - Parameters: + /// - headerFields: The HTTP header fields dictionary where the field is expected. + /// - name: The name of the header field to retrieve. + /// - type: The expected type of the decoded value. + /// - Returns: The decoded header field value as the specified type. + /// - Throws: An error if the field is not present or if there's an issue with decoding the URI component or + /// if the field is present but cannot be decoded as the specified type. public func getRequiredHeaderFieldAsURI( in headerFields: HTTPFields, name: String, @@ -154,7 +183,15 @@ extension Converter { ) } - // | common | get | header field | JSON | optional | getOptionalHeaderFieldAsJSON | + /// Attempts to retrieve an optional header field value and decodes it as JSON, returning it as the specified type. + /// + /// - Parameters: + /// - headerFields: The HTTP header fields dictionary where the field is expected. + /// - name: The name of the header field to retrieve. + /// - type: The expected type of the decoded value. + /// - Returns: The decoded header field value as the specified type, or + /// `nil` if the field is not present in the headerFields dictionary. + /// - Throws: An error if there's an issue with decoding the JSON value or if the field is present but cannot be decoded as the specified type. public func getOptionalHeaderFieldAsJSON( in headerFields: HTTPFields, name: String, @@ -168,7 +205,15 @@ extension Converter { ) } - // | common | get | header field | JSON | required | getRequiredHeaderFieldAsJSON | + /// Retrieves a required header field value and decodes it as JSON, returning it as the specified type. + /// + /// - Parameters: + /// - headerFields: The HTTP header fields dictionary where the field is expected. + /// - name: The name of the header field to retrieve. + /// - type: The expected type of the decoded value. + /// - Returns: The decoded header field value as the specified type. + /// - Throws: An error if the field is not present in the headerFields dictionary, if there's an issue with decoding the JSON value, + /// or if the field cannot be decoded as the specified type. public func getRequiredHeaderFieldAsJSON( in headerFields: HTTPFields, name: String, diff --git a/Sources/OpenAPIRuntime/Conversion/Converter+Server.swift b/Sources/OpenAPIRuntime/Conversion/Converter+Server.swift index b8ff2aa1..ee84c71e 100644 --- a/Sources/OpenAPIRuntime/Conversion/Converter+Server.swift +++ b/Sources/OpenAPIRuntime/Conversion/Converter+Server.swift @@ -23,6 +23,7 @@ extension Converter { /// header. /// - Returns: The parsed content types, or the default content types if /// the header was not provided. + /// - Throws: An error if the "accept" header is present but malformed, or if there are issues parsing its components. public func extractAcceptHeaderIfPresent( in headerFields: HTTPFields ) throws -> [AcceptHeaderContentType] { @@ -49,6 +50,8 @@ extension Converter { /// - substring: Expected content type, for example "application/json". /// - headerFields: Header fields in which to look for "Accept". /// Also supports wildcars, such as "application/\*" and "\*/\*". + /// - Throws: An error if the "Accept" header is present but incompatible with the provided content type, + /// or if there are issues parsing the header. public func validateAcceptIfPresent( _ substring: String, in headerFields: HTTPFields @@ -85,7 +88,14 @@ extension Converter { throw RuntimeError.unexpectedAcceptHeader(acceptHeader) } - // | server | get | request path | URI | required | getPathParameterAsURI | + /// Retrieves and decodes a path parameter as a URI-encoded value of the specified type. + /// + /// - Parameters: + /// - pathParameters: A dictionary of path parameters, where the keys are parameter names, and the values are substrings. + /// - name: The name of the path parameter to retrieve. + /// - type: The type to decode the parameter value into. + /// - Returns: The decoded value of the specified type. + /// - Throws: An error if the specified path parameter is not found or if there are issues decoding the value. public func getPathParameterAsURI( in pathParameters: [String: Substring], name: String, @@ -114,7 +124,16 @@ extension Converter { ) } - // | server | get | request query | URI | optional | getOptionalQueryItemAsURI | + /// Retrieves and decodes an optional query item as a URI-encoded value of the specified type. + /// + /// - Parameters: + /// - query: The query item to decode as a substring, or `nil` if the query item is not present. + /// - style: The parameter style. + /// - explode: An explode value. + /// - name: The name of the query parameter to retrieve. + /// - type: The type to decode the parameter value into. + /// - Returns: The decoded value of the specified type, or `nil` if the query item is not present. + /// - Throws: An error if there are issues decoding the value. public func getOptionalQueryItemAsURI( in query: Substring?, style: ParameterStyle?, @@ -147,7 +166,16 @@ extension Converter { ) } - // | server | get | request query | URI | required | getRequiredQueryItemAsURI | + /// Retrieves and decodes a required query item as a URI-encoded value of the specified type. + /// + /// - Parameters: + /// - query: The query item to decode as a substring, or `nil` if the query item is not present. + /// - style: The parameter style. + /// - explode: An explode value + /// - name: The name of the query parameter to retrieve. + /// - type: The type to decode the parameter value into. + /// - Returns: The decoded value of the specified type. + /// - Throws: An error if the query item is not present or if there are issues decoding the value. public func getRequiredQueryItemAsURI( in query: Substring?, style: ParameterStyle?, @@ -180,7 +208,14 @@ extension Converter { ) } - // | server | get | request body | JSON | optional | getOptionalRequestBodyAsJSON | + /// Retrieves and decodes an optional JSON-encoded request body and transforms it to a different type. + /// + /// - Parameters: + /// - type: The type to decode the request body into. + /// - data: The HTTP request body to decode, or `nil` if the body is not present. + /// - transform: A closure that transforms the decoded value to a different type. + /// - Returns: The transformed value, or `nil` if the request body is not present or if decoding fails. + /// - Throws: An error if there are issues decoding or transforming the request body. public func getOptionalRequestBodyAsJSON( _ type: T.Type, from data: HTTPBody?, @@ -194,7 +229,14 @@ extension Converter { ) } - // | server | get | request body | JSON | required | getRequiredRequestBodyAsJSON | + /// Retrieves and decodes a required JSON-encoded request body and transforms it to a different type. + /// + /// - Parameters: + /// - type: The type to decode the request body into. + /// - data: The HTTP request body to decode, or `nil` if the body is not present. + /// - transform: A closure that transforms the decoded value to a different type. + /// - Returns: The transformed value. + /// - Throws: An error if the request body is not present, if decoding fails, or if there are issues transforming the request body. public func getRequiredRequestBodyAsJSON( _ type: T.Type, from data: HTTPBody?, @@ -208,7 +250,14 @@ extension Converter { ) } - // | server | get | request body | binary | optional | getOptionalRequestBodyAsBinary | + /// Retrieves and transforms an optional binary request body. + /// + /// - Parameters: + /// - type: The type representing an HTTP request body (usually `HTTPBody.Type`). + /// - data: The HTTP request body to transform, or `nil` if the body is not present. + /// - transform: A closure that transforms the binary request body to a different type. + /// - Returns: The transformed value, or `nil` if the request body is not present. + /// - Throws: An error if there are issues transforming the request body. public func getOptionalRequestBodyAsBinary( _ type: HTTPBody.Type, from data: HTTPBody?, @@ -222,7 +271,14 @@ extension Converter { ) } - // | server | get | request body | binary | required | getRequiredRequestBodyAsBinary | + /// Retrieves and transforms a required binary request body. + /// + /// - Parameters: + /// - type: The type representing an HTTP request body (usually `HTTPBody.Type`). + /// - data: The HTTP request body to transform, or `nil` if the body is not present. + /// - transform: A closure that transforms the binary request body to a different type. + /// - Returns: The transformed value. + /// - Throws: An error if the request body is not present or if there are issues transforming the request body. public func getRequiredRequestBodyAsBinary( _ type: HTTPBody.Type, from data: HTTPBody?, @@ -236,7 +292,14 @@ extension Converter { ) } - // | server | get | request body | URLEncodedForm | codable | optional | getOptionalRequestBodyAsURLEncodedForm | + /// Retrieves and transforms an optional URL-encoded form request body. + /// + /// - Parameters: + /// - type: The type representing the expected structure of the URL-encoded form data. + /// - data: The HTTP request body to transform, or `nil` if the body is not present. + /// - transform: A closure that transforms the URL-encoded form request body to a different type. + /// - Returns: The transformed value, or `nil` if the request body is not present. + /// - Throws: An error if there are issues transforming the request body. public func getOptionalRequestBodyAsURLEncodedForm( _ type: T.Type, from data: HTTPBody?, @@ -250,7 +313,14 @@ extension Converter { ) } - // | server | get | request body | URLEncodedForm | codable | required | getRequiredRequestBodyAsURLEncodedForm | + /// Retrieves and decodes the required request body as URL-encoded form data. + /// + /// - Parameters: + /// - type: The type to decode the request body into. + /// - data: The HTTP body containing the URL-encoded form data. + /// - transform: A closure to further transform the decoded value. + /// - Returns: The transformed, decoded value of type `C`. + /// - Throws: An error if the decoding or transformation fails. public func getRequiredRequestBodyAsURLEncodedForm( _ type: T.Type, from data: HTTPBody?, @@ -264,7 +334,14 @@ extension Converter { ) } - // | server | set | response body | JSON | required | setResponseBodyAsJSON | + /// Sets the response body as JSON data, serializing the provided value. + /// + /// - Parameters: + /// - value: The value to be serialized into the response body. + /// - headerFields: The HTTP header fields to update with the new `contentType`. + /// - contentType: The content type to set in the HTTP header fields. + /// - Returns: An `HTTPBody` with the response body set as JSON data. + /// - Throws: An error if serialization or setting the response body fails. public func setResponseBodyAsJSON( _ value: T, headerFields: inout HTTPFields, @@ -278,7 +355,14 @@ extension Converter { ) } - // | server | set | response body | binary | required | setResponseBodyAsBinary | + /// Sets the response body as binary data. + /// + /// - Parameters: + /// - value: The binary data to set as the response body. + /// - headerFields: A reference to the header fields to update with the content type. + /// - contentType: The content type to set in the header fields. + /// - Returns: The updated `HTTPBody` containing the binary response data. + /// - Throws: An error if there are issues setting the response body or updating the header fields. public func setResponseBodyAsBinary( _ value: HTTPBody, headerFields: inout HTTPFields, diff --git a/Sources/OpenAPIRuntime/Conversion/CurrencyExtensions.swift b/Sources/OpenAPIRuntime/Conversion/CurrencyExtensions.swift index b6c92d1e..3b7a7d41 100644 --- a/Sources/OpenAPIRuntime/Conversion/CurrencyExtensions.swift +++ b/Sources/OpenAPIRuntime/Conversion/CurrencyExtensions.swift @@ -104,6 +104,7 @@ extension Converter { /// - key: The key to be encoded with the value. /// - value: The value to be encoded. /// - Returns: A URI encoded string. + /// - Throws: An error if encoding fails. func convertToURI( style: ParameterStyle, explode: Bool, @@ -132,6 +133,7 @@ extension Converter { /// - key: The key for which the value was decoded. /// - encodedValue: The encoded value to be decoded. /// - Returns: A decoded value. + /// - Throws: An error if decoding fails. func convertFromURI( style: ParameterStyle, explode: Bool, @@ -157,6 +159,7 @@ extension Converter { /// Returns a value decoded from a JSON body. /// - Parameter body: The body containing the raw JSON bytes. /// - Returns: A decoded value. + /// - Throws: An error if decoding from the body fails. func convertJSONToBodyCodable( _ body: HTTPBody ) async throws -> T { @@ -167,6 +170,7 @@ extension Converter { /// Returns a JSON body for the provided encodable value. /// - Parameter value: The value to encode as JSON. /// - Returns: The raw JSON body. + /// - Throws: An error if encoding to JSON fails. func convertBodyCodableToJSON( _ value: T ) throws -> HTTPBody { @@ -177,6 +181,7 @@ extension Converter { /// Returns a value decoded from a URL-encoded form body. /// - Parameter body: The body containing the raw URL-encoded form bytes. /// - Returns: A decoded value. + /// - Throws: An error if decoding from the URL-encoded form fails. func convertURLEncodedFormToCodable( _ body: HTTPBody ) async throws -> T { @@ -196,6 +201,7 @@ extension Converter { /// Returns a URL-encoded form string for the provided encodable value. /// - Parameter value: The value to encode. /// - Returns: The raw URL-encoded form body. + /// - Throws: An error if encoding to URL-encoded form fails. func convertBodyCodableToURLFormData( _ value: T ) throws -> HTTPBody { @@ -214,6 +220,7 @@ extension Converter { /// Returns a JSON string for the provided encodable value. /// - Parameter value: The value to encode. /// - Returns: A JSON string. + /// - Throws: An error if encoding the value to JSON fails. func convertHeaderFieldCodableToJSON( _ value: T ) throws -> String { @@ -225,6 +232,7 @@ extension Converter { /// Returns a value decoded from the provided JSON string. /// - Parameter stringValue: A JSON string. /// - Returns: The decoded value. + /// - Throws: An error if decoding from the JSON string fails. func convertJSONToHeaderFieldCodable( _ stringValue: Substring ) throws -> T { @@ -240,6 +248,7 @@ extension Converter { /// - name: The name of the header to set. /// - value: The value of the header to set. /// - convert: The closure used to serialize the header value to string. + /// - Throws: An error if an issue occurs while serializing the header value. func setHeaderField( in headerFields: inout HTTPFields, name: String, @@ -263,6 +272,7 @@ extension Converter { /// - headerFields: The header field storage. /// - name: The name of the header field. /// - Returns: The value of the header field, if found. Nil otherwise. + /// - Throws: An error if an issue occurs while retrieving the header value. func getHeaderFieldValuesString( in headerFields: HTTPFields, name: String @@ -277,6 +287,7 @@ extension Converter { /// - type: The type to decode the value as. /// - convert: The closure to convert the value from string. /// - Returns: The decoded value, if found. Nil otherwise. + /// - Throws: An error if an issue occurs while decoding or converting the header value. func getOptionalHeaderField( in headerFields: HTTPFields, name: String, @@ -301,6 +312,8 @@ extension Converter { /// - type: The type to decode the value as. /// - convert: The closure to convert the value from string. /// - Returns: The decoded value. + /// - Throws: An error if the required header field is missing or + /// if an issue occurs while decoding or converting the header value. func getRequiredHeaderField( in headerFields: HTTPFields, name: String, @@ -327,6 +340,7 @@ extension Converter { /// - value: The value of the query parameter. Must already be /// percent-escaped. /// - convert: The closure that converts the provided value to string. + /// - Throws: An error if an issue occurs while setting the query parameter, such as invalid input values or encoding errors. func setEscapedQueryItem( in request: inout HTTPRequest, style: ParameterStyle?, @@ -380,6 +394,7 @@ extension Converter { /// - type: The type to decode the string value as. /// - convert: The closure that decodes the value from string. /// - Returns: A decoded value, if found. Nil otherwise. + /// - Throws: An error if an issue occurs while decoding the query parameter, such as invalid input values or decoding errors. func getOptionalQueryItem( in query: Substring?, style: ParameterStyle?, @@ -409,6 +424,7 @@ extension Converter { /// - type: The type to decode the string value as. /// - convert: The closure that decodes the value from string. /// - Returns: A decoded value. + /// - Throws: An error if an issue occurs while decoding the query parameter, such as invalid input values or decoding errors. func getRequiredQueryItem( in query: Substring?, style: ParameterStyle?, @@ -440,6 +456,7 @@ extension Converter { /// - contentType: The content type value. /// - convert: The closure that encodes the value into a raw body. /// - Returns: The body. + /// - Throws: An error if an issue occurs while encoding the request body or setting the content type. func setRequiredRequestBody( _ value: T, headerFields: inout HTTPFields, @@ -458,6 +475,7 @@ extension Converter { /// - contentType: The content type value. /// - convert: The closure that encodes the value into a raw body. /// - Returns: The body, if value was not nil. + /// - Throws: An error if an issue occurs while encoding the request body or setting the content type. func setOptionalRequestBody( _ value: T?, headerFields: inout HTTPFields, @@ -482,6 +500,7 @@ extension Converter { /// - transform: The closure that wraps the body in its generated type. /// - convert: The closure that decodes the body. /// - Returns: A decoded wrapped type, if body is not nil. + /// - Throws: An error if an issue occurs while decoding the request body. func getOptionalBufferingRequestBody( _ type: T.Type, from body: HTTPBody?, @@ -502,6 +521,7 @@ extension Converter { /// - transform: The closure that wraps the body in its generated type. /// - convert: The closure that decodes the body. /// - Returns: A decoded wrapped type. + /// - Throws: An error if an issue occurs while decoding the request body or if the required body is missing. func getRequiredBufferingRequestBody( _ type: T.Type, from body: HTTPBody?, @@ -528,6 +548,7 @@ extension Converter { /// - transform: The closure that wraps the body in its generated type. /// - convert: The closure that decodes the body. /// - Returns: A decoded wrapped type, if body is not nil. + /// - Throws: An error if an issue occurs while decoding the request body. func getOptionalRequestBody( _ type: T.Type, from body: HTTPBody?, @@ -548,6 +569,7 @@ extension Converter { /// - transform: The closure that wraps the body in its generated type. /// - convert: The closure that decodes the body. /// - Returns: A decoded wrapped type. + /// - Throws: An error if an issue occurs while decoding the request body, or if the body is missing. func getRequiredRequestBody( _ type: T.Type, from body: HTTPBody?, @@ -574,6 +596,7 @@ extension Converter { /// - transform: The closure that wraps the body in its generated type. /// - convert: The closure that decodes the body. /// - Returns: A decoded wrapped type. + /// - Throws: An error if an issue occurs while decoding the response body. func getBufferingResponseBody( _ type: T.Type, from body: HTTPBody, @@ -592,6 +615,7 @@ extension Converter { /// - transform: The closure that wraps the body in its generated type. /// - convert: The closure that decodes the body. /// - Returns: A decoded wrapped type. + /// - Throws: An error if an issue occurs while decoding the response body. func getResponseBody( _ type: T.Type, from body: HTTPBody, @@ -611,6 +635,7 @@ extension Converter { /// - contentType: The content type value. /// - convert: The closure that encodes the value into a raw body. /// - Returns: The body, if value was not nil. + /// - Throws: An error if an issue occurs while encoding the request body. func setResponseBody( _ value: T, headerFields: inout HTTPFields, @@ -628,6 +653,7 @@ extension Converter { /// - type: The type to decode the value as. /// - convert: The closure that decodes the value from string. /// - Returns: A decoded value. + /// - Throws: An error if the specified path parameter is missing or if there's an issue decoding the value. func getRequiredRequestPath( in pathParameters: [String: Substring], name: String, diff --git a/Sources/OpenAPIRuntime/Conversion/ErrorExtensions.swift b/Sources/OpenAPIRuntime/Conversion/ErrorExtensions.swift index 1ad5651d..ff41be62 100644 --- a/Sources/OpenAPIRuntime/Conversion/ErrorExtensions.swift +++ b/Sources/OpenAPIRuntime/Conversion/ErrorExtensions.swift @@ -70,6 +70,7 @@ extension DecodingError { /// 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. public static func verifyAtLeastOneSchemaIsNotNil( _ values: [Any?], type: Any.Type, diff --git a/Sources/OpenAPIRuntime/Conversion/URLExtensions.swift b/Sources/OpenAPIRuntime/Conversion/URLExtensions.swift index 2ab67724..9c1a66d5 100644 --- a/Sources/OpenAPIRuntime/Conversion/URLExtensions.swift +++ b/Sources/OpenAPIRuntime/Conversion/URLExtensions.swift @@ -26,7 +26,6 @@ extension URL { /// Returns a validated server URL, or throws an error. /// - Parameter string: A URL string. - /// - Returns: A validated URL. /// - Throws: If the provided string doesn't convert to URL. public init(validatingOpenAPIServerURL string: String) throws { guard let url = Self(string: string) else { diff --git a/Sources/OpenAPIRuntime/Errors/ClientError.swift b/Sources/OpenAPIRuntime/Errors/ClientError.swift index 7663688a..53df18f3 100644 --- a/Sources/OpenAPIRuntime/Errors/ClientError.swift +++ b/Sources/OpenAPIRuntime/Errors/ClientError.swift @@ -72,10 +72,10 @@ public struct ClientError: Error { /// - operationID: The OpenAPI operation identifier. /// - operationInput: The operation-specific Input value. /// - request: The HTTP request created during the operation. - /// - request: The HTTP request body created during the operation. + /// - requestBody: The HTTP request body created during the operation. /// - baseURL: The base URL for HTTP requests. /// - response: The HTTP response received during the operation. - /// - response: The HTTP response body received during the operation. + /// - responseBody: The HTTP response body received during the operation. /// - underlyingError: The underlying error that caused the operation /// to fail. public init( @@ -109,12 +109,22 @@ public struct ClientError: Error { } extension ClientError: CustomStringConvertible { + /// A human-readable description of the client error. + /// + /// This computed property returns a string that includes information about the client error. + /// + /// - Returns: A string describing the client error and its associated details. public var description: String { "Client error - operationID: \(operationID), operationInput: \(String(describing: operationInput)), request: \(request?.prettyDescription ?? ""), requestBody: \(requestBody?.prettyDescription ?? ""), baseURL: \(baseURL?.absoluteString ?? ""), response: \(response?.prettyDescription ?? ""), responseBody: \(responseBody?.prettyDescription ?? "") , underlying error: \(underlyingErrorDescription)" } } extension ClientError: LocalizedError { + /// A localized description of the client error. + /// + /// This computed property provides a localized human-readable description of the client error, which is suitable for displaying to users. + /// + /// - Returns: A localized string describing the client error. public var errorDescription: String? { description } diff --git a/Sources/OpenAPIRuntime/Errors/RuntimeError.swift b/Sources/OpenAPIRuntime/Errors/RuntimeError.swift index 37069661..74eb1ef3 100644 --- a/Sources/OpenAPIRuntime/Errors/RuntimeError.swift +++ b/Sources/OpenAPIRuntime/Errors/RuntimeError.swift @@ -111,11 +111,23 @@ internal enum RuntimeError: Error, CustomStringConvertible, LocalizedError, Pret } } +/// Throws an error to indicate an unexpected HTTP response status. +/// +/// - Parameters: +/// - expectedStatus: The expected HTTP response status as a string. +/// - response: The HTTP response data. +/// - Throws: An error indicating an unexpected response status. @_spi(Generated) public func throwUnexpectedResponseStatus(expectedStatus: String, response: any Sendable) throws -> Never { throw RuntimeError.unexpectedResponseStatus(expectedStatus: expectedStatus, response: response) } +/// Throws an error to indicate an unexpected response body content. +/// +/// - Parameters: +/// - expectedContent: The expected content as a string. +/// - body: The response body data. +/// - Throws: An error indicating an unexpected response body content. @_spi(Generated) public func throwUnexpectedResponseBody(expectedContent: String, body: any Sendable) throws -> Never { throw RuntimeError.unexpectedResponseBody(expectedContent: expectedContent, body: body) diff --git a/Sources/OpenAPIRuntime/Errors/ServerError.swift b/Sources/OpenAPIRuntime/Errors/ServerError.swift index b93ac72b..1fee3a96 100644 --- a/Sources/OpenAPIRuntime/Errors/ServerError.swift +++ b/Sources/OpenAPIRuntime/Errors/ServerError.swift @@ -82,12 +82,22 @@ public struct ServerError: Error { } extension ServerError: CustomStringConvertible { + /// A human-readable description of the server error. + /// + /// This computed property returns a string that includes information about the server error. + /// + /// - Returns: A string describing the server error and its associated details. public var description: String { "Server error - operationID: \(operationID), request: \(request.prettyDescription), requestBody: \(requestBody?.prettyDescription ?? ""), metadata: \(requestMetadata.description), operationInput: \(operationInput.map { String(describing: $0) } ?? ""), operationOutput: \(operationOutput.map { String(describing: $0) } ?? ""), underlying error: \(underlyingErrorDescription)" } } extension ServerError: LocalizedError { + /// A localized description of the server error. + /// + /// This computed property provides a localized human-readable description of the server error, which is suitable for displaying to users. + /// + /// - Returns: A localized string describing the server error. public var errorDescription: String? { description } diff --git a/Sources/OpenAPIRuntime/Interface/ClientTransport.swift b/Sources/OpenAPIRuntime/Interface/ClientTransport.swift index 9ac1bf27..5d66ff6b 100644 --- a/Sources/OpenAPIRuntime/Interface/ClientTransport.swift +++ b/Sources/OpenAPIRuntime/Interface/ClientTransport.swift @@ -136,6 +136,7 @@ public protocol ClientTransport: Sendable { /// - baseURL: A server base URL. /// - operationID: The identifier of the OpenAPI operation. /// - Returns: An HTTP response and its body. + /// - Throws: An error if sending the request and receiving the response fails. func send( _ request: HTTPRequest, body: HTTPBody?, @@ -241,6 +242,7 @@ public protocol ClientMiddleware: Sendable { /// - operationID: The identifier of the OpenAPI operation. /// - next: A closure that calls the next middleware, or the transport. /// - Returns: An HTTP response and its body. + /// - Throws: An error if interception of the request and response fails. func intercept( _ request: HTTPRequest, body: HTTPBody?, diff --git a/Sources/OpenAPIRuntime/Interface/CurrencyTypes.swift b/Sources/OpenAPIRuntime/Interface/CurrencyTypes.swift index 328c5e56..477c5b93 100644 --- a/Sources/OpenAPIRuntime/Interface/CurrencyTypes.swift +++ b/Sources/OpenAPIRuntime/Interface/CurrencyTypes.swift @@ -22,8 +22,7 @@ public struct ServerRequestMetadata: Hashable, Sendable { public var pathParameters: [String: Substring] /// Creates a new metadata wrapper with the specified path and query parameters. - /// - Parameters: - /// - pathParameters: Path parameters parsed from the URL of the HTTP + /// - Parameter pathParameters: Path parameters parsed from the URL of the HTTP /// request. public init( pathParameters: [String: Substring] = [:] @@ -72,9 +71,7 @@ extension HTTPRequest { extension HTTPResponse { /// Creates a new response. - /// - Parameters: - /// - statusCode: The status code of the response.AsString - /// - headerFields: The HTTP header fields. + /// - Parameter statusCode: The status code of the response.AsString @_spi(Generated) public init(soar_statusCode statusCode: Int) { self.init(status: .init(code: statusCode)) @@ -82,6 +79,8 @@ extension HTTPResponse { } extension ServerRequestMetadata: CustomStringConvertible { + /// A textual description of the `ServerRequestMetadata` instance. + /// The description includes information about path parameters. public var description: String { "Path parameters: \(pathParameters.description)" } diff --git a/Sources/OpenAPIRuntime/Interface/HTTPBody.swift b/Sources/OpenAPIRuntime/Interface/HTTPBody.swift index 50b8b92d..c050450e 100644 --- a/Sources/OpenAPIRuntime/Interface/HTTPBody.swift +++ b/Sources/OpenAPIRuntime/Interface/HTTPBody.swift @@ -247,6 +247,14 @@ public final class HTTPBody: @unchecked Sendable { } extension HTTPBody: Equatable { + /// Compares two HTTPBody instances for equality by comparing their object identifiers. + /// + /// - Parameters: + /// - lhs: The left-hand side HTTPBody. + /// - rhs: The right-hand side HTTPBody. + /// + /// - Returns: `true` if the object identifiers of the two HTTPBody instances are equal, + /// indicating that they are the same object in memory; otherwise, returns `false`. public static func == ( lhs: HTTPBody, rhs: HTTPBody @@ -256,6 +264,9 @@ extension HTTPBody: Equatable { } extension HTTPBody: Hashable { + /// Hashes the HTTPBody instance by combining its object identifier into the provided hasher. + /// + /// - Parameter hasher: The hasher used to combine the hash value. public func hash(into hasher: inout Hasher) { hasher.combine(ObjectIdentifier(self)) } @@ -327,8 +338,7 @@ extension HTTPBody { } /// Creates a new body with the provided byte collection. - /// - Parameters: - /// - bytes: A byte chunk. + /// - Parameter bytes: A byte chunk. @inlinable public convenience init( _ bytes: some Collection & Sendable ) { @@ -405,8 +415,13 @@ extension HTTPBody { // MARK: - Consuming the body extension HTTPBody: AsyncSequence { + /// Represents a single element within an asynchronous sequence public typealias Element = ByteChunk + /// Represents an asynchronous iterator over a sequence of elements. public typealias AsyncIterator = Iterator + /// Creates and returns an asynchronous iterator + /// + /// - Returns: An asynchronous iterator for byte chunks. public func makeAsyncIterator() -> AsyncIterator { // The crash on error is intentional here. try! tryToMarkIteratorCreated() @@ -447,8 +462,7 @@ extension HTTPBody { /// Accumulates the full body in-memory into a single buffer /// up to the provided maximum number of bytes and returns it. - /// - Parameters: - /// - maxBytes: The maximum number of bytes this method is allowed + /// - Parameter maxBytes: The maximum number of bytes this method is allowed /// to accumulate in memory before it throws an error. /// - Throws: `TooManyBytesError` if the body contains more /// than `maxBytes`. @@ -524,8 +538,7 @@ extension HTTPBody { } /// Creates a new body with the provided string encoded as UTF-8 bytes. - /// - Parameters: - /// - string: A string to encode as bytes. + /// - Parameter string: A string to encode as bytes. @inlinable public convenience init( _ string: some StringProtocol & Sendable ) { @@ -613,6 +626,9 @@ extension String { // MARK: - HTTPBody conversions extension HTTPBody: ExpressibleByStringLiteral { + /// Initializes an `HTTPBody` instance with the provided string value. + /// + /// - Parameter value: The string literal to use for initializing the `HTTPBody`. public convenience init(stringLiteral value: String) { self.init(value) } @@ -628,7 +644,11 @@ extension HTTPBody { } extension HTTPBody: ExpressibleByArrayLiteral { + /// Element type for array literals. public typealias ArrayLiteralElement = UInt8 + /// Initializes an `HTTPBody` instance with a sequence of `UInt8` elements. + /// + /// - Parameter elements: A variadic list of `UInt8` elements used to initialize the `HTTPBody`. public convenience init(arrayLiteral elements: UInt8...) { self.init(elements) } @@ -681,6 +701,10 @@ extension HTTPBody { } } + /// Advances the iterator to the next element and returns it asynchronously. + /// + /// - Returns: The next element in the sequence, or `nil` if there are no more elements. + /// - Throws: An error if there is an issue advancing the iterator or retrieving the next element. public mutating func next() async throws -> Element? { try await produceNext() } diff --git a/Sources/OpenAPIRuntime/Interface/ServerTransport.swift b/Sources/OpenAPIRuntime/Interface/ServerTransport.swift index 7944c3d9..2ee147bc 100644 --- a/Sources/OpenAPIRuntime/Interface/ServerTransport.swift +++ b/Sources/OpenAPIRuntime/Interface/ServerTransport.swift @@ -111,6 +111,7 @@ public protocol ServerTransport { /// - handler: A handler to be invoked when an HTTP request is received. /// - method: An HTTP request method. /// - path: A URL template for the path, for example `/pets/{petId}`. + /// - Throws: An error if the registration of the handler fails. /// - Important: The `path` can have mixed components, such /// as `/file/{name}.zip`. func register( @@ -218,6 +219,7 @@ public protocol ServerMiddleware: Sendable { /// - operationID: The identifier of the OpenAPI operation. /// - next: A closure that calls the next middleware, or the transport. /// - Returns: An HTTP response and its body. + /// - Throws: An error if the interception process fails. func intercept( _ request: HTTPRequest, body: HTTPBody?, diff --git a/Sources/OpenAPIRuntime/Interface/UniversalClient.swift b/Sources/OpenAPIRuntime/Interface/UniversalClient.swift index 019ca728..06eaaf79 100644 --- a/Sources/OpenAPIRuntime/Interface/UniversalClient.swift +++ b/Sources/OpenAPIRuntime/Interface/UniversalClient.swift @@ -83,6 +83,7 @@ import Foundation /// - serializer: Creates an HTTP request from the provided Input value. /// - deserializer: Creates an Output value from the provided HTTP response. /// - Returns: The Output value produced by `deserializer`. + /// - Throws: An error if any part of the HTTP operation process fails. public func send( input: OperationInput, forOperation operationID: String, diff --git a/Sources/OpenAPIRuntime/Interface/UniversalServer.swift b/Sources/OpenAPIRuntime/Interface/UniversalServer.swift index 53cd613a..6fba52a2 100644 --- a/Sources/OpenAPIRuntime/Interface/UniversalServer.swift +++ b/Sources/OpenAPIRuntime/Interface/UniversalServer.swift @@ -91,6 +91,7 @@ import struct Foundation.URLComponents /// - serializer: A closure that creates an HTTP response from the /// provided Output value. /// - Returns: The HTTP response and its body produced by the serializer. + /// - Throws: An error if any part of the operation process fails. public func handle( request: HTTPRequest, requestBody: HTTPBody?, @@ -172,6 +173,7 @@ import struct Foundation.URLComponents /// Returns the path with the server URL's path prefix prepended. /// - Parameter path: The path suffix. /// - Returns: The path appended to the server URL's path. + /// - Throws: An error if resolving the server URL components fails or if the server URL is invalid. public func apiPathComponentsWithServerPrefix( _ path: String ) throws -> String { diff --git a/Sources/OpenAPIRuntime/URICoder/Decoding/URIDecoder.swift b/Sources/OpenAPIRuntime/URICoder/Decoding/URIDecoder.swift index c6b09dcb..138d60cc 100644 --- a/Sources/OpenAPIRuntime/URICoder/Decoding/URIDecoder.swift +++ b/Sources/OpenAPIRuntime/URICoder/Decoding/URIDecoder.swift @@ -72,6 +72,7 @@ extension URIDecoder { /// and explode options, ignored otherwise. /// - data: The URI-encoded string. /// - Returns: The decoded value. + /// - Throws: An error if decoding fails, for example, due to incompatible data or key. func decode( _ type: T.Type = T.self, forKey key: String = "", @@ -94,6 +95,7 @@ extension URIDecoder { /// and explode options, ignored otherwise. /// - data: The URI-encoded string. /// - Returns: The decoded value. + /// - Throws: An error if decoding fails, for example, due to incompatible data or key. func decodeIfPresent( _ type: T.Type = T.self, forKey key: String = "", @@ -112,6 +114,7 @@ extension URIDecoder { /// - calls: The closure that contains 0 or more calls to /// the `decode` method on `URICachedDecoder`. /// - Returns: The result of the closure invocation. + /// - Throws: An error if parsing or decoding fails. func withCachedParser( from data: Substring, calls: (URICachedDecoder) throws -> R @@ -142,6 +145,7 @@ struct URICachedDecoder { /// - key: The key of the decoded value. Only used with certain styles /// and explode options, ignored otherwise. /// - Returns: The decoded value. + /// - Throws: An error if decoding fails. func decode( _ type: T.Type = T.self, forKey key: String = "" @@ -167,6 +171,7 @@ struct URICachedDecoder { /// - key: The key of the decoded value. Only used with certain styles /// and explode options, ignored otherwise. /// - Returns: The decoded value. + /// - Throws: An error if decoding fails. func decodeIfPresent( _ type: T.Type = T.self, forKey key: String = "" diff --git a/Sources/OpenAPIRuntime/URICoder/Decoding/URIValueFromNodeDecoder+Keyed.swift b/Sources/OpenAPIRuntime/URICoder/Decoding/URIValueFromNodeDecoder+Keyed.swift index 03485d6b..6590be92 100644 --- a/Sources/OpenAPIRuntime/URICoder/Decoding/URIValueFromNodeDecoder+Keyed.swift +++ b/Sources/OpenAPIRuntime/URICoder/Decoding/URIValueFromNodeDecoder+Keyed.swift @@ -43,7 +43,9 @@ extension URIKeyedDecodingContainer { /// Returns the value found for the provided key in the underlying /// dictionary converted to the provided type. - /// - Parameter key: The key for which to return the value. + /// - Parameters: + /// - _: The `BinaryFloatingPoint` type to convert the value to. + /// - key: The key for which to return the value. /// - Returns: The converted value found for the provided key. /// - Throws: An error if no value for the key was found or if the /// conversion failed. @@ -65,7 +67,9 @@ extension URIKeyedDecodingContainer { /// Returns the value found for the provided key in the underlying /// dictionary converted to the provided type. - /// - Parameter key: The key for which to return the value. + /// - Parameters: + /// - _: The fixed-width integer type to convert the value to. + /// - key: The key for which to return the value. /// - Returns: The converted value found for the provided key. /// - Throws: An error if no value for the key was found or if the /// conversion failed. @@ -87,7 +91,9 @@ extension URIKeyedDecodingContainer { /// Returns the value found for the provided key in the underlying /// dictionary converted to the provided type. - /// - Parameter key: The key for which to return the value. + /// - Parameters: + /// - _: The type to convert the value to. + /// - key: The key for which to return the value. /// - Returns: The converted value found for the provided key. /// - Throws: An error if no value for the key was found or if the /// conversion failed. diff --git a/Sources/OpenAPIRuntime/URICoder/Decoding/URIValueFromNodeDecoder+Single.swift b/Sources/OpenAPIRuntime/URICoder/Decoding/URIValueFromNodeDecoder+Single.swift index 3929df11..32592fd7 100644 --- a/Sources/OpenAPIRuntime/URICoder/Decoding/URIValueFromNodeDecoder+Single.swift +++ b/Sources/OpenAPIRuntime/URICoder/Decoding/URIValueFromNodeDecoder+Single.swift @@ -32,6 +32,8 @@ extension URISingleValueDecodingContainer { /// Returns the value found in the underlying node converted to /// the provided type. + /// + /// - Parameter _: The `BinaryFloatingPoint` type to convert the value to. /// - Returns: The converted value found. /// - Throws: An error if the conversion failed. private func _decodeBinaryFloatingPoint( @@ -51,6 +53,8 @@ extension URISingleValueDecodingContainer { /// Returns the value found in the underlying node converted to /// the provided type. + /// + /// - Parameter _: The `FixedWidthInteger` type to convert the value to. /// - Returns: The converted value found. /// - Throws: An error if the conversion failed. private func _decodeFixedWidthInteger( @@ -70,6 +74,8 @@ extension URISingleValueDecodingContainer { /// Returns the value found in the underlying node converted to /// the provided type. + /// + /// - Parameter _: The `LosslessStringConvertible` type to convert the value to. /// - Returns: The converted value found. /// - Throws: An error if the conversion failed. private func _decodeLosslessStringConvertible( diff --git a/Sources/OpenAPIRuntime/URICoder/Decoding/URIValueFromNodeDecoder+Unkeyed.swift b/Sources/OpenAPIRuntime/URICoder/Decoding/URIValueFromNodeDecoder+Unkeyed.swift index 5f0d78be..c985145a 100644 --- a/Sources/OpenAPIRuntime/URICoder/Decoding/URIValueFromNodeDecoder+Unkeyed.swift +++ b/Sources/OpenAPIRuntime/URICoder/Decoding/URIValueFromNodeDecoder+Unkeyed.swift @@ -65,6 +65,8 @@ extension URIUnkeyedDecodingContainer { } /// Returns the next value converted to the provided type. + /// + /// - Parameter _: The `BinaryFloatingPoint` type to convert the value to. /// - Returns: The converted value. /// - Throws: An error if the container ran out of items or if /// the conversion failed. @@ -84,6 +86,8 @@ extension URIUnkeyedDecodingContainer { } /// Returns the next value converted to the provided type. + /// + /// - Parameter _: The `FixedWidthInteger` type to convert the value to. /// - Returns: The converted value. /// - Throws: An error if the container ran out of items or if /// the conversion failed. @@ -103,6 +107,8 @@ extension URIUnkeyedDecodingContainer { } /// Returns the next value converted to the provided type. + /// + /// - Parameter _: The `LosslessStringConvertible` type to convert the value to. /// - Returns: The converted value. /// - Throws: An error if the container ran out of items or if /// the conversion failed. diff --git a/Sources/OpenAPIRuntime/URICoder/Decoding/URIValueFromNodeDecoder.swift b/Sources/OpenAPIRuntime/URICoder/Decoding/URIValueFromNodeDecoder.swift index b197b82e..a8b319f3 100644 --- a/Sources/OpenAPIRuntime/URICoder/Decoding/URIValueFromNodeDecoder.swift +++ b/Sources/OpenAPIRuntime/URICoder/Decoding/URIValueFromNodeDecoder.swift @@ -150,6 +150,7 @@ extension URIValueFromNodeDecoder { /// value at the provided key. /// - Parameter codingKey: The coding key for the value that is then put /// at the top of the stack. + /// - Throws: An error if an issue occurs during the container push operation. func push(_ codingKey: URICoderCodingKey) throws { let nextElement: URIDecodedNode if let intValue = codingKey.intValue { @@ -171,6 +172,7 @@ extension URIValueFromNodeDecoder { /// Throws a type mismatch error with the provided message. /// - Parameter message: The message to be embedded as debug description /// inside the thrown `DecodingError`. + /// - Throws: A `DecodingError` with a type mismatch error if this function is called. private func throwMismatch(_ message: String) throws -> Never { throw DecodingError.typeMismatch( String.self, @@ -184,6 +186,7 @@ extension URIValueFromNodeDecoder { /// Extracts the root value of the provided node using the root key. /// - Parameter node: The node which to expect for the root key. /// - Returns: The value found at the root key in the provided node. + /// - Throws: A `DecodingError` if the value is not found at the root key private func rootValue(in node: URIParsedNode) throws -> URIParsedValueArray { guard let value = node[rootKey] else { if style == .simple, let valueForFallbackKey = node[""] { @@ -200,6 +203,7 @@ extension URIValueFromNodeDecoder { /// Extracts the node at the top of the coding stack and tries to treat it /// as a dictionary. /// - Returns: The value if it can be treated as a dictionary. + /// - Throws: An error if the current element cannot be treated as a dictionary. private func currentElementAsDictionary() throws -> URIParsedNode { try nodeAsDictionary(currentElement) } @@ -248,6 +252,7 @@ extension URIValueFromNodeDecoder { /// Extracts the node at the top of the coding stack and tries to treat it /// as an array. /// - Returns: The value if it can be treated as an array. + /// - Throws: An error if the node cannot be treated as an array. private func currentElementAsArray() throws -> URIParsedValueArray { try nodeAsArray(currentElement) } @@ -271,6 +276,7 @@ extension URIValueFromNodeDecoder { /// Extracts the node at the top of the coding stack and tries to treat it /// as a primitive value. /// - Returns: The value if it can be treated as a primitive value. + /// - Throws: An error if the node cannot be treated as a primitive value. func currentElementAsSingleValue() throws -> URIParsedValue { try nodeAsSingleValue(currentElement) } diff --git a/Sources/OpenAPIRuntime/URICoder/Encoding/URIEncoder.swift b/Sources/OpenAPIRuntime/URICoder/Encoding/URIEncoder.swift index 10afaa14..de400dc1 100644 --- a/Sources/OpenAPIRuntime/URICoder/Encoding/URIEncoder.swift +++ b/Sources/OpenAPIRuntime/URICoder/Encoding/URIEncoder.swift @@ -79,6 +79,7 @@ extension URIEncoder { /// - key: The key for which to encode the value. Can be an empty key, /// in which case you still get a key-value pair, like `=foo`. /// - Returns: The URI string. + /// - Throws: An error if encoding the object into a URI string fails func encode( _ value: some Encodable, forKey key: String @@ -102,6 +103,7 @@ extension URIEncoder { /// - key: The key for which to encode the value. Can be an empty key, /// in which case you still get a key-value pair, like `=foo`. /// - Returns: The URI string. + /// - Throws: An error if encoding the object into a URI string fails. func encodeIfPresent( _ value: (some Encodable)?, forKey key: String diff --git a/Sources/OpenAPIRuntime/URICoder/Encoding/URIValueToNodeEncoder+Keyed.swift b/Sources/OpenAPIRuntime/URICoder/Encoding/URIValueToNodeEncoder+Keyed.swift index 06810db6..1361a307 100644 --- a/Sources/OpenAPIRuntime/URICoder/Encoding/URIValueToNodeEncoder+Keyed.swift +++ b/Sources/OpenAPIRuntime/URICoder/Encoding/URIValueToNodeEncoder+Keyed.swift @@ -28,6 +28,7 @@ extension URIKeyedEncodingContainer { /// - Parameters: /// - node: The child node to insert. /// - key: The key for the child node. + /// - Throws: An error if inserting the child node into the underlying dictionary at the provided key fails. private func _insertValue(_ node: URIEncodedNode, atKey key: Key) throws { try encoder.currentStackEntry.storage.insert(node, atKey: key) } @@ -37,6 +38,7 @@ extension URIKeyedEncodingContainer { /// - Parameters: /// - node: The primitive value to insert. /// - key: The key for the value. + /// - Throws: An error if inserting the primitive value into the underlying dictionary at the provided key fails. private func _insertValue(_ node: URIEncodedNode.Primitive, atKey key: Key) throws { try _insertValue(.primitive(node), atKey: key) } @@ -44,8 +46,9 @@ extension URIKeyedEncodingContainer { /// Inserts the provided value into the underlying dictionary at /// the provided key. /// - Parameters: - /// - node: The value to insert. + /// - value: The value to insert. /// - key: The key for the value. + /// - Throws: An error if inserting the value into the underlying dictionary at the provided key fails. private func _insertBinaryFloatingPoint( _ value: some BinaryFloatingPoint, atKey key: Key @@ -56,8 +59,10 @@ extension URIKeyedEncodingContainer { /// Inserts the provided value into the underlying dictionary at /// the provided key. /// - Parameters: - /// - node: The value to insert. + /// - value: The value to insert. /// - key: The key for the value. + /// - Throws: An error if the provided value is outside the valid range for an integer, + /// or if inserting the value into the underlying dictionary at the provided key fails. private func _insertFixedWidthInteger( _ value: some FixedWidthInteger, atKey key: Key diff --git a/Sources/OpenAPIRuntime/URICoder/Encoding/URIValueToNodeEncoder+Single.swift b/Sources/OpenAPIRuntime/URICoder/Encoding/URIValueToNodeEncoder+Single.swift index a0da53bd..e2a45b6a 100644 --- a/Sources/OpenAPIRuntime/URICoder/Encoding/URIValueToNodeEncoder+Single.swift +++ b/Sources/OpenAPIRuntime/URICoder/Encoding/URIValueToNodeEncoder+Single.swift @@ -25,18 +25,21 @@ extension URISingleValueEncodingContainer { /// Sets the provided primitive value to the underlying node. /// - Parameter node: The primitive value to set. + /// - Throws: An error if setting the primitive value to the underlying node fails. private func _setValue(_ node: URIEncodedNode.Primitive) throws { try encoder.currentStackEntry.storage.set(node) } /// Sets the provided value to the underlying node. - /// - Parameter node: The value to set. + /// - Parameter value: The value to set. + /// - Throws: An error if setting the value to the underlying node fails private func _setBinaryFloatingPoint(_ value: some BinaryFloatingPoint) throws { try _setValue(.double(Double(value))) } /// Sets the provided value to the underlying node. - /// - Parameter node: The value to set. + /// - Parameter value: The value to set. + /// - Throws: An error if setting the value to the underlying node fails private func _setFixedWidthInteger(_ value: some FixedWidthInteger) throws { guard let validatedValue = Int(exactly: value) else { throw URIValueToNodeEncoder.GeneralError.integerOutOfRange diff --git a/Sources/OpenAPIRuntime/URICoder/Encoding/URIValueToNodeEncoder+Unkeyed.swift b/Sources/OpenAPIRuntime/URICoder/Encoding/URIValueToNodeEncoder+Unkeyed.swift index ee63f135..7dbf7d7a 100644 --- a/Sources/OpenAPIRuntime/URICoder/Encoding/URIValueToNodeEncoder+Unkeyed.swift +++ b/Sources/OpenAPIRuntime/URICoder/Encoding/URIValueToNodeEncoder+Unkeyed.swift @@ -25,24 +25,28 @@ extension URIUnkeyedEncodingContainer { /// Appends the provided node to the underlying array. /// - Parameter node: The node to append. + /// - Throws: An error if appending the node to the underlying array fails. private func _appendValue(_ node: URIEncodedNode) throws { try encoder.currentStackEntry.storage.append(node) } /// Appends the provided primitive value as a node to the underlying array. /// - Parameter node: The value to append. + /// - Throws: An error if appending the node to the underlying array fails. private func _appendValue(_ node: URIEncodedNode.Primitive) throws { try _appendValue(.primitive(node)) } /// Appends the provided value as a node to the underlying array. - /// - Parameter node: The value to append. + /// - Parameter value: The value to append. + /// - Throws: An error if appending the node to the underlying array fails. private func _appendBinaryFloatingPoint(_ value: some BinaryFloatingPoint) throws { try _appendValue(.double(Double(value))) } /// Appends the provided value as a node to the underlying array. - /// - Parameter node: The value to append. + /// - Parameter value: The value to append. + /// - Throws: An error if appending the node to the underlying array fails. private func _appendFixedWidthInteger(_ value: some FixedWidthInteger) throws { guard let validatedValue = Int(exactly: value) else { throw URIValueToNodeEncoder.GeneralError.integerOutOfRange diff --git a/Sources/OpenAPIRuntime/URICoder/Encoding/URIValueToNodeEncoder.swift b/Sources/OpenAPIRuntime/URICoder/Encoding/URIValueToNodeEncoder.swift index fb227794..d46ec9df 100644 --- a/Sources/OpenAPIRuntime/URICoder/Encoding/URIValueToNodeEncoder.swift +++ b/Sources/OpenAPIRuntime/URICoder/Encoding/URIValueToNodeEncoder.swift @@ -61,6 +61,7 @@ final class URIValueToNodeEncoder { /// Encodes the provided value into a node. /// - Parameter value: The value to encode. /// - Returns: The node with the encoded contents of the value. + /// - Throws: An error if encoding the value into a node fails. func encodeValue(_ value: some Encodable) throws -> URIEncodedNode { defer { _codingPath = [] diff --git a/Sources/OpenAPIRuntime/URICoder/Parsing/URIParser.swift b/Sources/OpenAPIRuntime/URICoder/Parsing/URIParser.swift index 8953da91..793f7fcc 100644 --- a/Sources/OpenAPIRuntime/URICoder/Parsing/URIParser.swift +++ b/Sources/OpenAPIRuntime/URICoder/Parsing/URIParser.swift @@ -52,6 +52,7 @@ extension URIParser { /// Parses the root node from the underlying string, selecting the logic /// based on the configuration. /// - Returns: The parsed root node. + /// - Throws: An error if parsing fails. mutating func parseRoot() throws -> URIParsedNode { // A completely empty string should get parsed as a single // empty key with a single element array with an empty string @@ -79,6 +80,7 @@ extension URIParser { /// Parses the root node assuming the raw string uses the form style /// and the explode parameter is enabled. /// - Returns: The parsed root node. + /// - Throws: An error if parsing fails. private mutating func parseExplodedFormRoot() throws -> URIParsedNode { try parseGenericRoot { data, appendPair in let keyValueSeparator: Character = "=" @@ -110,6 +112,7 @@ extension URIParser { /// Parses the root node assuming the raw string uses the form style /// and the explode parameter is disabled. /// - Returns: The parsed root node. + /// - Throws: An error if parsing fails. private mutating func parseUnexplodedFormRoot() throws -> URIParsedNode { try parseGenericRoot { data, appendPair in let keyValueSeparator: Character = "=" @@ -162,6 +165,7 @@ extension URIParser { /// Parses the root node assuming the raw string uses the simple style /// and the explode parameter is enabled. /// - Returns: The parsed root node. + /// - Throws: An error if parsing fails. private mutating func parseExplodedSimpleRoot() throws -> URIParsedNode { try parseGenericRoot { data, appendPair in let keyValueSeparator: Character = "=" @@ -193,6 +197,7 @@ extension URIParser { /// Parses the root node assuming the raw string uses the simple style /// and the explode parameter is disabled. /// - Returns: The parsed root node. + /// - Throws: An error if parsing fails. private mutating func parseUnexplodedSimpleRoot() throws -> URIParsedNode { // Unexploded simple dictionary cannot be told apart from // an array, so we just accumulate all pairs as standalone @@ -219,6 +224,7 @@ extension URIParser { /// - Parameter parser: A closure that accepts another closure, which should /// be called 0 or more times, once for each parsed key-value pair. /// - Returns: The accumulated node. + /// - Throws: An error if parsing using the provided parser closure fails, private mutating func parseGenericRoot( _ parser: (inout Raw, (Raw, [Raw]) -> Void) throws -> Void ) throws -> URIParsedNode { @@ -322,8 +328,7 @@ extension String.SubSequence { /// Accumulates characters until the provided character is found, /// or the end is reached. Moves the underlying startIndex. - /// - Parameters: - /// - character: A character to stop at. + /// - Parameter character: A character to stop at. /// - Returns: The accumulated substring. fileprivate mutating func parseUpToCharacterOrEnd( _ character: Character diff --git a/Sources/OpenAPIRuntime/URICoder/Serialization/URISerializer.swift b/Sources/OpenAPIRuntime/URICoder/Serialization/URISerializer.swift index a5bf1f08..2e3e8b20 100644 --- a/Sources/OpenAPIRuntime/URICoder/Serialization/URISerializer.swift +++ b/Sources/OpenAPIRuntime/URICoder/Serialization/URISerializer.swift @@ -38,6 +38,7 @@ struct URISerializer { /// - key: The key to serialize the node under (details depend on the /// style and explode parameters in the configuration). /// - Returns: The URI-encoded data for the provided node. + /// - Throws: An error if serialization of the node fails. mutating func serializeNode( _ value: URIEncodedNode, forKey key: String @@ -96,6 +97,7 @@ extension URISerializer { /// Provides a raw string value for the provided key. /// - Parameter key: The key to stringify. /// - Returns: The escaped version of the provided key. + /// - Throws: An error if the key cannot be converted to an escaped string. private func stringifiedKey(_ key: String) throws -> String { // The root key is handled separately. guard !key.isEmpty else { @@ -110,6 +112,7 @@ extension URISerializer { /// - value: The value to serialize. /// - key: The key to serialize the value under (details depend on the /// style and explode parameters in the configuration). + /// - Throws: An error if serialization of the value fails. private mutating func serializeTopLevelNode( _ value: URIEncodedNode, forKey key: String @@ -152,6 +155,7 @@ extension URISerializer { /// Serializes the provided value into the underlying string. /// - Parameter value: The primitive value to serialize. + /// - Throws: An error if serialization of the primitive value fails. private mutating func serializePrimitiveValue( _ value: URIEncodedNode.Primitive ) throws { @@ -178,6 +182,7 @@ extension URISerializer { /// style and explode parameters in the configuration). /// - separator: The separator to use, if nil, the key is not serialized, /// only the value. + /// - Throws: An error if serialization of the key-value pair fails. private mutating func serializePrimitiveKeyValuePair( _ value: URIEncodedNode.Primitive, forKey key: String, @@ -195,6 +200,7 @@ extension URISerializer { /// - array: The value to serialize. /// - key: The key to serialize the value under (details depend on the /// style and explode parameters in the configuration). + /// - Throws: An error if serialization of the array fails. private mutating func serializeArray( _ array: [URIEncodedNode.Primitive], forKey key: String @@ -244,6 +250,7 @@ extension URISerializer { /// - dictionary: The value to serialize. /// - key: The key to serialize the value under (details depend on the /// style and explode parameters in the configuration). + /// - Throws: An error if serialization of the dictionary fails. private mutating func serializeDictionary( _ dictionary: [String: URIEncodedNode.Primitive], forKey key: String diff --git a/Tests/OpenAPIRuntimeTests/Base/Test_CopyOnWriteBox.swift b/Tests/OpenAPIRuntimeTests/Base/Test_CopyOnWriteBox.swift new file mode 100644 index 00000000..59c9bd56 --- /dev/null +++ b/Tests/OpenAPIRuntimeTests/Base/Test_CopyOnWriteBox.swift @@ -0,0 +1,96 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the SwiftOpenAPIGenerator open source project +// +// Copyright (c) 2023 Apple Inc. and the SwiftOpenAPIGenerator project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of SwiftOpenAPIGenerator project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// +import XCTest +@_spi(Generated) import OpenAPIRuntime + +final class Test_CopyOnWriteBox: Test_Runtime { + + struct Node: Codable, Equatable { + var id: Int + var parent: CopyOnWriteBox? + } + + func testModification() throws { + var value = Node( + id: 3, + parent: .init( + value: .init( + id: 2 + ) + ) + ) + XCTAssertEqual( + value, + Node( + id: 3, + parent: .init( + value: .init( + id: 2 + ) + ) + ) + ) + value.parent!.value.parent = .init(value: .init(id: 1)) + XCTAssertEqual( + value, + Node( + id: 3, + parent: .init( + value: .init( + id: 2, + parent: .init( + value: .init(id: 1) + ) + ) + ) + ) + ) + } + + func testSerialization() throws { + let value = CopyOnWriteBox(value: "Hello") + try testRoundtrip( + value, + expectedJSON: #""Hello""# + ) + } + + func testIntegration() throws { + let value = Node( + id: 3, + parent: .init( + value: .init( + id: 2, + parent: .init( + value: .init(id: 1) + ) + ) + ) + ) + try testRoundtrip( + value, + expectedJSON: #""" + { + "id" : 3, + "parent" : { + "id" : 2, + "parent" : { + "id" : 1 + } + } + } + """# + ) + } +} diff --git a/Tests/OpenAPIRuntimeTests/Conversion/Test_Converter+Client.swift b/Tests/OpenAPIRuntimeTests/Conversion/Test_Converter+Client.swift index f86961ad..250642ce 100644 --- a/Tests/OpenAPIRuntimeTests/Conversion/Test_Converter+Client.swift +++ b/Tests/OpenAPIRuntimeTests/Conversion/Test_Converter+Client.swift @@ -264,6 +264,14 @@ final class Test_ClientConverterExtensions: Test_Runtime { } } +/// Asserts that the string representation of binary data is equal to an expected string. +/// +/// - Parameters: +/// - expression1: An autoclosure that evaluates to a `Data`, which represents the binary data. +/// - expression2: An autoclosure that evaluates to the expected string. +/// - message: An optional custom message to display upon test failure. +/// - file: The file name to include in the failure message (default is the source file where this function is called). +/// - line: The line number to include in the failure message (default is the line where this function is called). public func XCTAssertEqualStringifiedData( _ expression1: @autoclosure () throws -> Data, _ expression2: @autoclosure () throws -> String, diff --git a/Tests/OpenAPIRuntimeTests/Test_Runtime.swift b/Tests/OpenAPIRuntimeTests/Test_Runtime.swift index f99506e7..2d6756a4 100644 --- a/Tests/OpenAPIRuntimeTests/Test_Runtime.swift +++ b/Tests/OpenAPIRuntimeTests/Test_Runtime.swift @@ -131,11 +131,14 @@ class Test_Runtime: XCTestCase { Data(testStructURLFormString.utf8) } - func _testPrettyEncoded(_ value: Value, expectedJSON: String) throws { + @discardableResult + func _testPrettyEncoded(_ value: Value, expectedJSON: String) throws -> String { let encoder = JSONEncoder() encoder.outputFormatting = [.prettyPrinted, .sortedKeys] let data = try encoder.encode(value) - XCTAssertEqual(String(data: data, encoding: .utf8)!, expectedJSON) + let encodedString = String(decoding: data, as: UTF8.self) + XCTAssertEqual(encodedString, expectedJSON) + return encodedString } func _getDecoded(json: String) throws -> Value { @@ -143,8 +146,21 @@ class Test_Runtime: XCTestCase { let decoder = JSONDecoder() return try decoder.decode(Value.self, from: inputData) } + + func testRoundtrip(_ value: Value, expectedJSON: String) throws { + let encodedString = try _testPrettyEncoded(value, expectedJSON: expectedJSON) + let decoded: Value = try _getDecoded(json: encodedString) + XCTAssertEqual(decoded, value) + } } +/// Asserts that a given URL's absolute string representation is equal to an expected string. +/// +/// - Parameters: +/// - lhs: The URL to test, which can be optional. +/// - rhs: The expected absolute string representation. +/// - file: The file name to include in the failure message (default is the source file where this function is called). +/// - line: The line number to include in the failure message (default is the line where this function is called). public func XCTAssertEqualURLString(_ lhs: URL?, _ rhs: String, file: StaticString = #file, line: UInt = #line) { guard let lhs else { XCTFail("URL is nil") @@ -209,6 +225,14 @@ struct PrintingMiddleware: ClientMiddleware { } } +/// Asserts that the string representation of binary data in a given sequence is equal to an expected string. +/// +/// - Parameters: +/// - expression1: An autoclosure that evaluates to a sequence of `UInt8`, typically binary data. +/// - expression2: An autoclosure that evaluates to the expected string. +/// - message: An optional custom message to display upon test failure. +/// - file: The file name to include in the failure message (default is the source file where this function is called). +/// - line: The line number to include in the failure message (default is the line where this function is called). public func XCTAssertEqualStringifiedData( _ expression1: @autoclosure () throws -> S?, _ expression2: @autoclosure () throws -> String, @@ -228,6 +252,14 @@ public func XCTAssertEqualStringifiedData( } } +/// Asserts that the string representation of binary data in an HTTP body is equal to an expected string. +/// - Parameters: +/// - expression1: An autoclosure that evaluates to an `HTTPBody?`, which represents the binary data. +/// - expression2: An autoclosure that evaluates to the expected string. +/// - message: An optional custom message to display upon test failure. +/// - file: The file name to include in the failure message (default is the source file where this function is called). +/// - line: The line number to include in the failure message (default is the line where this function is called). +/// - Throws: If either of the autoclosures throws an error, the function will rethrow that error. public func XCTAssertEqualStringifiedData( _ expression1: @autoclosure () throws -> HTTPBody?, _ expression2: @autoclosure () throws -> String,