Skip to content

Commit da8df86

Browse files
committed
Fix mirror can't parse Optional<Value> and Nested Array
1 parent 11293b6 commit da8df86

File tree

2 files changed

+163
-6
lines changed

2 files changed

+163
-6
lines changed

Sources/Lookup/Mirrors.swift

Lines changed: 99 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,26 @@
44

55
import Foundation
66

7+
protocol OptionalProtocol {
8+
var isNil: Bool { get }
9+
}
10+
extension Optional: OptionalProtocol {
11+
var isNil: Bool { self == nil }
12+
}
13+
714
func canMirrorInto(_ reflecting: Any?) -> Bool {
815
if let _ = reflecting as? LookupRawValue {
916
return false
1017
}
1118
guard let ref = reflecting else { return false }
1219
let mirror = Mirror(reflecting: ref)
1320
guard let displayStyle = mirror.displayStyle else { return false }
14-
return (displayStyle == .class || displayStyle == .struct)
21+
switch displayStyle {
22+
case .class, .struct:
23+
return true
24+
default:
25+
return canMirrorInto(mirror.children.first?.value)
26+
}
1527
}
1628

1729
func mirrorValue(_ value: Any) -> Any {
@@ -25,6 +37,34 @@ func mirrorValue(_ value: Any) -> Any {
2537
return "\(value)"
2638
}
2739

40+
func unwrapValue(_ value: Any) -> Any? {
41+
let mirror = Mirror(reflecting: value)
42+
if mirror.displayStyle == .optional {
43+
return mirror.children.first?.value
44+
}
45+
return value
46+
}
47+
48+
func mirrorArray(_ array: [Any], _ each: ((_: String?, _: Any) -> Void)?) -> [Any] {
49+
return array.compactMap { item in
50+
if let nested = item as? [Any] {
51+
let arr = mirrorArray(nested, each)
52+
return arr.isEmpty ? nil : arr
53+
} else if canMirrorInto(item) {
54+
let mirrored = mirrors(reflecting: item, each)
55+
return mirrored.isEmpty ? nil : mirrored
56+
} else {
57+
let value = mirrorValue(item)
58+
if let opt = value as? OptionalProtocol, opt.isNil {
59+
return nil
60+
} else if value is NSNull {
61+
return nil
62+
}
63+
return value
64+
}
65+
}
66+
}
67+
2868
public func mirrors(reflecting: Any?, _ each: ((_: String?, _: Any) -> Void)? = nil) -> [String: Any] {
2969
guard let reflecting = reflecting else { return [:] }
3070

@@ -34,9 +74,36 @@ public func mirrors(reflecting: Any?, _ each: ((_: String?, _: Any) -> Void)? =
3474
for child in mirror.children {
3575
if let label = child.label, !label.isEmpty {
3676
if let unwrap = reflecting as? LookupUnwrap, let unwrapped = unwrap.lookupUnwrap(key: label, value: child.value) {
37-
map[label] = mirrorValue(unwrapped)
77+
let value = mirrorValue(unwrapped)
78+
if let opt = value as? OptionalProtocol, opt.isNil {
79+
// skip
80+
} else if value is NSNull {
81+
// skip
82+
} else {
83+
map[label] = value
84+
}
3885
} else {
39-
map[label] = canMirrorInto(child.value) ? mirrors(reflecting: child.value, each) : mirrorValue(child.value)
86+
let value = unwrapValue(child.value)
87+
if let array = value as? [Any] {
88+
let mirroredArray = mirrorArray(array, each)
89+
if !mirroredArray.isEmpty {
90+
map[label] = mirroredArray
91+
}
92+
} else if canMirrorInto(value) {
93+
let mirroredDict = mirrors(reflecting: value, each)
94+
if !mirroredDict.isEmpty {
95+
map[label] = mirroredDict
96+
}
97+
} else {
98+
let value = mirrorValue(child.value)
99+
if let opt = value as? OptionalProtocol, opt.isNil {
100+
// skip
101+
} else if value is NSNull {
102+
// skip
103+
} else {
104+
map[label] = value
105+
}
106+
}
40107
}
41108
}
42109
each?(child.label, child.value)
@@ -47,9 +114,36 @@ public func mirrors(reflecting: Any?, _ each: ((_: String?, _: Any) -> Void)? =
47114
for child in superMirror!.children {
48115
if let label = child.label, !label.isEmpty {
49116
if let unwrap = reflecting as? LookupUnwrap, let unwrapped = unwrap.lookupUnwrap(key: label, value: child.value) {
50-
map[label] = mirrorValue(unwrapped)
117+
let value = mirrorValue(unwrapped)
118+
if let opt = value as? OptionalProtocol, opt.isNil {
119+
// skip
120+
} else if value is NSNull {
121+
// skip
122+
} else {
123+
map[label] = value
124+
}
51125
} else {
52-
map[label] = canMirrorInto(child.value) ? mirrors(reflecting: child.value, each) : mirrorValue(child.value)
126+
let value = unwrapValue(child.value)
127+
if let array = value as? [Any] {
128+
let mirroredArray = mirrorArray(array, each)
129+
if !mirroredArray.isEmpty {
130+
map[label] = mirroredArray
131+
}
132+
} else if canMirrorInto(value) {
133+
let mirroredDict = mirrors(reflecting: value, each)
134+
if !mirroredDict.isEmpty {
135+
map[label] = mirroredDict
136+
}
137+
} else {
138+
let value = mirrorValue(child.value)
139+
if let opt = value as? OptionalProtocol, opt.isNil {
140+
// skip
141+
} else if value is NSNull {
142+
// skip
143+
} else {
144+
map[label] = value
145+
}
146+
}
53147
}
54148
}
55149
each?(child.label, child.value)
@@ -58,4 +152,3 @@ public func mirrors(reflecting: Any?, _ each: ((_: String?, _: Any) -> Void)? =
58152
}
59153
return map
60154
}
61-

Tests/LookupTests/LookupTests.swift

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,39 @@ open class AnimalClass {
3131
let intType: AnimalIntType = .dog
3232
}
3333

34+
struct KeyboardButton: Codable {
35+
let text: String
36+
let url: String?
37+
let callbackData: String?
38+
39+
enum CodingKeys: String, CodingKey {
40+
case text
41+
case url
42+
case callbackData = "callback_data"
43+
}
44+
init(text: String, url: String? = nil, callbackData: String? = nil) {
45+
self.text = text
46+
self.url = url
47+
self.callbackData = callbackData
48+
}
49+
}
50+
51+
struct Markup: Codable {
52+
var keyboards: [[KeyboardButton]]
53+
init(keyboards buttons: [[KeyboardButton]]) {
54+
self.keyboards = buttons
55+
}
56+
enum CodingKeys: String, CodingKey {
57+
case keyboards = "inline_keyboard"
58+
}
59+
}
60+
61+
struct MessageReply: Codable {
62+
let text: String
63+
let toID: Int
64+
let markup: Markup?
65+
}
66+
3467
final class Species: AnimalClass {
3568
let start: Date = Date()
3669
}
@@ -424,6 +457,37 @@ struct LookupTests {
424457
#expect(encoded?.contains("1") == true)
425458
}
426459

460+
@Test("Test ExpressionByDictionary")
461+
func testExpressionByDictionary() throws {
462+
let lookup = Lookup(["ids": [UUID(), UUID(), UUID()]])
463+
#expect(lookup.ids.count == 3)
464+
}
465+
466+
@Test("Test Nesting Codable")
467+
func testNestingCodable() throws {
468+
let markup = MessageReply(
469+
text: "This is a reply message!",
470+
toID: 10086,
471+
markup: Markup(
472+
keyboards: [
473+
[KeyboardButton(text: "Hang up", callbackData: "/hang-up")],
474+
[KeyboardButton(text: "Recording", callbackData: "/recording")]
475+
]
476+
)
477+
)
478+
let lookup = Lookup(markup)
479+
print(lookup.description)
480+
#expect(lookup.text.string == "This is a reply message!")
481+
#expect(lookup.toID.string == "10086")
482+
#expect(lookup.toID.int == 10086)
483+
484+
#expect(lookup.markup.keyboards.0.0.text.string == "Hang up")
485+
#expect(lookup.markup.keyboards.0.0.callbackData.string == "/hang-up")
486+
487+
#expect(lookup.markup.keyboards.1.0.text.string == "Recording")
488+
#expect(lookup.markup.keyboards.1.0.callbackData.string == "/recording")
489+
}
490+
427491
@Test("Test Unwrap")
428492
func testUnwrap() throws {
429493
let model = UnwrapModel(id: UUID(), age: 1, type: .cat, intType: .cat, date: Date())

0 commit comments

Comments
 (0)