forked from vapor/postgres-nio
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathPostgresCodable.swift
195 lines (170 loc) · 8.53 KB
/
PostgresCodable.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
import NIOCore
import class Foundation.JSONEncoder
import class Foundation.JSONDecoder
/// A type that can encode itself to a postgres wire binary representation.
public protocol PostgresEncodable {
// TODO: Rename to `PostgresThrowingEncodable` with next major release
/// identifies the data type that we will encode into `byteBuffer` in `encode`
static var psqlType: PostgresDataType { get }
/// identifies the postgres format that is used to encode the value into `byteBuffer` in `encode`
static var psqlFormat: PostgresFormat { get }
/// Encode the entity into the `byteBuffer` in Postgres binary format, without setting
/// the byte count. This method is called from the ``PostgresBindings``.
func encode<JSONEncoder: PostgresJSONEncoder>(into byteBuffer: inout ByteBuffer, context: PostgresEncodingContext<JSONEncoder>) throws
}
/// A type that can encode itself to a postgres wire binary representation. It enforces that the
/// ``PostgresEncodable/encode(into:context:)-1jkcp`` does not throw. This allows users
/// to create ``PostgresQuery``s using the `ExpressibleByStringInterpolation` without
/// having to spell `try`.
public protocol PostgresNonThrowingEncodable: PostgresEncodable {
// TODO: Rename to `PostgresEncodable` with next major release
func encode<JSONEncoder: PostgresJSONEncoder>(into byteBuffer: inout ByteBuffer, context: PostgresEncodingContext<JSONEncoder>)
}
/// A type that can decode itself from a postgres wire binary representation.
///
/// If you want to conform a type to PostgresDecodable you must implement the decode method.
public protocol PostgresDecodable {
/// A type definition of the type that actually implements the PostgresDecodable protocol. This is an escape hatch to
/// prevent a cycle in the conformace of the Optional type to PostgresDecodable.
///
/// String? should be PostgresDecodable, String?? should not be PostgresDecodable
associatedtype _DecodableType: PostgresDecodable = Self
/// Create an entity from the `byteBuffer` in postgres wire format
///
/// - Parameters:
/// - byteBuffer: A `ByteBuffer` to decode. The byteBuffer is sliced in such a way that it is expected
/// that the complete buffer is consumed for decoding
/// - type: The postgres data type. Depending on this type the `byteBuffer`'s bytes need to be interpreted
/// in different ways.
/// - format: The postgres wire format. Can be `.text` or `.binary`
/// - context: A `PSQLDecodingContext` providing context for decoding. This includes a `JSONDecoder`
/// to use when decoding json and metadata to create better errors.
/// - Returns: A decoded object
init<JSONDecoder: PostgresJSONDecoder>(
from byteBuffer: inout ByteBuffer,
type: PostgresDataType,
format: PostgresFormat,
context: PostgresDecodingContext<JSONDecoder>
) throws
/// Decode an entity from the `byteBuffer` in postgres wire format. This method has a default implementation and
/// is only overwritten for `Optional`s. Other than in the
static func _decodeRaw<JSONDecoder: PostgresJSONDecoder>(
from byteBuffer: inout ByteBuffer?,
type: PostgresDataType,
format: PostgresFormat,
context: PostgresDecodingContext<JSONDecoder>
) throws -> Self
}
extension PostgresDecodable {
@inlinable
public static func _decodeRaw<JSONDecoder: PostgresJSONDecoder>(
from byteBuffer: inout ByteBuffer?,
type: PostgresDataType,
format: PostgresFormat,
context: PostgresDecodingContext<JSONDecoder>
) throws -> Self {
guard var buffer = byteBuffer else {
throw PostgresDecodingError.Code.missingData
}
return try self.init(from: &buffer, type: type, format: format, context: context)
}
}
/// A type that can be encoded into and decoded from a postgres binary format
public typealias PostgresCodable = PostgresEncodable & PostgresDecodable
extension PostgresEncodable {
@inlinable
func encodeRaw<JSONEncoder: PostgresJSONEncoder>(
into buffer: inout ByteBuffer,
context: PostgresEncodingContext<JSONEncoder>
) throws {
// The length of the parameter value, in bytes (this count does not include
// itself). Can be zero.
let lengthIndex = buffer.writerIndex
buffer.writeInteger(0, as: Int32.self)
let startIndex = buffer.writerIndex
// The value of the parameter, in the format indicated by the associated format
// code. n is the above length.
try self.encode(into: &buffer, context: context)
// overwrite the empty length, with the real value
buffer.setInteger(numericCast(buffer.writerIndex - startIndex), at: lengthIndex, as: Int32.self)
}
}
extension PostgresNonThrowingEncodable {
@inlinable
func encodeRaw<JSONEncoder: PostgresJSONEncoder>(
into buffer: inout ByteBuffer,
context: PostgresEncodingContext<JSONEncoder>
) {
// The length of the parameter value, in bytes (this count does not include
// itself). Can be zero.
let lengthIndex = buffer.writerIndex
buffer.writeInteger(0, as: Int32.self)
let startIndex = buffer.writerIndex
// The value of the parameter, in the format indicated by the associated format
// code. n is the above length.
self.encode(into: &buffer, context: context)
// overwrite the empty length, with the real value
buffer.setInteger(numericCast(buffer.writerIndex - startIndex), at: lengthIndex, as: Int32.self)
}
}
/// A context that is passed to Swift objects that are encoded into the Postgres wire format. Used
/// to pass further information to the encoding method.
public struct PostgresEncodingContext<JSONEncoder: PostgresJSONEncoder> {
/// A ``PostgresJSONEncoder`` used to encode the object to json.
public var jsonEncoder: JSONEncoder
/// Creates a ``PostgresEncodingContext`` with the given ``PostgresJSONEncoder``. In case you want
/// to use the a ``PostgresEncodingContext`` with an unconfigured Foundation `JSONEncoder`
/// you can use the ``default`` context instead.
///
/// - Parameter jsonEncoder: A ``PostgresJSONEncoder`` to use when encoding objects to json
public init(jsonEncoder: JSONEncoder) {
self.jsonEncoder = jsonEncoder
}
}
extension PostgresEncodingContext where JSONEncoder == Foundation.JSONEncoder {
/// A default ``PostgresEncodingContext`` that uses a Foundation `JSONEncoder`.
public static let `default` = PostgresEncodingContext(jsonEncoder: JSONEncoder())
}
/// A context that is passed to Swift objects that are decoded from the Postgres wire format. Used
/// to pass further information to the decoding method.
public struct PostgresDecodingContext<JSONDecoder: PostgresJSONDecoder> {
/// A ``PostgresJSONDecoder`` used to decode the object from json.
public var jsonDecoder: JSONDecoder
/// Creates a ``PostgresDecodingContext`` with the given ``PostgresJSONDecoder``. In case you want
/// to use the a ``PostgresDecodingContext`` with an unconfigured Foundation `JSONDecoder`
/// you can use the ``default`` context instead.
///
/// - Parameter jsonDecoder: A ``PostgresJSONDecoder`` to use when decoding objects from json
public init(jsonDecoder: JSONDecoder) {
self.jsonDecoder = jsonDecoder
}
}
extension PostgresDecodingContext where JSONDecoder == Foundation.JSONDecoder {
/// A default ``PostgresDecodingContext`` that uses a Foundation `JSONDecoder`.
public static let `default` = PostgresDecodingContext(jsonDecoder: Foundation.JSONDecoder())
}
extension Optional: PostgresDecodable where Wrapped: PostgresDecodable, Wrapped._DecodableType == Wrapped {
public typealias _DecodableType = Wrapped
public init<JSONDecoder: PostgresJSONDecoder>(
from byteBuffer: inout ByteBuffer,
type: PostgresDataType,
format: PostgresFormat,
context: PostgresDecodingContext<JSONDecoder>
) throws {
preconditionFailure("This should not be called")
}
@inlinable
public static func _decodeRaw<JSONDecoder : PostgresJSONDecoder>(
from byteBuffer: inout ByteBuffer?,
type: PostgresDataType,
format: PostgresFormat,
context: PostgresDecodingContext<JSONDecoder>
) throws -> Optional<Wrapped> {
switch byteBuffer {
case .some(var buffer):
return try Wrapped(from: &buffer, type: type, format: format, context: context)
case .none:
return .none
}
}
}