Skip to content

Commit 506db1b

Browse files
authored
SWIFT-178 Conform various options/result types to Decodable (#269)
1 parent beaeca3 commit 506db1b

File tree

8 files changed

+131
-64
lines changed

8 files changed

+131
-64
lines changed

Sources/MongoSwift/MongoClient.swift

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import Foundation
22
import mongoc
33

44
/// Options to use when creating a `MongoClient`.
5-
public struct ClientOptions: CodingStrategyProvider {
5+
public struct ClientOptions: CodingStrategyProvider, Decodable {
66
/// Determines whether the client should retry supported write operations.
77
public let retryWrites: Bool?
88

@@ -14,24 +14,32 @@ public struct ClientOptions: CodingStrategyProvider {
1414
/// be used.
1515
public let readConcern: ReadConcern?
1616

17-
/// Specifies a ReadPreference to use for the client.
18-
public let readPreference: ReadPreference?
19-
2017
/// Specifies a WriteConcern to use for the client. If one is not specified, the server's default write concern
2118
/// will be used.
2219
public let writeConcern: WriteConcern?
2320

21+
// swiftlint:disable redundant_optional_initialization
22+
23+
/// Specifies a ReadPreference to use for the client.
24+
public var readPreference: ReadPreference? = nil
25+
2426
/// Specifies the `DateCodingStrategy` to use for BSON encoding/decoding operations performed by this client and any
2527
/// databases or collections that derive from it.
26-
public let dateCodingStrategy: DateCodingStrategy?
28+
public var dateCodingStrategy: DateCodingStrategy? = nil
2729

2830
/// Specifies the `UUIDCodingStrategy` to use for BSON encoding/decoding operations performed by this client and any
2931
/// databases or collections that derive from it.
30-
public let uuidCodingStrategy: UUIDCodingStrategy?
32+
public var uuidCodingStrategy: UUIDCodingStrategy? = nil
3133

3234
/// Specifies the `DataCodingStrategy` to use for BSON encoding/decoding operations performed by this client and any
3335
/// databases or collections that derive from it.
34-
public let dataCodingStrategy: DataCodingStrategy?
36+
public var dataCodingStrategy: DataCodingStrategy? = nil
37+
38+
// swiftlint:enable redundant_optional_initialization
39+
40+
private enum CodingKeys: CodingKey {
41+
case retryWrites, eventMonitoring, readConcern, writeConcern
42+
}
3543

3644
/// Convenience initializer allowing any/all to be omitted or optional.
3745
public init(eventMonitoring: Bool = false,

Sources/MongoSwift/MongoCollection+BulkWrite.swift

Lines changed: 39 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ extension MongoCollection {
4141
}
4242

4343
/// A model for a `deleteOne` operation within a bulk write.
44-
public struct DeleteOneModel: WriteModel {
44+
public struct DeleteOneModel: WriteModel, Decodable {
4545
/// A `Document` representing the match criteria.
4646
public let filter: Document
4747

@@ -78,7 +78,7 @@ extension MongoCollection {
7878
}
7979

8080
/// A model for a `deleteMany` operation within a bulk write.
81-
public struct DeleteManyModel: WriteModel {
81+
public struct DeleteManyModel: WriteModel, Decodable {
8282
/// A `Document` representing the match criteria.
8383
public let filter: Document
8484

@@ -115,7 +115,7 @@ extension MongoCollection {
115115
}
116116

117117
/// A model for an `insertOne` operation within a bulk write.
118-
public struct InsertOneModel: WriteModel {
118+
public struct InsertOneModel: WriteModel, Decodable {
119119
/// The `CollectionType` to insert.
120120
public let document: CollectionType
121121

@@ -158,7 +158,7 @@ extension MongoCollection {
158158
}
159159

160160
/// A model for a `replaceOne` operation within a bulk write.
161-
public struct ReplaceOneModel: WriteModel {
161+
public struct ReplaceOneModel: WriteModel, Decodable {
162162
/// A `Document` representing the match criteria.
163163
public let filter: Document
164164

@@ -216,7 +216,7 @@ extension MongoCollection {
216216
}
217217

218218
/// A model for an `updateOne` operation within a bulk write.
219-
public struct UpdateOneModel: WriteModel {
219+
public struct UpdateOneModel: WriteModel, Decodable {
220220
/// A `Document` representing the match criteria.
221221
public let filter: Document
222222

@@ -278,7 +278,7 @@ extension MongoCollection {
278278
}
279279

280280
/// A model for an `updateMany` operation within a bulk write.
281-
public struct UpdateManyModel: WriteModel {
281+
public struct UpdateManyModel: WriteModel, Decodable {
282282
/// A `Document` representing the match criteria.
283283
public let filter: Document
284284

@@ -418,7 +418,7 @@ public class BulkWriteOperation: Operation {
418418
}
419419

420420
/// Options to use when performing a bulk write operation on a `MongoCollection`.
421-
public struct BulkWriteOptions: Encodable {
421+
public struct BulkWriteOptions: Codable {
422422
/// If `true`, allows the write to opt-out of document level validation.
423423
public let bypassDocumentValidation: Bool?
424424

@@ -454,7 +454,7 @@ public struct BulkWriteOptions: Encodable {
454454
}
455455

456456
/// The result of a bulk write operation on a `MongoCollection`.
457-
public struct BulkWriteResult {
457+
public struct BulkWriteResult: Decodable {
458458
/// Number of documents deleted.
459459
public let deletedCount: Int
460460

@@ -476,6 +476,37 @@ public struct BulkWriteResult {
476476
/// Map of the index of the operation to the id of the upserted document.
477477
public let upsertedIds: [Int: BSONValue]
478478

479+
private enum CodingKeys: CodingKey {
480+
case deletedCount, insertedCount, insertedIds, matchedCount, modifiedCount, upsertedCount, upsertedIds
481+
}
482+
483+
public init(from decoder: Decoder) throws {
484+
let container = try decoder.container(keyedBy: CodingKeys.self)
485+
486+
// None of the results must be present themselves, but at least one must.
487+
guard !container.allKeys.isEmpty else {
488+
throw DecodingError.valueNotFound(BulkWriteResult.self,
489+
DecodingError.Context(codingPath: decoder.codingPath,
490+
debugDescription: "No results found"))
491+
}
492+
493+
self.deletedCount = try container.decodeIfPresent(Int.self, forKey: .deletedCount) ?? 0
494+
self.matchedCount = try container.decodeIfPresent(Int.self, forKey: .matchedCount) ?? 0
495+
self.modifiedCount = try container.decodeIfPresent(Int.self, forKey: .modifiedCount) ?? 0
496+
497+
let insertedIds =
498+
(try container.decodeIfPresent([Int: AnyBSONValue].self, forKey: .insertedIds) ?? [:])
499+
.mapValues { $0.value }
500+
self.insertedIds = insertedIds
501+
self.insertedCount = try container.decodeIfPresent(Int.self, forKey: .insertedCount) ?? insertedIds.count
502+
503+
let upsertedIds =
504+
(try container.decodeIfPresent([Int: AnyBSONValue].self, forKey: .upsertedIds) ?? [:])
505+
.mapValues { $0.value }
506+
self.upsertedIds = upsertedIds
507+
self.upsertedCount = try container.decodeIfPresent(Int.self, forKey: .upsertedCount) ?? upsertedIds.count
508+
}
509+
479510
/**
480511
* Create a `BulkWriteResult` from a reply and map of inserted IDs.
481512
*

Sources/MongoSwift/MongoCollection+FindAndModify.swift

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -105,11 +105,11 @@ extension MongoCollection {
105105
}
106106

107107
/// Indicates which document to return in a find and modify operation.
108-
public enum ReturnDocument {
108+
public enum ReturnDocument: String, Decodable {
109109
/// Indicates to return the document before the update, replacement, or insert occurred.
110-
case before
110+
case before = "Before"
111111
/// Indicates to return the document after the update, replacement, or insert occurred.
112-
case after
112+
case after = "After"
113113
}
114114

115115
/// Indicates that an options type can be represented as a `FindAndModifyOptions`
@@ -121,7 +121,7 @@ internal protocol FindAndModifyOptionsConvertible {
121121
}
122122

123123
/// Options to use when executing a `findOneAndDelete` command on a `MongoCollection`.
124-
public struct FindOneAndDeleteOptions: FindAndModifyOptionsConvertible {
124+
public struct FindOneAndDeleteOptions: FindAndModifyOptionsConvertible, Decodable {
125125
/// Specifies a collation to use.
126126
public let collation: Document?
127127

@@ -161,7 +161,7 @@ public struct FindOneAndDeleteOptions: FindAndModifyOptionsConvertible {
161161
}
162162

163163
/// Options to use when executing a `findOneAndReplace` command on a `MongoCollection`.
164-
public struct FindOneAndReplaceOptions: FindAndModifyOptionsConvertible {
164+
public struct FindOneAndReplaceOptions: FindAndModifyOptionsConvertible, Decodable {
165165
/// If `true`, allows the write to opt-out of document level validation.
166166
public let bypassDocumentValidation: Bool?
167167

@@ -218,7 +218,7 @@ public struct FindOneAndReplaceOptions: FindAndModifyOptionsConvertible {
218218
}
219219

220220
/// Options to use when executing a `findOneAndUpdate` command on a `MongoCollection`.
221-
public struct FindOneAndUpdateOptions: FindAndModifyOptionsConvertible {
221+
public struct FindOneAndUpdateOptions: FindAndModifyOptionsConvertible, Decodable {
222222
/// A set of filters specifying to which array elements an update should apply.
223223
public let arrayFilters: [Document]?
224224

Sources/MongoSwift/MongoCollection+Read.swift

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ extension MongoCollection {
138138
}
139139

140140
/// An index to "hint" or force MongoDB to use when performing a query.
141-
public enum Hint: Encodable {
141+
public enum Hint: Codable {
142142
/// Specifies an index to use by its name.
143143
case indexName(String)
144144
/// Specifies an index to use by a specification `Document` containing the index key(s).
@@ -153,10 +153,19 @@ public enum Hint: Encodable {
153153
try container.encode(doc)
154154
}
155155
}
156+
157+
public init(from decoder: Decoder) throws {
158+
let container = try decoder.singleValueContainer()
159+
if let str = try? container.decode(String.self) {
160+
self = .indexName(str)
161+
} else {
162+
self = .indexSpec(try container.decode(Document.self))
163+
}
164+
}
156165
}
157166

158167
/// Options to use when executing an `aggregate` command on a `MongoCollection`.
159-
public struct AggregateOptions: Encodable {
168+
public struct AggregateOptions: Codable {
160169
/// Enables writing to temporary files. When set to true, aggregation stages
161170
/// can write data to the _tmp subdirectory in the dbPath directory.
162171
public let allowDiskUse: Bool?
@@ -184,8 +193,10 @@ public struct AggregateOptions: Encodable {
184193
/// A `ReadConcern` to use in read stages of this operation.
185194
public let readConcern: ReadConcern?
186195

196+
// swiftlint:disable redundant_optional_initialization
187197
/// A ReadPreference to use for this operation.
188-
public let readPreference: ReadPreference?
198+
public var readPreference: ReadPreference? = nil
199+
// swiftlint:enable redundant_optional_initialization
189200

190201
/// A `WriteConcern` to use in `$out` stages of this operation.
191202
public let writeConcern: WriteConcern?
@@ -265,7 +276,7 @@ public enum CursorType {
265276
}
266277

267278
/// Options to use when executing a `find` command on a `MongoCollection`.
268-
public struct FindOptions: Encodable {
279+
public struct FindOptions: Codable {
269280
/// Get partial results from a mongos if some shards are down (instead of throwing an error).
270281
public let allowPartialResults: Bool?
271282

@@ -278,9 +289,6 @@ public struct FindOptions: Encodable {
278289
/// Attaches a comment to the query.
279290
public let comment: String?
280291

281-
/// Indicates the type of cursor to use. This value includes both the tailable and awaitData options.
282-
public let cursorType: CursorType?
283-
284292
/// If a `CursorType` is provided, indicates whether it is `.tailable` or .`tailableAwait`.
285293
private let tailable: Bool?
286294

@@ -332,8 +340,15 @@ public struct FindOptions: Encodable {
332340
/// A ReadConcern to use for this operation.
333341
public let readConcern: ReadConcern?
334342

343+
// swiftlint:disable redundant_optional_initialization
344+
335345
/// A ReadPreference to use for this operation.
336-
public let readPreference: ReadPreference?
346+
public var readPreference: ReadPreference? = nil
347+
348+
/// Indicates the type of cursor to use. This value includes both the tailable and awaitData options.
349+
public var cursorType: CursorType? = nil
350+
351+
// swiftlint:enable redundant_optional_initialization
337352

338353
/// Convenience initializer allowing any/all parameters to be omitted or optional.
339354
public init(allowPartialResults: Bool? = nil,

Sources/MongoSwift/MongoCollection+Write.swift

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,7 @@ private extension BulkWriteOptionsConvertible {
233233
// Write command options structs
234234

235235
/// Options to use when executing an `insertOne` command on a `MongoCollection`.
236-
public struct InsertOneOptions: Encodable, BulkWriteOptionsConvertible {
236+
public struct InsertOneOptions: Codable, BulkWriteOptionsConvertible {
237237
/// If true, allows the write to opt-out of document level validation.
238238
public let bypassDocumentValidation: Bool?
239239

@@ -251,7 +251,7 @@ public struct InsertOneOptions: Encodable, BulkWriteOptionsConvertible {
251251
public typealias InsertManyOptions = BulkWriteOptions
252252

253253
/// Options to use when executing an `update` command on a `MongoCollection`.
254-
public struct UpdateOptions: Encodable, BulkWriteOptionsConvertible {
254+
public struct UpdateOptions: Codable, BulkWriteOptionsConvertible {
255255
/// A set of filters specifying to which array elements an update should apply.
256256
public let arrayFilters: [Document]?
257257

@@ -282,7 +282,7 @@ public struct UpdateOptions: Encodable, BulkWriteOptionsConvertible {
282282
}
283283

284284
/// Options to use when executing a `replace` command on a `MongoCollection`.
285-
public struct ReplaceOptions: Encodable, BulkWriteOptionsConvertible {
285+
public struct ReplaceOptions: Codable, BulkWriteOptionsConvertible {
286286
/// If true, allows the write to opt-out of document level validation.
287287
public let bypassDocumentValidation: Bool?
288288

@@ -308,7 +308,7 @@ public struct ReplaceOptions: Encodable, BulkWriteOptionsConvertible {
308308
}
309309

310310
/// Options to use when executing a `delete` command on a `MongoCollection`.
311-
public struct DeleteOptions: Encodable, BulkWriteOptionsConvertible {
311+
public struct DeleteOptions: Codable, BulkWriteOptionsConvertible {
312312
/// Specifies a collation.
313313
public let collation: Document?
314314

@@ -328,7 +328,11 @@ public struct DeleteOptions: Encodable, BulkWriteOptionsConvertible {
328328
// Write command results structs
329329

330330
/// The result of an `insertOne` command on a `MongoCollection`.
331-
public struct InsertOneResult {
331+
public struct InsertOneResult: Decodable {
332+
private enum CodingKeys: String, CodingKey {
333+
case insertedId
334+
}
335+
332336
/// The identifier that was inserted. If the document doesn't have an identifier, this value
333337
/// will be generated and added to the document before insertion.
334338
public let insertedId: BSONValue
@@ -342,6 +346,12 @@ public struct InsertOneResult {
342346
}
343347
self.insertedId = id
344348
}
349+
350+
public init(from decoder: Decoder) throws {
351+
let container = try decoder.container(keyedBy: CodingKeys.self)
352+
let abv = try container.decode(AnyBSONValue.self, forKey: .insertedId)
353+
self.insertedId = abv.value
354+
}
345355
}
346356

347357
/// The result of a multi-document insert operation on a `MongoCollection`.

0 commit comments

Comments
 (0)