forked from vapor/postgres-kit
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathPostgreSQLDataDecoder.swift
145 lines (124 loc) · 5.28 KB
/
PostgreSQLDataDecoder.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
struct PostgreSQLDataDecoder {
/// Creates a new `PostgreSQLDataDecoder`.
init() {}
func decode<D>(_ type: D.Type, from data: PostgreSQLData) throws -> D where D: Decodable {
if let convertible = type as? PostgreSQLDataConvertible.Type {
return try convertible.convertFromPostgreSQLData(data) as! D
}
return try D(from: _Decoder(data: data))
}
// MARK: Private
private struct _Decoder: Decoder {
let codingPath: [CodingKey] = []
var userInfo: [CodingUserInfoKey: Any] = [:]
let data: PostgreSQLData
init(data: PostgreSQLData) {
self.data = data
}
struct DecoderUnwrapper: Decodable {
let decoder: Decoder
init(from decoder: Decoder) throws {
self.decoder = decoder
}
}
struct JSON { }
func container<Key>(keyedBy type: Key.Type) throws -> KeyedDecodingContainer<Key> where Key : CodingKey {
let json: Data
switch data.type {
case .jsonb, .json:
switch data.storage {
case .binary(let data):
assert(data[data.startIndex] == 0x01, "invalid JSONB data format")
json = data.advanced(by: 1)
case .text(let string): json = Data(string.utf8)
default: throw PostgreSQLError.decode(JSON.self, from: data)
}
default: throw PostgreSQLError.decode(JSON.self, from: data)
}
let unwrapper = try JSONDecoder().decode(DecoderUnwrapper.self, from: json)
return try unwrapper.decoder.container(keyedBy: Key.self)
}
func unkeyedContainer() throws -> UnkeyedDecodingContainer {
switch data.storage {
case .binary(var value):
/// Extract and convert each element.
var array: [PostgreSQLData] = []
let hasData = value.extract(Int32.self).bigEndian
if hasData == 1 {
/// Unknown
let _ = value.extract(Int32.self).bigEndian
/// The big-endian array element type
let type: PostgreSQLDataFormat = .init(value.extract(Int32.self).bigEndian)
/// The big-endian length of the array
let count = value.extract(Int32.self).bigEndian
/// The big-endian number of dimensions
let _ = value.extract(Int32.self).bigEndian
for _ in 0..<count {
let count = Int(value.extract(Int32.self).bigEndian)
let subValue = value.extract(count: count)
let psqlData = PostgreSQLData(type, binary: subValue)
array.append(psqlData)
}
} else {
array = []
}
return _UnkeyedDecodingContainer(data: array)
default: throw PostgreSQLError(identifier: "array", reason: "Cannot decode array from: \(data).")
}
}
func singleValueContainer() throws -> SingleValueDecodingContainer {
return _SingleValueDecodingContainer(data: data)
}
}
private struct _UnkeyedDecodingContainer: UnkeyedDecodingContainer {
let codingPath: [CodingKey] = []
let data: [PostgreSQLData]
var count: Int? {
return data.count
}
var isAtEnd: Bool {
return currentIndex >= data.count
}
var currentIndex: Int
init(data: [PostgreSQLData]) {
self.data = data
self.currentIndex = 0
}
mutating func decodeNil() throws -> Bool {
defer { currentIndex += 1 }
return data[currentIndex].isNull
}
mutating func decode<T>(_ type: T.Type) throws -> T where T : Decodable {
defer { currentIndex += 1 }
return try PostgreSQLDataDecoder().decode(T.self, from: data[currentIndex])
}
mutating func nestedContainer<NestedKey>(keyedBy type: NestedKey.Type) throws -> KeyedDecodingContainer<NestedKey> where NestedKey : CodingKey {
fatalError()
}
mutating func nestedUnkeyedContainer() throws -> UnkeyedDecodingContainer {
fatalError()
}
mutating func superDecoder() throws -> Decoder {
fatalError()
}
}
private struct _SingleValueDecodingContainer: SingleValueDecodingContainer {
let codingPath: [CodingKey] = []
let data: PostgreSQLData
init(data: PostgreSQLData) {
self.data = data
}
public func decodeNil() -> Bool {
switch data.storage {
case .null: return true
default: return false
}
}
func decode<T>(_ type: T.Type) throws -> T where T : Decodable {
guard let convertible = type as? PostgreSQLDataConvertible.Type else {
return try T(from: _Decoder(data: data))
}
return try convertible.convertFromPostgreSQLData(data) as! T
}
}
}