-
-
Notifications
You must be signed in to change notification settings - Fork 39
Expand file tree
/
Copy pathLeafDataStorage.swift
More file actions
232 lines (212 loc) · 9.46 KB
/
LeafDataStorage.swift
File metadata and controls
232 lines (212 loc) · 9.46 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
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
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
import NIO
import Foundation
internal indirect enum LeafDataStorage: Equatable, CustomStringConvertible, Sendable {
// MARK: - Cases
// Static values
case bool(Bool)
case string(String)
case int(Int)
case double(Double)
case data(Data)
// Collections (potentially holding lazy values)
case dictionary([String: LeafData])
case array([LeafData])
// Wrapped `Optional<LeafDataStorage>`
case optional(_ wrapped: LeafDataStorage?, _ type: LeafData.NaturalType)
// Lazy resolvable function
// Must specify return tuple giving (returnType, invariance)
@preconcurrency
case lazy(f: @Sendable () -> (LeafData),
returns: LeafData.NaturalType,
invariant: Bool)
// MARK: - LeafSymbol Conformance
// MARK: Properties
internal var resolved: Bool { true }
internal var invariant: Bool {
switch self {
case .bool(_),
.data(_),
.double(_),
.int(_),
.string(_): return true
case .lazy(_, _, let invariant): return invariant
case .optional(let o, _): return o?.invariant ?? true
case .array(let a):
let stored = a.map { $0.storage }.filter { $0.isLazy }
return stored.allSatisfy { $0.invariant }
case .dictionary(let d):
let stored = d.values.map { $0.storage }.filter { $0.isLazy }
return stored.allSatisfy { $0.invariant }
}
}
internal var symbols: Set<String> { .init() }
internal var isAtomic: Bool { true }
internal var isExpression: Bool { false }
internal var isAny: Bool { false }
internal var isConcrete: Bool { true }
/// Note: Will *always* return a value - can be force-unwrapped safely
internal var concreteType: LeafData.NaturalType? {
switch self {
// Concrete Types
case .array(_) : return .array
case .bool(_) : return .bool
case .data(_) : return .data
case .dictionary(_) : return .dictionary
case .double(_) : return .double
case .int(_) : return .int
case .string(_) : return .string
// Internal Wrapped Types
case .lazy(_, let t, _),
.optional(_, let t) : return t
}
}
internal var isNumeric: Bool { Self.numerics.contains(concreteType!) }
internal static let comparable: Set<LeafData.NaturalType> = [
.double, .int, .string
]
internal static let numerics: Set<LeafData.NaturalType> = [
.double, .int
]
// MARK: Functions
/// Will resolve anything but variant Lazy data (99% of everything), and unwrap optionals
internal func resolve() -> LeafDataStorage {
guard invariant else { return self }
switch self {
case .lazy(let f, _, _): return f().storage
case .optional(let o, _):
if let unwrapped = o { return unwrapped }
return self
case .array(let a):
let resolved: [LeafData] = a.map {
LeafData($0.storage.resolve())
}
return .array(resolved)
case .dictionary(let d):
let resolved: [String: LeafData] = d.mapValues {
LeafData($0.storage.resolve())
}
return .dictionary(resolved)
default: return self
}
}
/// Will serialize anything to a String except Lazy -> Lazy
internal func serialize() throws -> String? {
let c = LeafConfiguration.self
switch self {
// Atomic non-containers
case .bool(let b) : return c.boolFormatter(b)
case .int(let i) : return c.intFormatter(i)
case .double(let d) : return c.doubleFormatter(d)
case .string(let s) : return c.stringFormatter(s)
// Data
case .data(let d) : return c.dataFormatter(d)
// Wrapped
case .optional(let o, _) :
guard let wrapped = o else { return c.nilFormatter() }
return try wrapped.serialize()
// Atomic containers
case .array(let a) :
let result = try a.map { try $0.storage.serialize() ?? c.nilFormatter() }
return c.arrayFormatter(result)
case .dictionary(let d) :
let result = try d.mapValues { try $0.storage.serialize() ?? c.nilFormatter()}
return c.dictFormatter(result)
case .lazy(let f, _, _) :
guard let result = f() as LeafData?,
!result.storage.isLazy else {
// Silently fail lazy -> lazy... a better option would be nice
return c.nilFormatter()
}
return try result.storage.serialize() ?? c.nilFormatter()
}
}
/// Final serialization to a shared buffer
internal func serialize(buffer: inout ByteBuffer) throws {
let encoding = LeafConfiguration.encoding
var data: Data? = nil
switch self {
case .bool(_),
.int(_),
.double(_),
.string(_),
.lazy(_,_,_),
.optional(_,_),
.array(_),
.dictionary(_) : data = try serialize()!.data(using: encoding)
case .data(let d) : data = d
}
guard let validData = data else { throw "Serialization Error" }
buffer.writeBytes(validData)
}
// MARK: - Equatable Conformance
/// Strict equality comparision, with .nil/.void being equal - will fail on Lazy data that is variant
internal static func == (lhs: LeafDataStorage, rhs: LeafDataStorage) -> Bool {
// If both sides are optional and nil, equal
guard !lhs.isNil || !rhs.isNil else { return true }
// Both sides must be non-nil and same concrete type, or unequal
guard !lhs.isNil && !rhs.isNil,
lhs.concreteType == rhs.concreteType else { return false }
// As long as both are static types, test them
if !lhs.isLazy && !rhs.isLazy {
switch (lhs, rhs) {
// Direct concrete type comparisons
case ( .array(let a), .array(let b)) : return a == b
case (.dictionary(let a), .dictionary(let b)) : return a == b
case ( .bool(let a), .bool(let b)) : return a == b
case ( .string(let a), .string(let b)) : return a == b
case ( .int(let a), .int(let b)) : return a == b
case ( .double(let a), .double(let b)) : return a == b
case ( .data(let a), .data(let b)) : return a == b
// Both sides are optional, unwrap and compare
case (.optional(let l,_), .optional(let r,_)) :
if let l = l, let r = r,
l == r { return true } else { return false }
// ... or unwrap just one side
case (.optional(let l,_), _) :
if let l = l { return l == rhs } else { return false }
case ( _, .optional(let r,_)) :
if let r = r { return r == lhs } else { return false }
default : return false
}
} else if case .lazy(let lhsF, let lhsR, let lhsI) = lhs,
case .lazy(let rhsF, let rhsR, let rhsI) = rhs {
// Only compare lazy equality if invariant to avoid side-effects
guard lhsI && rhsI, lhsR == rhsR else { return false }
return lhsF() == rhsF()
} else { return false }
}
// MARK: - CustomStringConvertible
internal var description: String {
switch self {
case .array(let a) : return "array(\(a.count))"
case .bool(let b) : return "bool(\(b))"
case .data(let d) : return "data(\(d.count))"
case .dictionary(let d) : return "dictionary(\(d.count))"
case .double(let d) : return "double(\(d))"
case .int(let i) : return "int(\(i))"
case .lazy(_, let r, _) : return "lazy(() -> \(r)?)"
case .optional(_, let t) : return "\(t)()?"
case .string(let s) : return "string(\(s))"
}
}
internal var short: String { (try? self.serialize()) ?? "" }
// MARK: - Other
internal var isNil: Bool {
switch self {
case .optional(let o, _) where o == nil : return true
default : return false
}
}
internal var isLazy: Bool {
if case .lazy(_,_,_) = self { return true } else { return false }
}
/// Flat mapping behavior - will never re-wrap .optional
internal var wrap: LeafDataStorage {
if case .optional(_,_) = self { return self }
return .optional(self, concreteType!)
}
internal var unwrap: LeafDataStorage? {
guard case .optional(let optional, _) = self else { return self }
return optional
}
}