Skip to content

Commit be44c34

Browse files
authored
Support ordered dictionaries/sets (#80)
We currently auto-sort the children of any mirror with a display style of dictionary or set alphabetically. This is important for consistent dumps (good for snapshot tests, too), but affects types that don't need the sorting, like ordered dictionaries and sets. This PR introduces an `_UnorderedCollection` protocol to control this behavior, and conforms `Dictionary`, `Set`, and their `NS`- predecessors, but leaves other types open to dumping and diffing in an ordered way by default.
1 parent 2a34993 commit be44c34

File tree

6 files changed

+157
-8
lines changed

6 files changed

+157
-8
lines changed

Sources/CustomDump/Diff.swift

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -390,17 +390,19 @@ public func diff<T>(_ lhs: T, _ rhs: T, format: DiffFormat = .default) -> String
390390
}
391391
return lhs.key == rhs.key
392392
},
393-
areInIncreasingOrder: {
393+
areInIncreasingOrder: T.self is _UnorderedCollection.Type
394+
? {
394395
guard
395396
let lhs = $0.value as? (key: AnyHashable, value: Any),
396397
let rhs = $1.value as? (key: AnyHashable, value: Any)
397398
else {
398399
return _customDump($0.value, name: nil, indent: 0, isRoot: false, maxDepth: 1)
399-
< _customDump($1.value, name: nil, indent: 0, isRoot: false, maxDepth: 1)
400+
< _customDump($1.value, name: nil, indent: 0, isRoot: false, maxDepth: 1)
400401
}
401402
return _customDump(lhs.key.base, name: nil, indent: 0, isRoot: false, maxDepth: 1)
402-
< _customDump(rhs.key.base, name: nil, indent: 0, isRoot: false, maxDepth: 1)
403+
< _customDump(rhs.key.base, name: nil, indent: 0, isRoot: false, maxDepth: 1)
403404
}
405+
: nil
404406
) { child, _ in
405407
guard let pair = child.value as? (key: AnyHashable, value: Any) else { return }
406408
child = (
@@ -479,10 +481,12 @@ public func diff<T>(_ lhs: T, _ rhs: T, format: DiffFormat = .default) -> String
479481
areEquivalent: {
480482
isIdentityEqual($0.value, $1.value) || isMirrorEqual($0.value, $1.value)
481483
},
482-
areInIncreasingOrder: {
484+
areInIncreasingOrder: T.self is _UnorderedCollection.Type
485+
? {
483486
_customDump($0.value, name: nil, indent: 0, isRoot: false, maxDepth: 1)
484487
< _customDump($1.value, name: nil, indent: 0, isRoot: false, maxDepth: 1)
485488
}
489+
: nil
486490
)
487491

488492
case (_, .struct?, _, .struct?):

Sources/CustomDump/Dump.swift

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -209,15 +209,17 @@ func _customDump<T, TargetStream>(
209209
dumpChildren(
210210
of: mirror,
211211
prefix: "[", suffix: "]",
212-
by: {
212+
by: T.self is _UnorderedCollection.Type
213+
? {
213214
guard
214215
let (lhsKey, _) = $0.value as? (key: AnyHashable, value: Any),
215216
let (rhsKey, _) = $1.value as? (key: AnyHashable, value: Any)
216217
else { return false }
217218

218219
return _customDump(lhsKey.base, name: nil, indent: 0, isRoot: false, maxDepth: 1)
219220
< _customDump(rhsKey.base, name: nil, indent: 0, isRoot: false, maxDepth: 1)
220-
},
221+
}
222+
: nil,
221223
{ child, _ in
222224
guard let pair = child.value as? (key: AnyHashable, value: Any) else { return }
223225
let key = _customDump(
@@ -260,10 +262,13 @@ func _customDump<T, TargetStream>(
260262
dumpChildren(
261263
of: mirror,
262264
prefix: "Set([", suffix: "])",
263-
by: {
265+
by: T.self is _UnorderedCollection.Type
266+
? {
264267
_customDump($0.value, name: nil, indent: 0, isRoot: false, maxDepth: 1)
265268
< _customDump($1.value, name: nil, indent: 0, isRoot: false, maxDepth: 1)
266-
})
269+
}
270+
: nil
271+
)
267272

268273
case (_, .struct?):
269274
dumpChildren(of: mirror, prefix: "\(typeName(mirror.subjectType))(", suffix: ")")
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import Foundation
2+
3+
public protocol _UnorderedCollection {}
4+
extension Dictionary: _UnorderedCollection {}
5+
extension NSDictionary: _UnorderedCollection {}
6+
extension NSSet: _UnorderedCollection {}
7+
extension Set: _UnorderedCollection {}

Tests/CustomDumpTests/DiffTests.swift

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,100 @@ final class DiffTests: XCTestCase {
239239
]
240240
"""
241241
)
242+
243+
XCTAssertNoDifference(
244+
diff(
245+
OrderedDictionary(pairs: [
246+
1: User(
247+
id: 1,
248+
name: "Blob"
249+
),
250+
2: User(
251+
id: 2,
252+
name: "Blob, Jr."
253+
),
254+
]),
255+
OrderedDictionary(pairs: [
256+
1: User(
257+
id: 1,
258+
name: "Blob"
259+
),
260+
2: User(
261+
id: 2,
262+
name: "Blob, Sr."
263+
),
264+
3: User(
265+
id: 3,
266+
name: "Dr. Blob"
267+
),
268+
])
269+
),
270+
"""
271+
[
272+
1: User(…),
273+
2: User(
274+
id: 2,
275+
- name: "Blob, Jr."
276+
+ name: "Blob, Sr."
277+
),
278+
+ 3: User(
279+
+ id: 3,
280+
+ name: "Dr. Blob"
281+
+ )
282+
]
283+
"""
284+
)
285+
286+
XCTAssertNoDifference(
287+
diff(
288+
OrderedDictionary(pairs: [
289+
0: User(
290+
id: 0,
291+
name: "Original Blob"
292+
),
293+
1: User(
294+
id: 1,
295+
name: "Blob"
296+
),
297+
2: User(
298+
id: 2,
299+
name: "Blob, Jr."
300+
),
301+
]),
302+
OrderedDictionary(pairs: [
303+
0: User(
304+
id: 0,
305+
name: "Original Blob"
306+
),
307+
1: User(
308+
id: 1,
309+
name: "Blob"
310+
),
311+
2: User(
312+
id: 2,
313+
name: "Blob, Sr."
314+
),
315+
3: User(
316+
id: 3,
317+
name: "Dr. Blob"
318+
),
319+
])
320+
),
321+
"""
322+
[
323+
… (2 unchanged),
324+
2: User(
325+
id: 2,
326+
- name: "Blob, Jr."
327+
+ name: "Blob, Sr."
328+
),
329+
+ 3: User(
330+
+ id: 3,
331+
+ name: "Dr. Blob"
332+
+ )
333+
]
334+
"""
335+
)
242336
}
243337

244338
func testDictionaryCollapsing() {

Tests/CustomDumpTests/DumpTests.swift

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,37 @@ final class DumpTests: XCTestCase {
267267
]
268268
"""
269269
)
270+
271+
dump = ""
272+
customDump(
273+
OrderedDictionary(pairs: [
274+
2: User(
275+
id: 2,
276+
name: "Blob, Jr."
277+
),
278+
1: User(
279+
id: 1,
280+
name: "Blob"
281+
),
282+
] as KeyValuePairs),
283+
to: &dump
284+
)
285+
XCTAssertNoDifference(
286+
dump,
287+
"""
288+
[
289+
2: User(
290+
id: 2,
291+
name: "Blob, Jr."
292+
),
293+
1: User(
294+
id: 1,
295+
name: "Blob"
296+
)
297+
]
298+
"""
299+
)
300+
270301
}
271302

272303
func testEnum() {

Tests/CustomDumpTests/Mocks.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,3 +139,11 @@ struct Wrapped<Value> {
139139
struct Item {
140140
@Wrapped var isInStock = true
141141
}
142+
143+
struct OrderedDictionary<Key, Value>: CustomReflectable {
144+
var pairs: KeyValuePairs<Key, Value>
145+
146+
var customMirror: Mirror {
147+
Mirror(self.pairs, unlabeledChildren: self.pairs, displayStyle: .dictionary)
148+
}
149+
}

0 commit comments

Comments
 (0)