Created
May 2, 2019 16:15
-
-
Save a-voronov/9a1236daa5bd0b2e99d1cfcb7daf2dc8 to your computer and use it in GitHub Desktop.
Pretty Swift.Any description
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
func prettyDescription(of subject: Any, indentCoeff: Int = 0) -> String { | |
let mirror = Mirror(reflecting: subject) | |
let properties = mirror.children | |
guard let displayStyle = mirror.displayStyle else { | |
return String(reflecting: subject) | |
} | |
let indent = "\t" | |
let globalIndent = String(repeating: indent, count: indentCoeff) | |
switch displayStyle { | |
case .tuple: | |
guard !properties.isEmpty else { | |
return "()" | |
} | |
let descriptions = properties.reduce([String]()) { acc, property in | |
let label = property.label.map { lbl in lbl.first == "." ? "" : lbl + ": " } ?? "" | |
return acc + [label + prettyDescription(of: property.value, indentCoeff: indentCoeff + 1)] | |
} | |
return aligned(items: descriptions, indent: indent, globalIndent: globalIndent, start: "(", end: ")") | |
case .collection, .set: | |
guard !properties.isEmpty else { | |
return "[]" | |
} | |
let descriptions = properties.reduce([String]()) { acc, property in | |
return acc + [prettyDescription(of: property.value, indentCoeff: indentCoeff + 1)] | |
} | |
return aligned(items: descriptions, indent: indent, globalIndent: globalIndent, start: "[", end: "]") | |
case .dictionary: | |
guard !properties.isEmpty else { | |
return "[:]" | |
} | |
let descriptions = properties.reduce([String]()) { acc, property in | |
let pair = Array(Mirror(reflecting: property.value).children) | |
let key = prettyDescription(of: pair[0].value, indentCoeff: indentCoeff + 1) | |
let value = prettyDescription(of: pair[1].value, indentCoeff: indentCoeff + 1) | |
return acc + [key + ": " + value] | |
} | |
return aligned(items: descriptions, indent: indent, globalIndent: globalIndent, start: "[", end: "]") | |
case .enum: | |
if let subject = subject as? CustomDebugStringConvertible { | |
return subject.debugDescription | |
} | |
guard let property = properties.first else { | |
return String(reflecting: subject) | |
} | |
let associatedValue = prettyDescription(of: property.value, indentCoeff: indentCoeff) | |
let associatedValueDescription = associatedValue.first == "(" && associatedValue.last == ")" | |
? associatedValue | |
: "(\(associatedValue))" | |
return "\(String(reflecting: mirror.subjectType)).\(property.label ?? "")" + associatedValueDescription | |
case .struct, .class: | |
if let subject = subject as? CustomDebugStringConvertible { | |
return subject.debugDescription | |
} | |
guard !properties.isEmpty else { | |
return String(reflecting: subject) | |
} | |
let descriptions = properties.reduce([String]()) { acc, property in | |
let label = property.label.map { $0 + ": " } ?? "" | |
return acc + [label + prettyDescription(of: property.value, indentCoeff: indentCoeff + 1)] | |
} | |
let description = aligned(items: descriptions, indent: indent, globalIndent: globalIndent, start: "(", end: ")") | |
return String(reflecting: mirror.subjectType) + description | |
case .optional: | |
guard let property = properties.first else { | |
return String(reflecting: subject) | |
} | |
return prettyDescription(of: property.value, indentCoeff: indentCoeff + 1) | |
} | |
} | |
func aligned(items: [String], indent: String, globalIndent: String, start: String, end: String) -> String { | |
let description = items | |
.map { globalIndent + indent + $0 } | |
.joined(separator: ",\n") | |
return "\(start)\n" + description + "\n" + globalIndent + end | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Example
Some dummy data - nested types, optionals, full/empty collections, enums with/without associated values, tuples, functions, etc.
Prettified result:
Standard reflecting print
(x: 42, xx: (y: "yoo", z: ["b": Me.En.e, "e": Me.En.f(42), "c": (e: 22, f: 53), "pff": Me.Foo(), "d": [:], "ee": Me.En.g(27, "buzzzz"), "a": [1, 2, 3, 4, 5]], zz: (t: "moo", u: "foo", f: Me.Moo), fizz: Fizz(buzz: Optional(Buzz(x: Optional(23), fuzz: Fuzz)), fn: (Function), foo: "foo"), buzz: Buzz(x: Optional(23), fuzz: Fuzz)))