@@ -21,42 +21,62 @@ fileprivate let emptyNameString = ""
21
21
22
22
extension LogEntry {
23
23
init ( describing instance: Any , name: String ? = nil , policy: LogPolicy ) throws {
24
- self = try . init( describing: instance, name: name ?? emptyNameString, typeName: nil , summary : nil , policy: policy, currentDepth: 0 )
24
+ self = try . init( describing: instance, name: name ?? emptyNameString, typeName: nil , policy: policy, currentDepth: 0 )
25
25
}
26
26
27
- fileprivate init ( describing instance: Any , name: String , typeName passedInTypeName: String ? , summary passedInSummary : String ? , policy: LogPolicy , currentDepth: Int ) throws {
27
+ fileprivate init ( describing instance: Any , name: String , typeName passedInTypeName: String ? , policy: LogPolicy , currentDepth: Int ) throws {
28
28
guard currentDepth <= policy. maximumDepth else {
29
29
// We're trying to log an instance that is "too deep"; as a result, we need to just return a gap.
30
30
self = . gap
31
31
return
32
32
}
33
33
34
- // Returns either the passed-in type name/summary or the type name/summary of `instance`.
35
- var typeName : String { return passedInTypeName ?? normalizedName ( of: type ( of: instance) ) }
36
- var summary : String { return passedInSummary ?? String ( describing: instance) }
34
+ // Lazily-load the Mirror for this instance. (This is factored out this way as the Mirror is needed in a few different code paths.)
35
+ var _mirrorStorage : Mirror ? = nil
36
+ var mirror : Mirror {
37
+ if let mirror = _mirrorStorage {
38
+ return mirror
39
+ }
40
+
41
+ let mirror = Mirror ( reflecting: instance)
42
+ _mirrorStorage = mirror
43
+ return mirror
44
+ }
45
+
46
+ // Lazily-load the normalized type name for this instance. (This is factored out this way as the type name is expensive to compute, so we only want to do it once.)
47
+ var _typeNameStorage : String ? = nil
48
+ var typeName : String {
49
+ if let typeName = _typeNameStorage {
50
+ return typeName
51
+ }
52
+
53
+ let typeName = passedInTypeName ?? normalizedName ( of: type ( of: instance) )
54
+ _typeNameStorage = typeName
55
+ return typeName
56
+ }
37
57
38
58
// For types which conform to the `CustomPlaygroundDisplayConvertible` protocol, get their custom representation and then run it back through the initializer.
39
59
if let customPlaygroundDisplayConvertible = instance as? CustomPlaygroundDisplayConvertible {
40
- self = try . init( describing: customPlaygroundDisplayConvertible. playgroundDescription, name: name, typeName: typeName, summary : nil , policy: policy, currentDepth: currentDepth)
60
+ self = try . init( describing: customPlaygroundDisplayConvertible. playgroundDescription, name: name, typeName: typeName, policy: policy, currentDepth: currentDepth)
41
61
}
42
62
43
63
// For types which conform to the `CustomOpaqueLoggable` protocol, get their custom representation and construct an opaque log entry. (This is checked *second* so that user implementations of `CustomPlaygroundDisplayConvertible` are honored over this framework's implementations of `CustomOpaqueLoggable`.)
44
64
else if let customOpaqueLoggable = instance as? CustomOpaqueLoggable {
45
65
// TODO: figure out when to set `preferBriefSummary` to true
46
- self = try . opaque( name: name, typeName: typeName, summary: summary , preferBriefSummary: false , representation: customOpaqueLoggable. opaqueRepresentation ( ) )
66
+ self = try . opaque( name: name, typeName: typeName, summary: generateSummary ( for : instance , withTypeName : typeName , using : mirror ) , preferBriefSummary: false , representation: customOpaqueLoggable. opaqueRepresentation ( ) )
47
67
}
48
68
49
69
// For types which conform to the legacy `CustomPlaygroundQuickLookable` or `_DefaultCustomPlaygroundQuickLookable` protocols, get their `PlaygroundQuickLook` and use that for logging.
50
70
else if let customQuickLookable = instance as? CustomPlaygroundQuickLookable {
51
- self = try . init( playgroundQuickLook: customQuickLookable. customPlaygroundQuickLook, name: name, typeName: typeName, summary: summary )
71
+ self = try . init( playgroundQuickLook: customQuickLookable. customPlaygroundQuickLook, name: name, typeName: typeName, summary: generateSummary ( for : instance , withTypeName : typeName , using : mirror ) )
52
72
}
53
73
else if let defaultQuickLookable = instance as? _DefaultCustomPlaygroundQuickLookable {
54
- self = try . init( playgroundQuickLook: defaultQuickLookable. _defaultCustomPlaygroundQuickLook, name: name, typeName: typeName, summary: summary )
74
+ self = try . init( playgroundQuickLook: defaultQuickLookable. _defaultCustomPlaygroundQuickLook, name: name, typeName: typeName, summary: generateSummary ( for : instance , withTypeName : typeName , using : mirror ) )
55
75
}
56
76
57
77
// If a type implements the `debugQuickLookObject()` Objective-C method, then get their debug quick look object and use that for logging (by passing it back through this initializer).
58
78
else if let debugQuickLookObjectMethod = ( instance as AnyObject ) . debugQuickLookObject, let debugQuickLookObject = debugQuickLookObjectMethod ( ) {
59
- self = try . init( describing: debugQuickLookObject, name: name, typeName: typeName, summary : nil , policy: policy, currentDepth: currentDepth)
79
+ self = try . init( describing: debugQuickLookObject, name: name, typeName: typeName, policy: policy, currentDepth: currentDepth)
60
80
}
61
81
62
82
// Otherwise, first check if this is an interesting CF type before logging structure.
@@ -65,23 +85,20 @@ extension LogEntry {
65
85
switch CFGetTypeID ( instance as CFTypeRef ) {
66
86
case CGColor . typeID:
67
87
let cgColor = instance as! CGColor
68
- self = . opaque( name: name, typeName: typeName, summary: summary , preferBriefSummary: false , representation: cgColor. opaqueRepresentation ( ) )
88
+ self = . opaque( name: name, typeName: typeName, summary: generateSummary ( for : instance , withTypeName : typeName , using : mirror ) , preferBriefSummary: false , representation: cgColor. opaqueRepresentation ( ) )
69
89
case CGImage . typeID:
70
90
let cgImage = instance as! CGImage
71
- self = . opaque( name: name, typeName: typeName, summary: summary , preferBriefSummary: false , representation: cgImage. opaqueRepresentation ( ) )
91
+ self = . opaque( name: name, typeName: typeName, summary: generateSummary ( for : instance , withTypeName : typeName , using : mirror ) , preferBriefSummary: false , representation: cgImage. opaqueRepresentation ( ) )
72
92
default :
73
93
// This isn't one of the CF types we want to specially handle, so the log entry should just reflect the instance's structure.
74
-
75
- // Get a Mirror which reflects the instance being logged.
76
- let mirror = Mirror ( reflecting: instance)
77
-
94
+
78
95
if mirror. displayStyle == . optional && mirror. children. count == 1 {
79
96
// If the mirror displays as an Optional and has exactly one child, then we want to unwrap the optionality and generate a log entry for the child.
80
- self = try . init( describing: mirror. children. first!. value, name: name, typeName: nil , summary : nil , policy: policy, currentDepth: currentDepth)
97
+ self = try . init( describing: mirror. children. first!. value, name: name, typeName: nil , policy: policy, currentDepth: currentDepth)
81
98
}
82
99
else {
83
100
// Otherwise, we want to generate a log entry with the structure from the mirror.
84
- self = . init( structureFrom: mirror, name: name, typeName: typeName, summary: summary , policy: policy, currentDepth: currentDepth)
101
+ self = . init( structureFrom: mirror, name: name, typeName: typeName, summary: generateSummary ( for : instance , withTypeName : typeName , using : mirror ) , policy: policy, currentDepth: currentDepth)
85
102
}
86
103
}
87
104
}
@@ -167,7 +184,7 @@ extension Mirror {
167
184
168
185
func logEntry( forChild child: Mirror . Child ) -> LogEntry {
169
186
do {
170
- return try LogEntry ( describing: child. value, name: child. label ?? emptyNameString, typeName: nil , summary : nil , policy: policy, currentDepth: childDepth)
187
+ return try LogEntry ( describing: child. value, name: child. label ?? emptyNameString, typeName: nil , policy: policy, currentDepth: childDepth)
171
188
}
172
189
catch let LoggingError . failedToGenerateOpaqueRepresentation( reason) {
173
190
return LogEntry . error ( reason: reason)
@@ -249,3 +266,27 @@ extension Mirror {
249
266
return LogEntry ( structureFrom: self , name: name, typeName: subjectTypeName, summary: subjectTypeName, policy: policy, currentDepth: depth)
250
267
}
251
268
}
269
+
270
+ /// Construct the summary for `instance`.
271
+ ///
272
+ /// In precedence order, the rules are:
273
+ /// - If the instance is itself a `String`, return the instance
274
+ /// - If the instance is `CustomStringConvertible` or `CustomDebugStringConvertible`, use `String(reflecting:)`
275
+ /// - If the instance is an enum (as reported using Mirror), use `String(describing:)`
276
+ /// - Otherwise, use the normalized type name
277
+ fileprivate func generateSummary( for instance: Any , withTypeName typeNameProvider: @autoclosure ( ) -> String , using mirrorProvider: @autoclosure ( ) -> Mirror ) -> String {
278
+ if let string = instance as? String {
279
+ return string
280
+ }
281
+
282
+ if instance is CustomStringConvertible || instance is CustomDebugStringConvertible {
283
+ return String ( reflecting: instance)
284
+ }
285
+
286
+ let mirror = mirrorProvider ( )
287
+ if mirror. displayStyle == . enum {
288
+ return String ( describing: instance)
289
+ }
290
+
291
+ return typeNameProvider ( )
292
+ }
0 commit comments