-
Notifications
You must be signed in to change notification settings - Fork 30
/
Copy pathDatabaseEncoder.swift
139 lines (111 loc) · 5.36 KB
/
DatabaseEncoder.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
/**
Copyright IBM Corporation 2018
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import Foundation
import KituraContracts
import SwiftKuery
/// Class used to construct a dictionary [String: Any] from a Model
open class DatabaseEncoder {
private var databaseEncoder = _DatabaseEncoder()
/// Encode a Encodable type to a dictionary [String: Any]
open func encode<T: Encodable>(_ value: T, dateEncodingStrategy: DateEncodingFormat) throws -> [String: Any] {
databaseEncoder.dateEncodingStrategy = dateEncodingStrategy
try value.encode(to: databaseEncoder)
return databaseEncoder.values
}
}
fileprivate class _DatabaseEncoder: Encoder {
public var codingPath = [CodingKey]()
public var values: [String: Any] = [:]
public var dateEncodingStrategy: DateEncodingFormat = .double
public var userInfo: [CodingUserInfoKey: Any] = [:]
public func container<Key>(keyedBy: Key.Type) -> KeyedEncodingContainer<Key> {
let container = _DatabaseKeyedEncodingContainer<Key>(encoder: self, codingPath: codingPath)
return KeyedEncodingContainer(container)
}
public func unkeyedContainer() -> UnkeyedEncodingContainer {
return _DatabaseEncodingContainer(encoder: self, codingPath: codingPath, count: 0)
}
public func singleValueContainer() -> SingleValueEncodingContainer {
return _DatabaseEncodingContainer(encoder: self, codingPath: codingPath, count: 0)
}
}
fileprivate struct _DatabaseKeyedEncodingContainer<K: CodingKey> : KeyedEncodingContainerProtocol {
typealias Key = K
var encoder: _DatabaseEncoder
var codingPath = [CodingKey]()
public mutating func encodeNil(forKey key: Key) throws {}
public mutating func encode<T: Encodable>(_ value: T, forKey key: Key) throws {
if let dataValue = value as? Data {
encoder.values[key.stringValue] = dataValue.base64EncodedString()
} else if let urlValue = value as? URL {
encoder.values[key.stringValue] = urlValue.absoluteString
} else if let uuidValue = value as? UUID {
encoder.values[key.stringValue] = uuidValue.uuidString
} else if let dateValue = value as? Date {
switch encoder.dateEncodingStrategy {
case .double:
encoder.values[key.stringValue] = dateValue.timeIntervalSinceReferenceDate
case .timestamp:
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
encoder.values[key.stringValue] = dateFormatter.string(from: dateValue)
case .date:
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd"
encoder.values[key.stringValue] = dateFormatter.string(from: dateValue)
case .time:
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "HH:mm:ss"
encoder.values[key.stringValue] = dateFormatter.string(from: dateValue)
}
} else if value is [Any] {
throw RequestError(.ormDatabaseEncodingError, reason: "Encoding an array is not currently supported")
} else if value is [AnyHashable: Any] {
throw RequestError(.ormDatabaseEncodingError, reason: "Encoding a dictionary is not currently supported")
} else {
encoder.values[key.stringValue] = value
}
}
public mutating func nestedContainer<NestedKey>(keyedBy keyType: NestedKey.Type, forKey key: Key) -> KeyedEncodingContainer<NestedKey> where NestedKey: CodingKey {
return encoder.container(keyedBy: keyType)
}
public mutating func nestedUnkeyedContainer(forKey key: Key) -> UnkeyedEncodingContainer {
return encoder.unkeyedContainer()
}
public mutating func superEncoder() -> Encoder {
return _DatabaseEncoder()
}
public mutating func superEncoder(forKey key: Key) -> Encoder {
return _DatabaseEncoder()
}
}
/// Default implenations of UnkeyedEncodingContainer and SingleValueEncodingContainer
/// Should never go into these containers. Types are checked in the TypeDecoder
fileprivate struct _DatabaseEncodingContainer: UnkeyedEncodingContainer, SingleValueEncodingContainer {
var encoder: Encoder
var codingPath = [CodingKey]()
var count: Int = 0
public mutating func encodeNil() throws {}
public mutating func encode<T: Encodable>(_ value: T) {
// TODO when encoding Arrays ( not supported for now )
}
public mutating func nestedContainer<NestedKey>(keyedBy keyType: NestedKey.Type) -> KeyedEncodingContainer<NestedKey> where NestedKey : CodingKey {
return encoder.container(keyedBy: keyType)
}
public mutating func nestedUnkeyedContainer() -> UnkeyedEncodingContainer {
return encoder.unkeyedContainer()
}
public mutating func superEncoder() -> Encoder {
return encoder
}
}