@@ -66,23 +66,29 @@ extension Snapshotting where Format == String {
66
66
}
67
67
}
68
68
69
- private func snap< T> ( _ value: T , name: String ? = nil , indent: Int = 0 ) -> String {
69
+ private func snap< T> (
70
+ _ value: T ,
71
+ name: String ? = nil ,
72
+ indent: Int = 0 ,
73
+ visitedValues: Set < ObjectIdentifier > = . init( )
74
+ ) -> String {
70
75
let indentation = String ( repeating: " " , count: indent)
71
76
let mirror = Mirror ( reflecting: value)
72
77
var children = mirror. children
73
78
let count = children. count
74
79
let bullet = count == 0 ? " - " : " ▿ "
80
+ var visitedValues = visitedValues
75
81
76
82
let description : String
77
83
switch ( value, mirror. displayStyle) {
78
84
case ( _, . collection? ) :
79
85
description = count == 1 ? " 1 element " : " \( count) elements "
80
86
case ( _, . dictionary? ) :
81
87
description = count == 1 ? " 1 key/value pair " : " \( count) key/value pairs "
82
- children = sort ( children)
88
+ children = sort ( children, visitedValues : visitedValues )
83
89
case ( _, . set? ) :
84
90
description = count == 1 ? " 1 member " : " \( count) members "
85
- children = sort ( children)
91
+ children = sort ( children, visitedValues : visitedValues )
86
92
case ( _, . tuple? ) :
87
93
description = count == 1 ? " (1 element) " : " ( \( count) elements) "
88
94
case ( _, . optional? ) :
@@ -95,10 +101,19 @@ private func snap<T>(_ value: T, name: String? = nil, indent: Int = 0) -> String
95
101
return " \( indentation) - \( name. map { " \( $0) : " } ?? " " ) \( value. snapshotDescription) \n "
96
102
case ( let value as CustomStringConvertible , _) :
97
103
description = value. description
98
- case ( _, . class? ) , ( _, . struct? ) :
104
+ case let ( value as AnyObject , . class? ) :
105
+ let objectID = ObjectIdentifier ( value)
106
+ if visitedValues. contains ( objectID) {
107
+ return " \( indentation) \( bullet) \( name ?? " value " ) (circular reference detected) \n "
108
+ }
109
+ visitedValues. insert ( objectID)
110
+ description = String ( describing: mirror. subjectType)
111
+ . replacingOccurrences ( of: " # \\ d+ " , with: " " , options: . regularExpression)
112
+ children = sort ( children, visitedValues: visitedValues)
113
+ case ( _, . struct? ) :
99
114
description = String ( describing: mirror. subjectType)
100
115
. replacingOccurrences ( of: " # \\ d+ " , with: " " , options: . regularExpression)
101
- children = sort ( children)
116
+ children = sort ( children, visitedValues : visitedValues )
102
117
case ( _, . enum? ) :
103
118
let subjectType = String ( describing: mirror. subjectType)
104
119
. replacingOccurrences ( of: " # \\ d+ " , with: " " , options: . regularExpression)
@@ -109,15 +124,15 @@ private func snap<T>(_ value: T, name: String? = nil, indent: Int = 0) -> String
109
124
110
125
let lines =
111
126
[ " \( indentation) \( bullet) \( name. map { " \( $0) : " } ?? " " ) \( description) \n " ]
112
- + children. map { snap ( $1, name: $0, indent: indent + 2 ) }
127
+ + children. map { snap ( $1, name: $0, indent: indent + 2 , visitedValues : visitedValues ) }
113
128
114
129
return lines. joined ( )
115
130
}
116
131
117
- private func sort( _ children: Mirror . Children ) -> Mirror . Children {
132
+ private func sort( _ children: Mirror . Children , visitedValues : Set < ObjectIdentifier > ) -> Mirror . Children {
118
133
return . init(
119
134
children
120
- . map ( { ( child: $0, snap: snap ( $0) ) } )
135
+ . map ( { ( child: $0, snap: snap ( $0, visitedValues : visitedValues ) ) } )
121
136
. sorted ( by: { $0. snap < $1. snap } )
122
137
. map ( { $0. child } )
123
138
)
0 commit comments