This repository has been archived by the owner on Jun 27, 2023. It is now read-only.
/
ReflectionDecoders.swift
213 lines (179 loc) 路 7.57 KB
/
ReflectionDecoders.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
/// Internal types for powering the default implementation of `Reflectable` for `Decodable` types.
///
/// See `Decodable.decodeProperties(depth:)` and `Decodable.decodeProperty(forKey:)` for more information.
// MARK: Internal
/// Reference class for collecting information about `Decodable` types when initializing them.
final class ReflectionDecoderContext {
/// If set, this is the `CodingKey` path to the truthy value in the initialized model.
var activeCodingPath: [CodingKey]?
/// Sets a maximum depth for decoding nested types like optionals and structs. This value ensures
/// that models with recursive structures can be decoded without looping infinitely.
var maxDepth: Int
/// An array of all properties seen while initilaizing the `Decodable` type.
var properties: [ReflectedProperty]
/// If `true`, the property be decoded currently should be set to a truthy value.
/// This property will cycle each time it is called.
var isActive: Bool {
defer { currentOffset += 1 }
return currentOffset == activeOffset
}
/// This decoder context's curent active offset. This will determine which property gets
/// set to a truthy value while decoding.
private var activeOffset: Int
/// Current offset. This is equal to the number of times `isActive` has been called so far.
private var currentOffset: Int
/// Creates a new `ReflectionDecoderContext`.
init(activeOffset: Int, maxDepth: Int) {
self.activeCodingPath = nil
self.maxDepth = maxDepth
self.properties = []
self.activeOffset = activeOffset
currentOffset = 0
}
/// Adds a property to this `ReflectionDecoderContext`.
func addProperty<T>(type: T.Type, at path: [CodingKey]) {
let path = path.map { $0.stringValue }
// remove any duplicates, favoring the new type
properties = properties.filter { $0.path != path }
let property = ReflectedProperty.init(T.self, at: path)
properties.append(property)
}
}
/// Main decoder for codable reflection.
struct ReflectionDecoder: Decoder {
var codingPath: [CodingKey]
var context: ReflectionDecoderContext
var userInfo: [CodingUserInfoKey: Any] { return [:] }
init(codingPath: [CodingKey], context: ReflectionDecoderContext) {
self.codingPath = codingPath
self.context = context
}
func container<Key>(keyedBy type: Key.Type) throws -> KeyedDecodingContainer<Key> where Key : CodingKey {
return .init(ReflectionKeyedDecoder<Key>(codingPath: codingPath, context: context))
}
func unkeyedContainer() throws -> UnkeyedDecodingContainer {
return ReflectionUnkeyedDecoder(codingPath: codingPath, context: context)
}
func singleValueContainer() throws -> SingleValueDecodingContainer {
return ReflectionSingleValueDecoder(codingPath: codingPath, context: context)
}
}
/// Single value decoder for codable reflection.
struct ReflectionSingleValueDecoder: SingleValueDecodingContainer {
var codingPath: [CodingKey]
var context: ReflectionDecoderContext
init(codingPath: [CodingKey], context: ReflectionDecoderContext) {
self.codingPath = codingPath
self.context = context
}
func decodeNil() -> Bool {
return false
}
func decode<T>(_ type: T.Type) throws -> T where T: Decodable {
context.addProperty(type: T.self, at: codingPath)
let type = try forceCast(T.self)
let reflected = try type.anyReflectDecoded()
if context.isActive {
context.activeCodingPath = codingPath
return reflected.0 as! T
}
return reflected.1 as! T
}
}
/// Keyed decoder for codable reflection.
final class ReflectionKeyedDecoder<K>: KeyedDecodingContainerProtocol where K: CodingKey {
typealias Key = K
var allKeys: [K] { return [] }
var codingPath: [CodingKey]
var context: ReflectionDecoderContext
var nextIsOptional: Bool
init(codingPath: [CodingKey], context: ReflectionDecoderContext) {
self.codingPath = codingPath
self.context = context
self.nextIsOptional = false
}
func contains(_ key: K) -> Bool {
nextIsOptional = true
return true
}
func decodeNil(forKey key: K) throws -> Bool {
if context.maxDepth > codingPath.count {
return false
}
return true
}
func nestedContainer<NestedKey>(keyedBy type: NestedKey.Type, forKey key: K) throws -> KeyedDecodingContainer<NestedKey> where NestedKey: CodingKey {
return .init(ReflectionKeyedDecoder<NestedKey>(codingPath: codingPath + [key], context: context))
}
func nestedUnkeyedContainer(forKey key: K) throws -> UnkeyedDecodingContainer {
return ReflectionUnkeyedDecoder(codingPath: codingPath + [key], context: context)
}
func superDecoder() throws -> Decoder {
return ReflectionDecoder(codingPath: codingPath, context: context)
}
func superDecoder(forKey key: K) throws -> Decoder {
return ReflectionDecoder(codingPath: codingPath + [key], context: context)
}
func decode<T>(_ type: T.Type, forKey key: K) throws -> T where T : Decodable {
if nextIsOptional {
context.addProperty(type: T?.self, at: codingPath + [key])
nextIsOptional = false
} else {
context.addProperty(type: T.self, at: codingPath + [key])
}
if let type = T.self as? AnyReflectionDecodable.Type, let reflected = try? type.anyReflectDecoded() {
if context.isActive {
context.activeCodingPath = codingPath + [key]
return reflected.0 as! T
}
return reflected.1 as! T
} else {
let decoder = ReflectionDecoder(codingPath: codingPath + [key], context: context)
return try T(from: decoder)
}
}
}
/// Unkeyed decoder for codable reflection.
fileprivate struct ReflectionUnkeyedDecoder: UnkeyedDecodingContainer {
var count: Int?
var isAtEnd: Bool
var currentIndex: Int
var codingPath: [CodingKey]
var context: ReflectionDecoderContext
init(codingPath: [CodingKey], context: ReflectionDecoderContext) {
self.codingPath = codingPath
self.context = context
self.currentIndex = 0
if context.isActive {
self.count = 1
self.isAtEnd = false
context.activeCodingPath = codingPath
} else {
self.count = 0
self.isAtEnd = true
}
}
mutating func decodeNil() throws -> Bool {
isAtEnd = true
return true
}
mutating func decode<T>(_ type: T.Type) throws -> T where T : Decodable {
context.addProperty(type: [T].self, at: codingPath)
isAtEnd = true
if let type = T.self as? AnyReflectionDecodable.Type, let reflected = try? type.anyReflectDecoded() {
return reflected.0 as! T
} else {
let decoder = ReflectionDecoder(codingPath: codingPath, context: context)
return try T(from: decoder)
}
}
mutating func nestedContainer<NestedKey>(keyedBy type: NestedKey.Type) throws -> KeyedDecodingContainer<NestedKey> where NestedKey : CodingKey {
return .init(ReflectionKeyedDecoder<NestedKey>(codingPath: codingPath, context: context))
}
mutating func nestedUnkeyedContainer() throws -> UnkeyedDecodingContainer {
return ReflectionUnkeyedDecoder(codingPath: codingPath, context: context)
}
mutating func superDecoder() throws -> Decoder {
return ReflectionDecoder(codingPath: codingPath, context: context)
}
}