@@ -12,16 +12,29 @@ public final class PostgresDataDecoder {
12
12
public func decode< T> ( _ type: T . Type , from data: PostgresData ) throws -> T
13
13
where T: Decodable
14
14
{
15
+ // If `T` can be converted directly, just do so.
15
16
if let convertible = T . self as? PostgresDataConvertible . Type {
16
17
guard let value = convertible. init ( postgresData: data) else {
17
- throw DecodingError . typeMismatch ( T . self, DecodingError . Context . init (
18
+ throw DecodingError . typeMismatch ( T . self, . init(
18
19
codingPath: [ ] ,
19
- debugDescription: " Could not convert to \( T . self) : \( data) "
20
+ debugDescription: " Could not convert PostgreSQL data to \( T . self) : \( data) "
20
21
) )
21
22
}
22
23
return value as! T
23
24
} else {
24
- return try T . init ( from: _Decoder ( data: data, json: self . json) )
25
+ // Probably a Postgres array, JSON array/object, or enum type not using @Enum. See if it can be "unwrapped"
26
+ // as a single-value decoding container, since this is much faster than attempting a JSON decode, or as an
27
+ // array in the Postgres-native sense; this will handle "box" types such as `RawRepresentable` enums while
28
+ // still allowing falling back to JSON.
29
+ do {
30
+ return try T . init ( from: GiftBoxUnwrapDecoder ( decoder: self , data: data) )
31
+ } catch DecodingError . dataCorrupted {
32
+ // Couldn't unwrap it either. Fall back to attempting a JSON decode.
33
+ guard let jsonData = data. jsonb ?? data. json else {
34
+ throw Error . unexpectedDataType ( data. type, expected: " jsonb/json " )
35
+ }
36
+ return try self . json. decode ( T . self, from: jsonData)
37
+ }
25
38
}
26
39
}
27
40
@@ -39,125 +52,77 @@ public final class PostgresDataDecoder {
39
52
}
40
53
}
41
54
42
- final class _Decoder : Decoder {
43
- var codingPath : [ CodingKey ] {
44
- return [ ]
45
- }
46
-
47
- var userInfo : [ CodingUserInfoKey : Any ] {
48
- return [ : ]
49
- }
55
+ private final class GiftBoxUnwrapDecoder : Decoder , SingleValueDecodingContainer {
56
+ var codingPath : [ CodingKey ] { [ ] }
57
+ var userInfo : [ CodingUserInfoKey : Any ] { [ : ] }
50
58
59
+ let dataDecoder : PostgresDataDecoder
51
60
let data : PostgresData
52
- let json : PostgresJSONDecoder
53
61
54
- init ( data: PostgresData , json: PostgresJSONDecoder ) {
62
+ init ( decoder: PostgresDataDecoder , data: PostgresData ) {
63
+ self . dataDecoder = decoder
55
64
self . data = data
56
- self . json = json
65
+ }
66
+
67
+ func container< Key> ( keyedBy type: Key . Type ) throws -> KeyedDecodingContainer < Key > where Key: CodingKey {
68
+ throw DecodingError . dataCorruptedError ( in: self , debugDescription: " Dictionary containers must be JSON-encoded " )
57
69
}
58
70
59
71
func unkeyedContainer( ) throws -> UnkeyedDecodingContainer {
60
- guard let data = self . data. array else {
61
- throw Error . unexpectedDataType ( self . data . type , expected : " array " )
72
+ guard let array = self . data. array else {
73
+ throw DecodingError . dataCorruptedError ( in : self , debugDescription : " Non-natively typed arrays must be JSON-encoded " )
62
74
}
63
- return _UnkeyedDecoder ( data: data, json: self . json)
64
- }
65
-
66
- func container< Key> (
67
- keyedBy type: Key . Type
68
- ) throws -> KeyedDecodingContainer < Key > where Key : CodingKey {
69
- let data : Data
70
- if let jsonb = self . data. jsonb {
71
- data = jsonb
72
- } else if let json = self . data. json {
73
- data = json
74
- } else {
75
- throw Error . unexpectedDataType ( self . data. type, expected: " json " )
75
+ return ArrayContainer ( data: array, dataDecoder: self . dataDecoder)
76
+ }
77
+
78
+ struct ArrayContainer : UnkeyedDecodingContainer {
79
+ let data : [ PostgresData ]
80
+ let dataDecoder : PostgresDataDecoder
81
+ var codingPath : [ CodingKey ] { [ ] }
82
+ var count : Int ? { self . data. count }
83
+ var isAtEnd : Bool { self . currentIndex >= self . data. count }
84
+ var currentIndex : Int = 0
85
+
86
+ mutating func decodeNil( ) throws -> Bool {
87
+ // Do _not_ shorten this using `defer`, otherwise `currentIndex` is incorrectly incremented.
88
+ if self . data [ self . currentIndex] . value == nil {
89
+ self . currentIndex += 1
90
+ return true
91
+ }
92
+ return false
93
+ }
94
+
95
+ mutating func decode< T> ( _ type: T . Type ) throws -> T where T : Decodable {
96
+ // Do _not_ shorten this using `defer`, otherwise `currentIndex` is incorrectly incremented.
97
+ let result = try self . dataDecoder. decode ( T . self, from: self . data [ self . currentIndex] )
98
+ self . currentIndex += 1
99
+ return result
100
+ }
101
+
102
+ mutating func nestedContainer< NewKey: CodingKey > ( keyedBy _: NewKey . Type ) throws -> KeyedDecodingContainer < NewKey > {
103
+ throw DecodingError . dataCorruptedError ( in: self , debugDescription: " Data nesting is not supported " )
104
+ }
105
+
106
+ mutating func nestedUnkeyedContainer( ) throws -> UnkeyedDecodingContainer {
107
+ throw DecodingError . dataCorruptedError ( in: self , debugDescription: " Data nesting is not supported " )
108
+ }
109
+
110
+ mutating func superDecoder( ) throws -> Decoder {
111
+ throw DecodingError . dataCorruptedError ( in: self , debugDescription: " Data nesting is not supported " )
76
112
}
77
- return try self . json
78
- . decode ( DecoderUnwrapper . self, from: data)
79
- . decoder. container ( keyedBy: Key . self)
80
113
}
81
-
114
+
82
115
func singleValueContainer( ) throws -> SingleValueDecodingContainer {
83
- _ValueDecoder ( data: self . data, json: self . json)
84
- }
85
- }
86
-
87
- struct _UnkeyedDecoder : UnkeyedDecodingContainer {
88
- var count : Int ? {
89
- self . data. count
90
- }
91
-
92
- var isAtEnd : Bool {
93
- self . currentIndex == self . data. count
94
- }
95
- var currentIndex : Int = 0
96
-
97
- let data : [ PostgresData ]
98
- let json : PostgresJSONDecoder
99
- var codingPath : [ CodingKey ] {
100
- [ ]
116
+ return self
101
117
}
102
-
103
- mutating func decodeNil( ) throws -> Bool {
104
- defer { self . currentIndex += 1 }
105
- return self . data [ self . currentIndex] . value == nil
106
- }
107
-
108
- mutating func decode< T> ( _ type: T . Type ) throws -> T where T : Decodable {
109
- defer { self . currentIndex += 1 }
110
- let data = self . data [ self . currentIndex]
111
- return try PostgresDataDecoder ( json: self . json) . decode ( T . self, from: data)
112
- }
113
-
114
- mutating func nestedContainer< NestedKey> (
115
- keyedBy type: NestedKey . Type
116
- ) throws -> KeyedDecodingContainer < NestedKey >
117
- where NestedKey : CodingKey
118
- {
119
- throw Error . nestingNotSupported
120
- }
121
-
122
- mutating func nestedUnkeyedContainer( ) throws -> UnkeyedDecodingContainer {
123
- throw Error . nestingNotSupported
124
- }
125
-
126
- mutating func superDecoder( ) throws -> Decoder {
127
- throw Error . nestingNotSupported
128
- }
129
- }
130
-
131
- struct _ValueDecoder : SingleValueDecodingContainer {
132
- let data : PostgresData
133
- let json : PostgresJSONDecoder
134
- var codingPath : [ CodingKey ] {
135
- [ ]
136
- }
137
-
118
+
138
119
func decodeNil( ) -> Bool {
139
- return self . data. value == nil
120
+ self . data. value == nil
140
121
}
141
122
142
123
func decode< T> ( _ type: T . Type ) throws -> T where T : Decodable {
143
- if let convertible = T . self as? PostgresDataConvertible . Type {
144
- guard let value = convertible. init ( postgresData: data) else {
145
- throw DecodingError . typeMismatch ( T . self, DecodingError . Context. init (
146
- codingPath: [ ] ,
147
- debugDescription: " Could not convert to \( T . self) : \( data) "
148
- ) )
149
- }
150
- return value as! T
151
- } else {
152
- return try T . init ( from: _Decoder ( data: self . data, json: self . json) )
153
- }
124
+ // Recurse back into the data decoder, don't repeat its logic here.
125
+ return try self . dataDecoder. decode ( T . self, from: self . data)
154
126
}
155
127
}
156
128
}
157
-
158
- struct DecoderUnwrapper : Decodable {
159
- let decoder : Decoder
160
- init ( from decoder: Decoder ) {
161
- self . decoder = decoder
162
- }
163
- }
0 commit comments