@@ -59,12 +59,318 @@ extension AnyCodable: Equatable {
59
59
return lhs == rhs
60
60
case let ( lhs as String , rhs as String ) :
61
61
return lhs == rhs
62
- case ( let lhs as [ String : AnyCodable ] , let rhs as [ String : AnyCodable ] ) :
63
- return lhs == rhs
64
- case ( let lhs as [ AnyCodable ] , let rhs as [ AnyCodable ] ) :
65
- return lhs == rhs
66
62
case ( let lhs as CoatyUUID , let rhs as CoatyUUID ) :
67
63
return lhs == rhs
64
+ case let ( lhs as CoatyObject , rhs as CoatyObject ) :
65
+ return AnyCodable . deepEquals ( lhs, rhs)
66
+ case ( let lhs as [ String : AnyCodable ] , let rhs as [ String : AnyCodable ] ) :
67
+ return AnyCodable . deepEquals ( lhs, rhs)
68
+ case ( let lhs as [ Any ] , let rhs as [ Any ] ) :
69
+ return AnyCodable . deepEquals ( lhs, rhs)
70
+ default :
71
+ return false
72
+ }
73
+ }
74
+ }
75
+
76
+ extension AnyCodable {
77
+ /// - Note: Internal for Internal use in framework only
78
+ ///
79
+ /// Utitlity function to pack Any as AnyCodable while using the most specific type for the value.
80
+ ///
81
+ /// - Parameters:
82
+ /// - value: value to be packed as AnyCodable
83
+ /// - Returns: AnyCodable of the value (with the most specific type); nil if the value should never be packed as AnyCodable
84
+ internal static func _getAnyAsAnyCodable( _ value: Any ) -> AnyCodable ? {
85
+ if let v = value as? Bool {
86
+ return AnyCodable ( booleanLiteral: v)
87
+ } else if let v = value as? Int {
88
+ return AnyCodable ( integerLiteral: v)
89
+ } else if let v = value as? Int8 {
90
+ return AnyCodable ( v)
91
+ } else if let v = value as? Int16 {
92
+ return AnyCodable ( v)
93
+ } else if let v = value as? Int32 {
94
+ return AnyCodable ( v)
95
+ } else if let v = value as? Int64 {
96
+ return AnyCodable ( v)
97
+ } else if let v = value as? UInt {
98
+ return AnyCodable ( v)
99
+ } else if let v = value as? UInt8 {
100
+ return AnyCodable ( v)
101
+ } else if let v = value as? UInt16 {
102
+ return AnyCodable ( v)
103
+ } else if let v = value as? UInt32 {
104
+ return AnyCodable ( v)
105
+ } else if let v = value as? UInt64 {
106
+ return AnyCodable ( v)
107
+ } else if let v = value as? Float {
108
+ return AnyCodable ( v)
109
+ } else if let v = value as? Double {
110
+ return AnyCodable ( v)
111
+ } else if let v = value as? String {
112
+ return AnyCodable ( stringLiteral: v)
113
+ } else if let v = value as? CoatyUUID {
114
+ return AnyCodable ( v)
115
+ } else if let v = value as? CoatyObject {
116
+ return AnyCodable ( v)
117
+ } else if let v = value as? [ Any ] {
118
+ let result = v. map { any -> AnyCodable in
119
+ if let nonNil = AnyCodable . _getAnyAsAnyCodable ( any) {
120
+ return nonNil
121
+ } else {
122
+ return AnyCodable ( nil )
123
+ }
124
+ }
125
+ return AnyCodable ( result)
126
+ } else if let v = value as? [ String : Any ] {
127
+ var result = [ String: AnyCodable] ( )
128
+ for key in v. keys {
129
+ if let unwrappedValue = AnyCodable . _getAnyAsAnyCodable ( v [ key] !) {
130
+ result [ key] = unwrappedValue
131
+ } else {
132
+ result [ key] = nil
133
+ }
134
+ }
135
+ return AnyCodable ( result)
136
+ } else if let v = value as? AnyCodable {
137
+ return AnyCodable . _getAnyAsAnyCodable ( v. value)
138
+ } else {
139
+ return nil
140
+ }
141
+ }
142
+ }
143
+
144
+ extension AnyCodable {
145
+ /// - Note: Internal for internal use in framework only
146
+ ///
147
+ /// Determines whether two Arrays [Any] are structurally equal
148
+ /// (aka. deep equal) according to a recursive equality algorithm.
149
+ ///
150
+ /// - Parameters:
151
+ /// - lhs: an array of type [Any]
152
+ /// - rhs: an array of type [Any]
153
+ internal static func deepEquals( _ lhs: [ Any ] , _ rhs: [ Any ] ) -> Bool {
154
+ // All elements have to be of type AnyCodable to perform element-wise comparisons
155
+ let lhsAsCodables = lhs. map { AnyCodable . _getAnyAsAnyCodable ( $0) }
156
+ let rhsAsCodables = rhs. map { AnyCodable . _getAnyAsAnyCodable ( $0) }
157
+
158
+ return lhsAsCodables == rhsAsCodables
159
+ }
160
+
161
+ /// - Note: Internal for internal use in framework only
162
+ ///
163
+ /// Determines whether two Dictionaries [String: AnyCodable] are structurally equal
164
+ /// (aka. deep equal) according to a recursive equality algorithm.
165
+ ///
166
+ /// - Parameters:
167
+ /// - lhs: a dictionary of type [String: AnyCodable]
168
+ /// - rhs: a dictionary of type [String: AnyCodable]
169
+ /// - Returns: true if two dictionaries are structurally equal
170
+ internal static func deepEquals( _ lhs: [ String : AnyCodable ] , _ rhs: [ String : AnyCodable ] ) -> Bool {
171
+ if lhs. keys. count != rhs. keys. count {
172
+ return false
173
+ }
174
+
175
+ for prop in lhs. keys {
176
+ if let lhsVal = lhs [ prop] ,
177
+ let rhsVal = rhs [ prop] ,
178
+ lhsVal != rhsVal {
179
+ return false
180
+ }
181
+ }
182
+
183
+ return true
184
+ }
185
+
186
+ /// - Note: Internal for internal use in framework only
187
+ ///
188
+ /// Determines whether two CoatyObjects are structurally equal
189
+ /// (aka. deep equal) according to a recursive equality algorithm.
190
+ ///
191
+ /// - Parameters:
192
+ /// - lhs: a Coaty object
193
+ /// - rhs: a Coaty object
194
+ /// - Returns: true if the two objects are structurally equal (property names and values must be the same)
195
+ internal static func deepEquals( _ lhs: CoatyObject , _ rhs: CoatyObject ) -> Bool {
196
+ let lhsProperties = AnyCodable . getDictionaryOfProperties ( from: Mirror ( reflecting: lhs) )
197
+ let rhsProperties = AnyCodable . getDictionaryOfProperties ( from: Mirror ( reflecting: rhs) )
198
+
199
+ if lhsProperties. keys. count != rhsProperties. keys. count {
200
+ return false
201
+ }
202
+
203
+ for prop in lhsProperties. keys {
204
+ if let lhsVal = lhsProperties [ prop] ,
205
+ let rhsVal = rhsProperties [ prop] ,
206
+ lhsVal != rhsVal {
207
+ return false
208
+ }
209
+ }
210
+
211
+ return true
212
+ }
213
+ }
214
+
215
+ extension AnyCodable {
216
+ /// Checks if a JavaScript value (usually an object or array) contains
217
+ /// other values. Primitive value types (number, string, boolean, null,undefined) contain
218
+ /// only the identical value. Object properties match if all the key-value
219
+ /// pairs of the specified object are contained in them. Array properties
220
+ /// match if all the specified array elements are contained in them.
221
+ ///
222
+ /// The general principle is that the contained object must match the containing object
223
+ /// as to structure and data contents recursively on all levels, possibly after discarding
224
+ /// some non-matching array elements or object key/value pairs from the containing object.
225
+ /// But remember that the order of array elements is not significant when doing a containment match,
226
+ /// and duplicate array elements are effectively considered only once.
227
+ ///
228
+ /// As a special exception to the general principle that the structures must match, an
229
+ /// array on *toplevel* may contain a primitive value:
230
+ /// ```ts
231
+ /// contains([1, 2, 3], [3]) => true
232
+ /// contains([1, 2, 3], 3) => true
233
+ /// ```
234
+ ///
235
+ /// @param a a JavaScript value containing another value
236
+ /// @param b a JavaScript value to be contained in another value
237
+ internal static func deepContains( _ a: AnyCodable , _ b: AnyCodable ) -> Bool {
238
+
239
+ let bAsAnyCodable = AnyCodable . _getAnyAsAnyCodable ( b) !
240
+ let aAsAnyCodable = AnyCodable . _getAnyAsAnyCodable ( a) !
241
+ return AnyCodable . _deepContains ( aAsAnyCodable, bAsAnyCodable, true )
242
+ }
243
+
244
+ internal static func _deepContains( _ x: AnyCodable , _ y: AnyCodable , _ isTopLevel: Bool ) -> Bool {
245
+ if let xValues = x. value as? [ AnyCodable ] {
246
+ if let yValues = y. value as? [ AnyCodable ] {
247
+ return yValues. allSatisfy { yv -> Bool in
248
+ return xValues. contains { xv -> Bool in
249
+ AnyCodable . _deepContains ( xv, yv, false )
250
+ }
251
+ }
252
+ } else {
253
+ // Special exception: check containment of a primitive array element on toplevel
254
+ if isTopLevel {
255
+ return xValues. contains { xv -> Bool in
256
+ xv == y
257
+ }
258
+ }
259
+ return false
260
+ }
261
+
262
+ }
263
+
264
+ if let xAsObject = x. value as? CoatyObject {
265
+ if let yAsObject = y. value as? CoatyObject {
266
+ let xProperties = AnyCodable . getDictionaryOfProperties ( from: Mirror ( reflecting: xAsObject) )
267
+ let yProperties = AnyCodable . getDictionaryOfProperties ( from: Mirror ( reflecting: yAsObject) )
268
+
269
+ return xProperties. keys. allSatisfy { xk -> Bool in
270
+ if yProperties. index ( forKey: xk) == nil {
271
+ return false
272
+ }
273
+ return AnyCodable . _deepContains ( xProperties [ xk] !, yProperties [ xk] !, false )
274
+ }
275
+ } else {
276
+ return false
277
+ }
278
+ }
279
+
280
+ return x == y
281
+ }
282
+ }
283
+
284
+ extension AnyCodable {
285
+
286
+ /// - Note: Internal for internal use in framework only
287
+ ///
288
+ /// Checks if a value is included on toplevel in the given
289
+ /// operand array of values which may be primitive types (number, string, boolean, null)
290
+ /// or object types compared using the == equality operator.
291
+ ///
292
+ /// - Parameters:
293
+ /// - lhs: an array containing another value on toplevel
294
+ /// - rhs: a value to be contained on toplevel in an array
295
+ /// - Returns: true if rhs is contained in lhs
296
+ internal static func deepIncludes( _ lhs: AnyCodable , _ rhs: AnyCodable ) -> Bool {
297
+ // First argument must be an array of Any values
298
+ guard let lhsAsArray = lhs. value as? [ Any ] else {
299
+ return false
300
+ }
301
+
302
+ let lhsAsCodables = lhsAsArray. compactMap { AnyCodable . _getAnyAsAnyCodable ( $0) }
303
+
304
+ return lhsAsCodables. contains { element -> Bool in
305
+ return element == rhs
306
+ }
307
+ }
308
+ }
309
+
310
+ extension AnyCodable {
311
+ /// - Note: Internal for interal use in framework only
312
+ ///
313
+ /// Recursively get a dictionary of all properties as [String: AnyCodable] associated with a given mirror of an object
314
+ ///
315
+ /// - Parameters:
316
+ /// - mirror: mirror of the object that properties need to be extracted
317
+ /// - Returns: a dictionary of all properties associated with the object (only those properties that can be represented as AnyCodable)
318
+ internal static func getDictionaryOfProperties( from mirror: Mirror ) -> [ String : AnyCodable ] {
319
+ var result = [ String: AnyCodable] ( )
320
+
321
+ for child in mirror. children {
322
+ result [ child. label!] = AnyCodable . _getAnyAsAnyCodable ( child. value)
323
+ }
324
+
325
+ guard let superMirror = mirror. superclassMirror else {
326
+ return result
327
+ }
328
+
329
+ return result. merging ( AnyCodable . getDictionaryOfProperties ( from: superMirror) ) { ( _, new) in
330
+ new
331
+ }
332
+ }
333
+ }
334
+
335
+ extension AnyCodable : Comparable {
336
+ public static func < ( lhs: AnyCodable , rhs: AnyCodable ) -> Bool {
337
+ switch ( lhs. value, rhs. value) {
338
+ case is ( Void , Void ) :
339
+ return false
340
+ case let ( lhs as Bool , rhs as Bool ) :
341
+ return ( lhs == false ) && ( rhs == true )
342
+ case let ( lhs as Int , rhs as Int ) :
343
+ return lhs < rhs
344
+ case let ( lhs as Int8 , rhs as Int8 ) :
345
+ return lhs < rhs
346
+ case let ( lhs as Int16 , rhs as Int16 ) :
347
+ return lhs < rhs
348
+ case let ( lhs as Int32 , rhs as Int32 ) :
349
+ return lhs < rhs
350
+ case let ( lhs as Int64 , rhs as Int64 ) :
351
+ return lhs < rhs
352
+ case let ( lhs as UInt , rhs as UInt ) :
353
+ return lhs < rhs
354
+ case let ( lhs as UInt8 , rhs as UInt8 ) :
355
+ return lhs < rhs
356
+ case let ( lhs as UInt16 , rhs as UInt16 ) :
357
+ return lhs < rhs
358
+ case let ( lhs as UInt32 , rhs as UInt32 ) :
359
+ return lhs < rhs
360
+ case let ( lhs as UInt64 , rhs as UInt64 ) :
361
+ return lhs < rhs
362
+ case let ( lhs as Float , rhs as Float ) :
363
+ return lhs < rhs
364
+ case let ( lhs as Double , rhs as Double ) :
365
+ return lhs < rhs
366
+ case let ( lhs as String , rhs as String ) :
367
+ switch lhs. localizedCompare ( rhs) {
368
+ case . orderedAscending:
369
+ return true
370
+ default :
371
+ return false
372
+ }
373
+ // CoatyUUID (description)
68
374
default :
69
375
return false
70
376
}
@@ -96,3 +402,4 @@ extension AnyCodable: CustomDebugStringConvertible {
96
402
}
97
403
98
404
extension AnyCodable : ExpressibleByNilLiteral , ExpressibleByBooleanLiteral , ExpressibleByIntegerLiteral , ExpressibleByFloatLiteral , ExpressibleByStringLiteral , ExpressibleByArrayLiteral , ExpressibleByDictionaryLiteral { }
405
+
0 commit comments