Skip to content

Commit 08c07ff

Browse files
authored
Simplify AsyncDNSResolver.Error (#27)
Motivation: The 'AsyncDNSResolver.Error' is closely modelled on the c-ares error codes and doesn't fit well with the DNSSD error codes. Further, the DNSSD backed doesn't map its error cdes back into an 'AsyncDNSResolver.Error' so all DNSSD errors become 'other' errors. Modifications: - Reduce the number of error codes in 'AsyncDNSResolver.Error' to a few high level error codes and a catch-all 'internal' error code. - Update mappings from c-ares error codes, add mappings from dnssd error codes. - Add a 'source' property, which can be 'any Error', allowing users to get fine grained error information if necessary. Result: Error codes are more consistent across implementations.
1 parent 6e94db5 commit 08c07ff

File tree

9 files changed

+158
-173
lines changed

9 files changed

+158
-173
lines changed

Sources/AsyncDNSResolver/Errors.swift

Lines changed: 41 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -15,150 +15,84 @@
1515
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
1616
extension AsyncDNSResolver {
1717
/// Possible ``AsyncDNSResolver/AsyncDNSResolver`` errors.
18-
public struct Error: Swift.Error, Hashable, CustomStringConvertible {
18+
public struct Error: Swift.Error, CustomStringConvertible {
1919
public struct Code: Hashable, Sendable {
2020
fileprivate enum Value: Hashable, Sendable {
21-
case invalidQuery
22-
case serverFailure
23-
case notFound
24-
case notImplemented
25-
case serverRefused
2621
case badQuery
27-
case badName
28-
case badFamily
2922
case badResponse
3023
case connectionRefused
3124
case timeout
32-
case eof
33-
case fileIO
34-
case noMemory
35-
case destruction
36-
case badString
37-
case badFlags
38-
case noName
39-
case badHints
40-
case notInitialized
41-
case initError
42-
case cancelled
43-
case service
44-
case other(Int)
25+
case internalError
4526
}
4627

4728
fileprivate var value: Value
4829
private init(_ value: Value) {
4930
self.value = value
5031
}
5132

52-
public static var invalidQuery: Self { Self(.invalidQuery) }
53-
54-
public static var serverFailure: Self { Self(.serverFailure) }
55-
56-
public static var notFound: Self { Self(.notFound) }
57-
58-
public static var notImplemented: Self { Self(.notImplemented) }
59-
60-
public static var serverRefused: Self { Self(.serverRefused) }
61-
33+
/// The query was badly formed.
6234
public static var badQuery: Self { Self(.badQuery) }
6335

64-
public static var badName: Self { Self(.badName) }
65-
66-
public static var badFamily: Self { Self(.badFamily) }
67-
36+
/// The response couldn't be parsed.
6837
public static var badResponse: Self { Self(.badResponse) }
6938

39+
/// The server refused to accept a connection.
7040
public static var connectionRefused: Self { Self(.connectionRefused) }
7141

42+
/// The query timed out.
7243
public static var timeout: Self { Self(.timeout) }
7344

74-
public static var eof: Self { Self(.eof) }
75-
76-
public static var fileIO: Self { Self(.fileIO) }
77-
78-
public static var noMemory: Self { Self(.noMemory) }
79-
80-
public static var destruction: Self { Self(.destruction) }
81-
82-
public static var badString: Self { Self(.badString) }
83-
84-
public static var badFlags: Self { Self(.badFlags) }
85-
86-
public static var noName: Self { Self(.noName) }
87-
88-
public static var badHints: Self { Self(.badHints) }
89-
90-
public static var notInitialized: Self { Self(.notInitialized) }
91-
92-
public static var initError: Self { Self(.initError) }
93-
94-
public static var cancelled: Self { Self(.cancelled) }
95-
96-
public static var service: Self { Self(.service) }
97-
98-
public static func other(_ code: Int) -> Self {
99-
Self(.other(code))
100-
}
45+
/// An internal error.
46+
public static var internalError: Self { Self(.internalError) }
10147
}
10248

10349
public var code: Code
10450
public var message: String
51+
public var source: Swift.Error?
10552

106-
public init(code: Code, message: String = "") {
53+
public init(code: Code, message: String = "", source: Swift.Error? = nil) {
10754
self.code = code
10855
self.message = message
56+
self.source = source
10957
}
11058

11159
public var description: String {
60+
let name: String
11261
switch self.code.value {
113-
case .invalidQuery:
114-
return "invalid query: \(self.message)"
115-
case .serverFailure:
116-
return "server failure: \(self.message)"
117-
case .notFound:
118-
return "not found: \(self.message)"
119-
case .notImplemented:
120-
return "not implemented: \(self.message)"
121-
case .serverRefused:
122-
return "server refused: \(self.message)"
12362
case .badQuery:
124-
return "bad query: \(self.message)"
125-
case .badName:
126-
return "bad name: \(self.message)"
127-
case .badFamily:
128-
return "bad family: \(self.message)"
63+
name = "bad query"
12964
case .badResponse:
130-
return "bad response: \(self.message)"
65+
name = "bad response"
13166
case .connectionRefused:
132-
return "connection refused: \(self.message)"
67+
name = "connection refused"
13368
case .timeout:
134-
return "timeout: \(self.message)"
135-
case .eof:
136-
return "EOF: \(self.message)"
137-
case .fileIO:
138-
return "file IO: \(self.message)"
139-
case .noMemory:
140-
return "no memory: \(self.message)"
141-
case .destruction:
142-
return "destruction: \(self.message)"
143-
case .badString:
144-
return "bad string: \(self.message)"
145-
case .badFlags:
146-
return "bad flags: \(self.message)"
147-
case .noName:
148-
return "no name: \(self.message)"
149-
case .badHints:
150-
return "bad hints: \(self.message)"
151-
case .notInitialized:
152-
return "not initialized: \(self.message)"
153-
case .initError:
154-
return "initialization error: \(self.message)"
155-
case .cancelled:
156-
return "cancelled: \(self.message)"
157-
case .service:
158-
return "service: \(self.message)"
159-
case .other(let code):
160-
return "other [\(code)]: \(self.message)"
69+
name = "timeout"
70+
case .internalError:
71+
name = "internal"
16172
}
73+
74+
let suffix = self.source.map { " (\($0))" } ?? ""
75+
return "\(name): \(self.message)\(suffix)"
16276
}
16377
}
16478
}
79+
80+
/// An error thrown from c-ares.
81+
public struct CAresError: Error, Hashable, Sendable {
82+
/// The error code.
83+
public var code: Int
84+
85+
public init(code: Int) {
86+
self.code = code
87+
}
88+
}
89+
90+
/// An error thrown from DNSSD.
91+
public struct DNSSDError: Error, Hashable, Sendable {
92+
/// The error code.
93+
public var code: Int
94+
95+
public init(code: Int) {
96+
self.code = code
97+
}
98+
}

Sources/AsyncDNSResolver/c-ares/AresChannel.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,6 @@ class AresChannel {
6767
private func checkAresResult(body: () -> Int32) throws {
6868
let result = body()
6969
guard result == ARES_SUCCESS else {
70-
throw AsyncDNSResolver.Error(code: result, "failed to initialize channel")
70+
throw AsyncDNSResolver.Error(cAresCode: result, "failed to initialize channel")
7171
}
7272
}

Sources/AsyncDNSResolver/c-ares/DNSResolver_c-ares.swift

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -268,7 +268,7 @@ extension Ares {
268268
init<Parser: AresQueryReplyParser>(parser: Parser, _ continuation: CheckedContinuation<Parser.Reply, Error>) {
269269
self._handler = { status, buffer, length in
270270
guard status == ARES_SUCCESS || status == ARES_ENODATA else {
271-
return continuation.resume(throwing: AsyncDNSResolver.Error(code: status))
271+
return continuation.resume(throwing: AsyncDNSResolver.Error(cAresCode: status))
272272
}
273273

274274
do {
@@ -322,7 +322,7 @@ extension Ares {
322322
return []
323323

324324
default:
325-
throw AsyncDNSResolver.Error(code: parseStatus, "failed to parse A query reply")
325+
throw AsyncDNSResolver.Error(cAresCode: parseStatus, "failed to parse A query reply")
326326
}
327327
}
328328
}
@@ -351,7 +351,7 @@ extension Ares {
351351
return []
352352

353353
default:
354-
throw AsyncDNSResolver.Error(code: parseStatus, "failed to parse AAAA query reply")
354+
throw AsyncDNSResolver.Error(cAresCode: parseStatus, "failed to parse AAAA query reply")
355355
}
356356
}
357357
}
@@ -378,7 +378,7 @@ extension Ares {
378378
return NSRecord(nameservers: [])
379379

380380
default:
381-
throw AsyncDNSResolver.Error(code: parseStatus, "failed to parse NS query reply")
381+
throw AsyncDNSResolver.Error(cAresCode: parseStatus, "failed to parse NS query reply")
382382
}
383383
}
384384
}
@@ -402,7 +402,7 @@ extension Ares {
402402
case ARES_ENODATA:
403403
return nil
404404
default:
405-
throw AsyncDNSResolver.Error(code: parseStatus, "failed to parse CNAME query reply")
405+
throw AsyncDNSResolver.Error(cAresCode: parseStatus, "failed to parse CNAME query reply")
406406
}
407407
}
408408
}
@@ -435,7 +435,7 @@ extension Ares {
435435
return nil
436436

437437
default:
438-
throw AsyncDNSResolver.Error(code: parseStatus, "failed to parse SOA query reply")
438+
throw AsyncDNSResolver.Error(cAresCode: parseStatus, "failed to parse SOA query reply")
439439
}
440440
}
441441
}
@@ -464,7 +464,7 @@ extension Ares {
464464
return PTRRecord(names: [])
465465

466466
default:
467-
throw AsyncDNSResolver.Error(code: parseStatus, "failed to parse PTR query record")
467+
throw AsyncDNSResolver.Error(cAresCode: parseStatus, "failed to parse PTR query record")
468468
}
469469
}
470470
}
@@ -496,7 +496,7 @@ extension Ares {
496496
return []
497497

498498
default:
499-
throw AsyncDNSResolver.Error(code: parseStatus, "failed to parse MX query record")
499+
throw AsyncDNSResolver.Error(cAresCode: parseStatus, "failed to parse MX query record")
500500
}
501501
}
502502
}
@@ -528,7 +528,7 @@ extension Ares {
528528
return []
529529

530530
default:
531-
throw AsyncDNSResolver.Error(code: parseStatus, "failed to parse TXT query reply")
531+
throw AsyncDNSResolver.Error(cAresCode: parseStatus, "failed to parse TXT query reply")
532532
}
533533
}
534534
}
@@ -563,7 +563,7 @@ extension Ares {
563563
return []
564564

565565
default:
566-
throw AsyncDNSResolver.Error(code: parseStatus, "failed to parse SRV query reply")
566+
throw AsyncDNSResolver.Error(cAresCode: parseStatus, "failed to parse SRV query reply")
567567
}
568568
}
569569
}
@@ -600,7 +600,7 @@ extension Ares {
600600
return []
601601

602602
default:
603-
throw AsyncDNSResolver.Error(code: parseStatus, "failed to parse NAPTR query reply")
603+
throw AsyncDNSResolver.Error(cAresCode: parseStatus, "failed to parse NAPTR query reply")
604604
}
605605
}
606606
}

