-
Notifications
You must be signed in to change notification settings - Fork 23
/
Copy pathCodingStrategies.swift
218 lines (189 loc) · 8.5 KB
/
CodingStrategies.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
import Foundation
/// Protocol indicating a set of options can be used to configure `BSONEncoder` and `BSONDecoder`.
public protocol CodingStrategyProvider {
/// Specifies the strategy to use when converting `Date`s between their BSON representations and their
/// representations in (non `BSONDocument`) `Codable` types.
var dateCodingStrategy: DateCodingStrategy? { get }
/// Specifies the strategy to use when converting `UUID`s between their BSON representations and their
/// representations in (non `BSONDocument`) `Codable` types.
var uuidCodingStrategy: UUIDCodingStrategy? { get }
/// Specifies the strategy to use when converting `Data`s between their BSON representations and their
/// representations in (non `BSONDocument`) `Codable` types.
var dataCodingStrategy: DataCodingStrategy? { get }
}
/// Options struct used for configuring the coding strategies on `BSONEncoder` and `BSONDecoder`.
public struct BSONCoderOptions: CodingStrategyProvider {
public var dataCodingStrategy: DataCodingStrategy?
public var dateCodingStrategy: DateCodingStrategy?
public var uuidCodingStrategy: UUIDCodingStrategy?
/// Initializes a new `BSONCoderOptions`.
public init(
dataCodingStrategy: DataCodingStrategy? = nil,
dateCodingStrategy: DateCodingStrategy? = nil,
uuidCodingStrategy: UUIDCodingStrategy? = nil
) {
self.dataCodingStrategy = dataCodingStrategy
self.dateCodingStrategy = dateCodingStrategy
self.uuidCodingStrategy = uuidCodingStrategy
}
}
/**
* Enum representing the various encoding/decoding strategy pairs for `Date`s.
* Set these on a `MongoClient`, `MongoDatabase`, or `MongoCollection` so that the strategies will be applied when
* converting `Date`s between their BSON representations and their representations in (non `BSONDocument`) `Codable`
* types.
*
* As per the BSON specification, the default strategy is to encode `Date`s as BSON datetime objects.
*
* - SeeAlso: bsonspec.org
*/
public enum DateCodingStrategy: RawRepresentable {
public typealias RawValue = (encoding: BSONEncoder.DateEncodingStrategy, decoding: BSONDecoder.DateDecodingStrategy)
/// Encode/decode the `Date` by deferring to its default encoding/decoding implementations.
case deferredToDate
/// Encode/decode the `Date` to/from a BSON datetime object (default).
case bsonDateTime
/// Encode/decode the `Date` to/from a 64-bit integer counting the number of milliseconds since January 1, 1970.
case millisecondsSince1970
/// Encode/decode the `Date` to/from a BSON double counting the number of seconds since January 1, 1970.
case secondsSince1970
/// Encode/decode the `Date` to/from an ISO-8601-formatted string (in RFC 339 format).
case iso8601
/// Encode/decode the `Date` to/from a string formatted by the given formatter.
case formatted(DateFormatter)
/// Encode the `Date` by using the given `encodeFunc`. Decode the `Date` by using the given `decodeFunc`.
/// If `encodeFunc` does not encode a value, an empty document will be encoded in its place.
case custom(encodeFunc: (Date, Encoder) throws -> Void, decodeFunc: (Decoder) throws -> Date)
public init?(rawValue: RawValue) {
switch rawValue {
case (.deferredToDate, .deferredToDate):
self = .deferredToDate
case (.bsonDateTime, .bsonDateTime):
self = .bsonDateTime
case (.millisecondsSince1970, .millisecondsSince1970):
self = .millisecondsSince1970
case (.secondsSince1970, .secondsSince1970):
self = .secondsSince1970
case (.iso8601, .iso8601):
self = .iso8601
case let (.formatted(encodingFormatter), .formatted(decodingFormatter)):
guard encodingFormatter == decodingFormatter else {
return nil
}
self = .formatted(encodingFormatter)
case let (.custom(encodeFunc), .custom(decodeFunc)):
self = .custom(encodeFunc: encodeFunc, decodeFunc: decodeFunc)
default:
return nil
}
}
public var rawValue: RawValue {
switch self {
case .deferredToDate:
return (.deferredToDate, .deferredToDate)
case .bsonDateTime:
return (.bsonDateTime, .bsonDateTime)
case .millisecondsSince1970:
return (.millisecondsSince1970, .millisecondsSince1970)
case .secondsSince1970:
return (.secondsSince1970, .secondsSince1970)
case .iso8601:
return (.iso8601, .iso8601)
case let .formatted(formatter):
return (.formatted(formatter), .formatted(formatter))
case let .custom(encodeFunc, decodeFunc):
return (.custom(encodeFunc), .custom(decodeFunc))
}
}
}
/**
* Enum representing the various encoding/decoding strategy pairs for `Date`s.
* Set these on a `MongoClient`, `MongoDatabase`, or `MongoCollection` so that the strategies will be applied when
* converting `UUID`s between their BSON representations and their representations in (non `BSONDocument`) `Codable`
* types.
*
* As per the BSON specification, the default strategy is to encode `UUID`s as BSON binary types with the UUID
* subtype.
*
* - SeeAlso: bsonspec.org
*/
public enum UUIDCodingStrategy: RawRepresentable {
public typealias RawValue = (encoding: BSONEncoder.UUIDEncodingStrategy, decoding: BSONDecoder.UUIDDecodingStrategy)
/// Encode/decode the `UUID` by deferring to its default encoding/decoding implementations.
case deferredToUUID
/// Encode/decode the `UUID` to/from a BSON binary type (default).
case binary
public init?(rawValue: RawValue) {
switch rawValue {
case (.deferredToUUID, .deferredToUUID):
self = .deferredToUUID
case (.binary, .binary):
self = .binary
default:
return nil
}
}
public var rawValue: RawValue {
switch self {
case .deferredToUUID:
return (.deferredToUUID, .deferredToUUID)
case .binary:
return (.binary, .binary)
}
}
}
/**
* Enum representing the various encoding/decoding strategy pairs for `Date`s.
* Set these on a `MongoClient`, `MongoDatabase`, or `MongoCollection` so that the strategies will be applied when
* converting `Data`s between their BSON representations and their representations in (non `BSONDocument`) `Codable`
* types.
*
* As per the BSON specification, the default strategy is to encode `Data`s as BSON binary types with the generic
* binary subtype.
*
* - SeeAlso: bsonspec.org
*/
public enum DataCodingStrategy: RawRepresentable {
public typealias RawValue = (encoding: BSONEncoder.DataEncodingStrategy, decoding: BSONDecoder.DataDecodingStrategy)
/**
* Encode/decode the `Data` by deferring to its default encoding implementations.
*
* Note: The default encoding implementation attempts to encode the `Data` as a `[UInt8]`, but because BSON
* does not support integer types besides `Int32` or `Int64`, it actually gets encoded to BSON as an `[Int32]`.
* This results in a space inefficient storage of the `Data` (using 4 bytes of BSON storage per byte of data).
*/
case deferredToData
/// Encode/decode the `Data` to/from a BSON binary type (default).
case binary
/// Encode the `Data` to/from a base64 encoded string.
case base64
/// Encode the `Data` by using the given `encodeFunc`. Decode the `Data` by using the given `decodeFunc`.
/// If `encodeFunc` does not encode a value, an empty document will be encoded in its place.
case custom(encodeFunc: (Data, Encoder) throws -> Void, decodeFunc: (Decoder) throws -> Data)
public init?(rawValue: RawValue) {
switch rawValue {
case (.deferredToData, .deferredToData):
self = .deferredToData
case (.binary, .binary):
self = .binary
case (.base64, .base64):
self = .base64
case let (.custom(encodeFunc), .custom(decodeFunc)):
self = .custom(encodeFunc: encodeFunc, decodeFunc: decodeFunc)
default:
return nil
}
}
public var rawValue: RawValue {
switch self {
case .deferredToData:
return (.deferredToData, .deferredToData)
case .binary:
return (.binary, .binary)
case .base64:
return (.base64, .base64)
case let .custom(encodeFunc, decodeFunc):
return (.custom(encodeFunc), .custom(decodeFunc))
}
}
}