Skip to content

Commit b7fa48b

Browse files
Added new codable for StringOrDouble (#17)
1 parent 51bb99c commit b7fa48b

File tree

7 files changed

+106
-0
lines changed

7 files changed

+106
-0
lines changed

Sources/SageSwiftKit/CodableMacros.swift

+3
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ public macro CustomCodable(nestedProperty: String? = nil) = #externalMacro(modul
2020
@attached(peer)
2121
public macro StringOrInt() = #externalMacro(module: "SageSwiftKitMacros", type: "StringOrInt")
2222

23+
@attached(peer)
24+
public macro StringOrDouble() = #externalMacro(module: "SageSwiftKitMacros", type: "StringOrDouble")
25+
2326
@attached(peer)
2427
public macro StringToDouble() = #externalMacro(module: "SageSwiftKitMacros", type: "StringToDouble")
2528

Sources/SageSwiftKitClient/main.swift

+3
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ struct PlayingObject {
2222

2323
@StringOrInt
2424
var value: String?
25+
26+
@StringOrDouble
27+
var sum: String?
2528

2629
@CustomURL
2730
var createdOn: URL?

Sources/SageSwiftKitMacros/CodableMacros/Builders/DecodeVariableBuild.swift

+48
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ enum DecodingAttribute: Identifiable, Equatable {
1111
case date(AttributeSyntax)
1212
case url(AttributeSyntax)
1313
case stringOrInt(AttributeSyntax)
14+
case stringOrDouble(AttributeSyntax)
1415
case stringToDouble(AttributeSyntax)
1516

1617
var id: String {
@@ -23,6 +24,8 @@ enum DecodingAttribute: Identifiable, Equatable {
2324
return String(describing: CustomURL.self)
2425
case .stringOrInt:
2526
return String(describing: StringOrInt.self)
27+
case .stringOrDouble:
28+
return String(describing: StringOrDouble.self)
2629
case .stringToDouble:
2730
return String(describing: StringToDouble.self)
2831
}
@@ -38,6 +41,8 @@ enum DecodingAttribute: Identifiable, Equatable {
3841
return attributeSyntax
3942
case .stringOrInt(let attributeSyntax):
4043
return attributeSyntax
44+
case .stringOrDouble(let attributeSyntax):
45+
return attributeSyntax
4146
case .stringToDouble(let attributeSyntax):
4247
return attributeSyntax
4348
}
@@ -87,6 +92,10 @@ struct DecodeVariableBuild {
8792
return .stringOrInt(variableAttribute)
8893
}
8994

95+
if variableAttribute.adapter.name == String(describing: StringOrDouble.self) {
96+
return .stringOrDouble(variableAttribute)
97+
}
98+
9099
if variableAttribute.adapter.name == String(describing: StringToDouble.self) {
91100
return .stringToDouble(variableAttribute)
92101
}
@@ -112,6 +121,10 @@ struct DecodeVariableBuild {
112121
return buildStringOrInt(attribute: attribute)
113122
}
114123

124+
if let attribute = variableDecodingAttributes.getAttribute(macro: StringOrDouble.self)?.attribute {
125+
return buildStringOrDouble(attribute: attribute)
126+
}
127+
115128
if let attribute = variableDecodingAttributes.getAttribute(macro: StringToDouble.self)?.attribute {
116129
return buildStringToDouble(attribute: attribute)
117130
}
@@ -207,6 +220,41 @@ struct DecodeVariableBuild {
207220
return .init(otherBuilder: ifBuilder)
208221
}
209222

223+
func buildStringOrDouble(attribute: AttributeSyntax) -> CodeBlockItemSyntaxBuilder {
224+
guard type == "String" else {
225+
return buildBasicDecode()
226+
}
227+
228+
let conditionalName = "tmp"+varName.capitalized
229+
230+
let ifBody: [CodeBlockItemSyntaxBuilder] = [
231+
.init(code: "\(varName) = \(conditionalName)")
232+
]
233+
234+
let elseIfBody: [CodeBlockItemSyntaxBuilder] = [
235+
.init(code: "\(varName) = String(\(conditionalName))")
236+
]
237+
238+
let elseBody: [CodeBlockItemSyntaxBuilder] = [
239+
.init(code: "\(varName) = nil")
240+
]
241+
242+
let elseIfBuilder = IfExprSyntaxBuilder(
243+
condition: "if let \(conditionalName) = try? container.decode(Double.self, forKey: .\(varName))",
244+
body: elseIfBody,
245+
elseBody: elseBody
246+
)
247+
let elseIfCode = CodeBlockItemSyntaxBuilder.init(otherBuilder: elseIfBuilder)
248+
249+
let ifBuilder = IfExprSyntaxBuilder(
250+
condition: "if let \(conditionalName) = try? container.decode(String.self, forKey: .\(varName))",
251+
body: ifBody,
252+
elseBody: [elseIfCode]
253+
)
254+
255+
return .init(otherBuilder: ifBuilder)
256+
}
257+
210258
func buildStringToDouble(attribute: AttributeSyntax) -> CodeBlockItemSyntaxBuilder {
211259

212260
guard type == "Double" else {

Sources/SageSwiftKitMacros/CodableMacros/CobableMacros.swift

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ struct CobableMacros: CompilerPlugin {
1616
CustomDate.self,
1717
CustomURL.self,
1818
StringOrInt.self,
19+
StringOrDouble.self,
1920
StringToDouble.self
2021
]
2122
}

Sources/SageSwiftKitMacros/CodableMacros/Macros/CustomDecodingMacros.swift

+6
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,12 @@ public enum StringOrInt: PeerMacro {
3131
}
3232
}
3333

34+
public enum StringOrDouble: PeerMacro {
35+
public static func expansion(of node: SwiftSyntax.AttributeSyntax, providingPeersOf declaration: some SwiftSyntax.DeclSyntaxProtocol, in context: some SwiftSyntaxMacros.MacroExpansionContext) throws -> [SwiftSyntax.DeclSyntax] {
36+
[]
37+
}
38+
}
39+
3440
public enum StringToDouble: PeerMacro {
3541
public static func expansion(of node: SwiftSyntax.AttributeSyntax, providingPeersOf declaration: some SwiftSyntax.DeclSyntaxProtocol, in context: some SwiftSyntaxMacros.MacroExpansionContext) throws -> [SwiftSyntax.DeclSyntax] {
3642
[]

Tests/SageSwiftKitTests/CodableMacrosTests/CustomDecodableTests.swift

+44
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,50 @@ final class CustomDecodableTests: XCTestCase {
170170
)
171171
#else
172172
throw XCTSkip("macros are only supported when running tests for the host platform")
173+
#endif
174+
}
175+
176+
func testStringOrDouble() throws {
177+
#if canImport(SageSwiftKitMacros)
178+
assertMacroExpansion(
179+
"""
180+
@CustomCodable
181+
struct PlayingObject {
182+
@StringOrDouble
183+
var value: String
184+
}
185+
""",
186+
expandedSource: """
187+
struct PlayingObject {
188+
var value: String
189+
190+
enum CodingKeys: String, CodingKey {
191+
case value
192+
}
193+
194+
public init(from decoder: any Decoder) throws {
195+
let container = try decoder.container(keyedBy: CodingKeys.self)
196+
if let tmpValue = try? container.decode(String.self, forKey: .value) {
197+
value = tmpValue
198+
} else {
199+
if let tmpValue = try? container.decode(Double.self, forKey: .value) {
200+
value = String(tmpValue)
201+
} else {
202+
value = nil
203+
}
204+
}
205+
}
206+
207+
public func encode(to encoder: Encoder) throws {
208+
var container = encoder.container(keyedBy: CodingKeys.self)
209+
try container.encode(value, forKey: .value)
210+
}
211+
}
212+
""",
213+
macros: codableMacros
214+
)
215+
#else
216+
throw XCTSkip("macros are only supported when running tests for the host platform")
173217
#endif
174218
}
175219
}

Tests/SageSwiftKitTests/SageSwiftKitTests.swift

+1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ let codableMacros: [String: Macro.Type] = [
1919
"CustomDate": CustomDate.self,
2020
"CustomURL": CustomURL.self,
2121
"StringOrInt": StringOrInt.self,
22+
"StringOrDouble": StringOrDouble.self,
2223
"StringToDouble": StringToDouble.self
2324
]
2425

0 commit comments

Comments
 (0)