Sources/AsyncDNSResolver/c-ares/Errors_c-ares.swift

Lines changed: 11 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -17,56 +17,21 @@ import CAsyncDNSResolver
1717
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
1818
extension AsyncDNSResolver.Error {
1919
/// Create an ``AsyncDNSResolver/AsyncDNSResolver/Error`` from c-ares error code.
20-
init(code: Int32, _ description: String = "") {
21-
switch code {
22-
case ARES_EFORMERR:
23-
self = .init(code: .invalidQuery, message: description)
24-
case ARES_ESERVFAIL:
25-
self = .init(code: .serverFailure, message: description)
26-
case ARES_ENOTFOUND:
27-
self = .init(code: .notFound, message: description)
28-
case ARES_ENOTIMP:
29-
self = .init(code: .notImplemented, message: description)
30-
case ARES_EREFUSED:
31-
self = .init(code: .serverRefused, message: description)
32-
case ARES_EBADQUERY:
33-
self = .init(code: .badQuery, message: description)
34-
case ARES_EBADNAME:
35-
self = .init(code: .badName, message: description)
36-
case ARES_EBADFAMILY:
37-
self = .init(code: .badFamily, message: description)
20+
init(cAresCode: Int32, _ description: String = "") {
21+
self.message = description
22+
self.source = CAresError(code: Int(cAresCode))
23+
24+
switch cAresCode {
25+
case ARES_EFORMERR, ARES_EBADQUERY, ARES_EBADNAME, ARES_EBADFAMILY, ARES_EBADFLAGS:
26+
self.code = .badQuery
3827
case ARES_EBADRESP:
39-
self = .init(code: .badResponse, message: description)
28+
self.code = .badResponse
4029
case ARES_ECONNREFUSED:
41-
self = .init(code: .connectionRefused, message: description)
30+
self.code = .connectionRefused
4231
case ARES_ETIMEOUT:
43-
self = .init(code: .timeout, message: description)
44-
case ARES_EOF:
45-
self = .init(code: .eof, message: description)
46-
case ARES_EFILE:
47-
self = .init(code: .fileIO, message: description)
48-
case ARES_ENOMEM:
49-
self = .init(code: .noMemory, message: description)
50-
case ARES_EDESTRUCTION:
51-
self = .init(code: .destruction, message: description)
52-
case ARES_EBADSTR:
53-
self = .init(code: .badString, message: description)
54-
case ARES_EBADFLAGS:
55-
self = .init(code: .badFlags, message: description)
56-
case ARES_ENONAME:
57-
self = .init(code: .noName, message: description)
58-
case ARES_EBADHINTS:
59-
self = .init(code: .badHints, message: description)
60-
case ARES_ENOTINITIALIZED:
61-
self = .init(code: .notInitialized, message: description)
62-
case ARES_ELOADIPHLPAPI, ARES_EADDRGETNETWORKPARAMS:
63-
self = .init(code: .initError, message: description)
64-
case ARES_ECANCELLED:
65-
self = .init(code: .cancelled, message: description)
66-
case ARES_ESERVICE:
67-
self = .init(code: .service, message: description)
32+
self.code = .timeout
6833
default:
69-
self = .init(code: .other(Int(code)), message: description)
34+
self.code = .internalError
7035
}
7136
}
7237
}

Sources/AsyncDNSResolver/dnssd/DNSResolver_dnssd.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ struct DNSSD {
155155

156156
// Check if query completed successfully
157157
guard _code == kDNSServiceErr_NoError else {
158-
return continuation.finish(throwing: AsyncDNSResolver.Error(code: .other(Int(_code))))
158+
return continuation.finish(throwing: AsyncDNSResolver.Error(dnssdCode: _code))
159159
}
160160

161161
// Read reply from the socket (blocking) then call reply handler
@@ -198,7 +198,7 @@ extension DNSSD {
198198
data = nil
199199
length = 0
200200
default:
201-
return continuation.finish(throwing: AsyncDNSResolver.Error(code: .other(Int(errorCode))))
201+
return continuation.finish(throwing: AsyncDNSResolver.Error(dnssdCode: errorCode))
202202
}
203203

204204
do {

0 commit comments

Comments
 (0)