Repository: apple/swift-nio
File: Sources/NIOPosix/HappyEyeballs.swift (Swift)

Code before:
public struct NIOConnectionError: Error {
    /// The hostname SwiftNIO was trying to connect to.
    public let host: String

    /// The port SwiftNIO was trying to connect to.
    public let port: Int

    /// The error we encountered doing the DNS A lookup, if any.
    public fileprivate(set) var dnsAError: Error? = nil

    /// The error we encountered doing the DNS AAAA lookup, if any.
    public fileprivate(set) var dnsAAAAError: Error? = nil

    /// The errors we encountered during the connection attempts.
    public fileprivate(set) var connectionErrors: [SingleConnectionFailure] = []

    fileprivate init(host: String, port: Int) {
        self.host = host
        self.port = port
    }
}

extension NIOConnectionError: CustomStringConvertible {
    public var description: String {
        if let dnsError = (dnsAError ?? dnsAAAAError) {
             return "DNS error: \(dnsError.localizedDescription)"
         }

         if !connectionErrors.isEmpty {
             let descriptions = connectionErrors.map {
                 if let channelError = $0.error as? ChannelError {
                     channelError.description
                 } else {
                     $0.error.localizedDescription
                 }
             }

             return "Connection errors: \(descriptions.joined(separator: ", "))"
         }

         return "Unknown error"
     }
}

/// A simple iterator that manages iterating over the possible targets.
///
/// This iterator knows how to merge together the A and AAAA records in a sensible way:
/// specifically, it keeps track of what the last address family it emitted was, and emits the
/// address of the opposite family next.
private struct TargetIterator: IteratorProtocol {
    typealias Element = SocketAddress

Review comment (suggestion):
Localized description gives bad error messages unless the underlying error is an `NSError`:

```suggestion
             return "DNS error: \(dnsError)"
```

Code after:
public struct NIOConnectionError: Error {
    /// The hostname SwiftNIO was trying to connect to.
    public let host: String

    /// The port SwiftNIO was trying to connect to.
    public let port: Int

    /// The error we encountered doing the DNS A lookup, if any.
    public fileprivate(set) var dnsAError: Error? = nil

    /// The error we encountered doing the DNS AAAA lookup, if any.
    public fileprivate(set) var dnsAAAAError: Error? = nil

    /// The errors we encountered during the connection attempts.
    public fileprivate(set) var connectionErrors: [SingleConnectionFailure] = []

    fileprivate init(host: String, port: Int) {
        self.host = host
        self.port = port
    }
}

extension NIOConnectionError: CustomStringConvertible {
    public var description: String {
        if let dnsError = (self.dnsAError ?? self.dnsAAAAError) {
            return "DNS error: \(dnsError)"
        }

        if !self.connectionErrors.isEmpty {
            let descriptions = self.connectionErrors.map {
                String(describing: $0)
            }

            return "Connection errors: \(descriptions.joined(separator: ", "))"
        }

        return "NIOConnectionError: unknown"
    }
}

/// A simple iterator that manages iterating over the possible targets.
///
/// This iterator knows how to merge together the A and AAAA records in a sensible way:
/// specifically, it keeps track of what the last address family it emitted was, and emits the
/// address of the opposite family next.
private struct TargetIterator: IteratorProtocol {
    typealias Element = SocketAddress

    private enum AddressFamily {
        case v4
        case v6