Skip to content

Commit 4877029

Browse files
authored
SWIFT-502 Reimplement errors as structs (#374)
1 parent f93982f commit 4877029

File tree

62 files changed

+746
-733
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

62 files changed

+746
-733
lines changed

Guides/BSON.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ func foo(x: BSON, y: BSON) throws {
6464
print("got something else")
6565
}
6666
guard case let .double(d) = y else {
67-
throw UserError.invalidArgumentError(message: "y must be a double")
67+
throw InvalidArgumentError(message: "y must be a double")
6868
}
6969
print(d * d)
7070
}

Guides/Error-Handling.md

+30-27
Original file line numberDiff line numberDiff line change
@@ -14,53 +14,56 @@
1414
* [See Also](#see-also)
1515

1616
## Error Types
17-
The driver uses errors to communicate that an operation failed, an assumption wasn't met, or that the user did something incorrectly. Applications that use the driver can in turn catch these errors and respond appropriately without crashing or resulting in an otherwise inconsistent state. To correctly model the different sources of errors, the driver defines three separate types of errors (`ServerError`, `UserError`, `RuntimeError`), each of which conforms to the `MongoError` protocol. These errors are defined in `MongoError.swift` and are outlined here. The documentation for every public function that throws lists some of the errors that could possibly be thrown and the reasons they might be. The errors listed there are not comprehensive but will generally cover the most common cases.
17+
The driver uses errors to communicate that an operation failed, an assumption wasn't met, or that the user did something incorrectly. Applications that use the driver can in turn catch these errors and respond appropriately without crashing or resulting in an otherwise inconsistent state. To correctly model the different sources of errors, the driver defines three separate caregories of errors (`ServerError`, `UserError`, `RuntimeError`), each of which are protocols that inherit from the `MongoError` protocol. These protocols are defined in `MongoError.swift`, and the structs that conform to them are outlined here. The documentation for every public function that throws lists some of the errors that could possibly be thrown and the reasons they might be. The errors listed there are not comprehensive but will generally cover the most common cases.
1818

19-
**Error Labels:** Some types of errors may contain more specific information describing the context in which they occured. This information is conveyed through the usage of `errorLabels`. Specifically, any server error or connection related error may contain labels.
19+
**Error Labels:** Some types of errors may contain more specific information describing the context in which they occured. Such errors conform to the `LabeledError` protocol, and the extra information is conveyed through the `errorLabels` property. Specifically, any server error or connection related error may contain labels.
2020

2121
### Server Errors
22-
Server errors correspond to failures that occur in the database itself and are returned to the driver via some response to a command. Each `ServerError` case contains at least one error code representing what went wrong on the server.
22+
Server errors correspond to failures that occur in the database itself and are returned to the driver via some response to a command. Each error that conforms to `ServerError` contains at least one error code representing what went wrong on the server.
2323

24-
For an enumeration of the possible server error codes, [see this list](https://github.com/mongodb/mongo/blob/master/src/mongo/base/error_codes.err).
24+
For an enumeration of the possible server error codes, [see this list](https://github.com/mongodb/mongo/blob/master/src/mongo/base/error_codes.yml).
2525

26-
The `ServerError` cases are as follows:
27-
- `.commandError(code: ServerErrorCode, message: String, errorLabels: [String]?)`:
26+
The possible errors that conform to `ServerError` are as follows:
27+
- `CommandError`:
2828
- Thrown when commands experience errors server side that prevent execution.
2929
- Example command failures include failure to parse, operation aborted by the user, and unexpected errors during execution.
30-
- `.writeError(writeError: WriteError?, writeConcernError: WriteConcernError?, errorLabels: [String]?)`
31-
- Thrown when a single write command fails on the server.
32-
- Only one of the two optionals will be non-nil.
33-
- `.bulkWriteError(writeErrors: [BulkWriteError]?, writeConcernError: WriteConcernError?, result: BulkWriteResult, errorLabels: [String]?)`
30+
- `WriteError`
31+
- Thrown when a single write command fails on the server (e.g. insertOne, updateOne, updateMany).
32+
- `BulkWriteError`
3433
- Thrown when the server returns errors as part of an executed bulk write.
35-
- If WriteConcernError is populated, writeErrors may not be.
36-
- **Note:** `InsertMany` throws a `.bulkWriteError`, _not_ a `.writeError`.
34+
- If WriteConcernFailure is populated, writeErrors may not be.
35+
- **Note:** `InsertMany` throws a `BulkWriteError`, _not_ a `WriteError`.
3736

3837

3938
### User Errors
4039
User applications can sometimes cause errors by using the driver incorrectly (e.g. by passing invalid argument combinations). This category of error covers those cases.
4140

42-
The `UserError` cases are as follows:
43-
- `.logicError(message: String)`
41+
The possible errors that conform to `UserError` are as follows:
42+
- `LogicError`
4443
- Thrown when the user uses the driver incorrectly (e.g. advancing a dead cursor).
45-
- `.invalidArgument(message: String)`
44+
- `InvalidArgumentError`
4645
- Thrown when user passes invalid arguments to some driver function.
4746

4847

4948
### Runtime Errors
5049
The driver may experience errors that happen at runtime unexpectedly. These errors don't fit neatly into the categories of occurring only server-side or only as part of the user's fault, so they are represented by their own set of cases.
5150

5251
The `RuntimeError` cases are as follows:
53-
- `.internalError(message: String)`
52+
- `InternalError`
5453
- Thrown when something is null when it shouldn't be, the driver has an internal failure, or MongoSwift cannot understand a server response.
55-
- This is generally indicative of a bug somewhere in the driver stack or a system related failure (e.g. memory allocation failure). If you experience an error that you think is the result of a bug, please file a bug report on GitHub or our Jira project.
56-
- `.connectionError(message: String, errorLabels: [String]?)`
54+
- This is generally indicative of a bug somewhere in the driver stack or a system related failure (e.g. a memory allocation failure). If you experience an error that you think is the result of a bug, please file a bug report on GitHub or our Jira project.
55+
- `ConnectionError`
5756
- Thrown during any connection establishment / socket related errors.
58-
- `.authenticationError(message: String)`
57+
- This error also conforms to `LabeledError`.
58+
- `AuthenticationError`
5959
- Thrown when the driver is not authorized to perform a requested command (e.g. due to invalid credentials)
60+
- `ServerSelectionError`
61+
- Thrown when the driver was unable to select a server for an operation (e.g. due to a timeout or unsatisfiable read preference)
62+
- See [the official MongoDB documentation](https://docs.mongodb.com/manual/core/read-preference-mechanics/) for more information.
6063

6164

6265
### Encoding/Decoding Errors
63-
As part of the driver, `BSONEncoder` and `BSONDecoder` are implemented according to the `Encoder` and `Decoder` protocols [defined in Apple's Foundation](https://developer.apple.com/documentation/foundation/archives_and_serialization/encoding_and_decoding_custom_types). User applications can use them to seamlessly convert between their Swift data structures and the BSON documents stored in the database. While this functionality is part of the public API, the driver itself also makes heavy use of it internally. During any encoding or decoding operations, errors can occur that prevent the data from being written to or read from BSON. In these cases, the driver throws an `EncodingError` or `DecodingError` as appropriate. These error types are not unique to MongoSwift and are commonly used by other encoder implementations, such as Foundation's `JSONEncoder`, so they do not conform to the `MongoError` protocol.
66+
As part of the driver, `BSONEncoder` and `BSONDecoder` are implemented according to the `Encoder` and `Decoder` protocols [defined in Apple's Foundation](https://developer.apple.com/documentation/foundation/archives_and_serialization/encoding_and_decoding_custom_types). User applications can use them to seamlessly convert between their Swift data structures and the BSON documents stored in the database. While this functionality is part of the public API, the driver itself also makes heavy use of it internally. During any encoding or decoding operations, errors can occur that prevent the data from being written to or read from BSON. In these cases, the driver throws an `EncodingError` or `DecodingError` as appropriate. These error types are not unique to MongoSwift and are commonly used by other encoder implementations, such as Foundation's `JSONEncoder`, so they do not conform to the `MongoError` protocol or any of the other error protocols defined in the driver.
6467

6568
See the official documentation for both [`EncodingErrors`](https://developer.apple.com/documentation/swift/encodingerror) and [`DecodingErrors`](https://developer.apple.com/documentation/swift/decodingerror) for more information.
6669

@@ -93,8 +96,8 @@ do {
9396
```swift
9497
do {
9598
try db.runCommand(["asdfasdf": "sadfsadfasdf"])
96-
} catch let ServerError.commandError(code, message, _) {
97-
print("Command failed: code: \(code) message: \(message)")
99+
} catch let commandError as CommandError {
100+
print("Command failed: code: \(commandError.code) message: \(commandError.message)")
98101
} catch { ... }
99102
```
100103
Output:
@@ -108,8 +111,8 @@ Command failed: code: 59 message: no such command: 'asdfasdf'
108111
do {
109112
try coll.insertOne(["_id": 1])
110113
try coll.insertOne(["_id": 1])
111-
} catch let ServerError.writeError(writeError, _, _) where writeError?.code == 11000 {
112-
print("duplicate key error: \(1) \(writeError?.message ?? "")")
114+
} catch let writeError as WriteError where writeError.writeFailure?.code == 11000 {
115+
print("duplicate key error: \(1) \(writeError.writeFailure?.message ?? "")")
113116
}
114117
```
115118
Output:
@@ -123,11 +126,11 @@ let docs: [Document] = [["_id": 2], ["_id": 1]]
123126
do {
124127
try coll.insertOne(["_id": 1])
125128
try coll.insertMany(docs)
126-
} catch let ServerError.bulkWriteError(writeErrors, _, result, _) {
127-
if let writeErrors = writeErrors {
129+
} catch let bwe as BulkWriteError {
130+
if let writeErrors = bwe.writeFailures {
128131
writeErrors.forEach { err in print("Write Error inserting \(docs[err.index]), code: \(err.code), message: \(err.message)") }
129132
}
130-
if let result = result {
133+
if let result = bwe.result {
131134
print("Result: ")
132135
print("nInserted: \(result.insertedCount)")
133136
print("InsertedIds: \(result.insertedIds)")

Sources/MongoSwift/APM.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ public struct CommandFailedEvent: MongoCommandEvent, InitializableFromOpaquePoin
136136
var error = bson_error_t()
137137
mongoc_apm_command_failed_get_error(event, &error)
138138
let reply = Document(copying: mongoc_apm_command_failed_get_reply(event))
139-
self.failure = extractMongoError(error: error, reply: reply) // should always return a ServerError.commandError
139+
self.failure = extractMongoError(error: error, reply: reply) // should always return a CommandError
140140
self.requestId = mongoc_apm_command_failed_get_request_id(event)
141141
self.operationId = mongoc_apm_command_failed_get_operation_id(event)
142142
self.connectionId = ConnectionId(mongoc_apm_command_failed_get_host(event))

0 commit comments

Comments
 (0)