-
Notifications
You must be signed in to change notification settings - Fork 86
Expand file tree
/
Copy pathCodableCBOREncoder.swift
More file actions
127 lines (100 loc) · 3.79 KB
/
CodableCBOREncoder.swift
File metadata and controls
127 lines (100 loc) · 3.79 KB
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
import Foundation
public class CodableCBOREncoder {
public var useStringKeys: Bool = false
public var dateStrategy: DateStrategy = .taggedAsEpochTimestamp
public var forbidNonStringMapKeys: Bool = false
struct _Options {
let useStringKeys: Bool
let dateStrategy: DateStrategy
let forbidNonStringMapKeys: Bool
init(
useStringKeys: Bool = false,
dateStrategy: DateStrategy = .taggedAsEpochTimestamp,
forbidNonStringMapKeys: Bool = false
) {
self.useStringKeys = useStringKeys
self.dateStrategy = dateStrategy
self.forbidNonStringMapKeys = forbidNonStringMapKeys
}
func toCBOROptions() -> CBOROptions {
return CBOROptions(
useStringKeys: self.useStringKeys,
dateStrategy: self.dateStrategy,
forbidNonStringMapKeys: self.forbidNonStringMapKeys
)
}
}
var options: _Options {
return _Options(
useStringKeys: self.useStringKeys,
dateStrategy: self.dateStrategy,
forbidNonStringMapKeys: self.forbidNonStringMapKeys
)
}
public init() {}
public func encode(_ value: Encodable) throws -> Data {
let encoder = _CBOREncoder(options: self.options)
if let dateVal = value as? Date {
return Data(CBOR.encodeDate(dateVal, options: self.options.toCBOROptions()))
} else if let dataVal = value as? Data {
return Data(CBOR.encodeData(dataVal, options: self.options.toCBOROptions()))
}
if options.forbidNonStringMapKeys {
if let dict = value as? Dictionary<AnyHashable, Any?> {
guard let _ = dict as? Dictionary<String, Any?> else {
throw CBOREncoderError.nonStringKeyInMap
}
}
}
try value.encode(to: encoder)
return encoder.data
}
func setOptions(_ newOptions: _Options) {
self.useStringKeys = newOptions.useStringKeys
self.dateStrategy = newOptions.dateStrategy
self.forbidNonStringMapKeys = newOptions.forbidNonStringMapKeys
}
}
final class _CBOREncoder {
var codingPath: [CodingKey] = []
var userInfo: [CodingUserInfoKey : Any] = [:]
fileprivate var container: CBOREncodingContainer? {
willSet {
precondition(self.container == nil)
}
}
var data: Data {
return container?.data ?? Data()
}
let options: CodableCBOREncoder._Options
init(options: CodableCBOREncoder._Options = CodableCBOREncoder._Options()) {
self.options = options
}
}
extension _CBOREncoder: Encoder {
fileprivate func assertCanCreateContainer() {
precondition(self.container == nil)
}
func container<Key: CodingKey>(keyedBy type: Key.Type) -> KeyedEncodingContainer<Key> {
assertCanCreateContainer()
let container = KeyedContainer<Key>(codingPath: self.codingPath, userInfo: self.userInfo, options: self.options)
self.container = container
return KeyedEncodingContainer(container)
}
func unkeyedContainer() -> UnkeyedEncodingContainer {
assertCanCreateContainer()
let container = UnkeyedContainer(codingPath: self.codingPath, userInfo: self.userInfo, options: self.options)
self.container = container
return container
}
func singleValueContainer() -> SingleValueEncodingContainer {
assertCanCreateContainer()
let container = SingleValueContainer(codingPath: self.codingPath, userInfo: self.userInfo, options: self.options)
self.container = container
return container
}
}
protocol CBOREncodingContainer: AnyObject {
var data: Data { get }
}
extension _CBOREncoder: CBOREncodingContainer {}