Skip to content

Commit

Permalink
Added support for optional types
Browse files Browse the repository at this point in the history
  • Loading branch information
emilrb committed Sep 1, 2024
1 parent 1371dba commit 3be0758
Show file tree
Hide file tree
Showing 3 changed files with 26 additions and 5 deletions.
1 change: 1 addition & 0 deletions Sources/PublicMemberwiseInitializerClient/main.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import PublicMemberwiseInitializer
@PublicMemberwiseInitializer
public struct MyStruct {
let myCoolString: String
let myOptionalString: String?
let someInt: Int, anotherInt: Int
let a, b: String, c, d: Int
let e = "String"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import SwiftSyntaxMacros
public struct PublicMemberwiseInitializerMacro: MemberMacro {
enum PublicMemberwiseInitializerMacroError: Error {
case unsupportedType
case unsupportedVarTypeSyntax
case missingTypeAnnotation
}
public static func expansion(
Expand All @@ -18,22 +19,22 @@ public struct PublicMemberwiseInitializerMacro: MemberMacro {
}
let variableDeclarations = declaration.memberBlock.members.compactMap({ $0.decl.as(VariableDeclSyntax.self) })
let rows = try variableDeclarations.flatMap { declaration in
try (declaration.bindings.as(PatternBindingListSyntax.self)?.compactMap { patternBinding -> (IdentifierPatternSyntax, IdentifierTypeSyntax?)? in
try (declaration.bindings.as(PatternBindingListSyntax.self)?.compactMap { patternBinding -> (IdentifierPatternSyntax, TypeSyntaxProtocol?)? in
guard patternBinding.accessorBlock == nil && patternBinding.initializer == nil else {
return nil // Ignore computed properties
}

if let identifier = patternBinding.pattern.as(IdentifierPatternSyntax.self) {
// Get the type, if available
let typeAnnotation = patternBinding.typeAnnotation?.type.as(IdentifierTypeSyntax.self)
let typeAnnotation: TypeSyntaxProtocol? = patternBinding.typeAnnotation?.type.as(IdentifierTypeSyntax.self) ?? patternBinding.typeAnnotation?.type.as(OptionalTypeSyntax.self)
return (identifier, typeAnnotation)
}
return nil
} ?? []
)
.reversed()
// Backfill any ommited types
.reduce([(IdentifierPatternSyntax, IdentifierTypeSyntax)]()) { partialResult, identifierAndTypeAnnotation in
.reduce([(IdentifierPatternSyntax, TypeSyntaxProtocol)]()) { partialResult, identifierAndTypeAnnotation in
var partialResult = partialResult
guard let typeAnnotation = identifierAndTypeAnnotation.1 ?? partialResult.last?.1 else {
throw PublicMemberwiseInitializerMacroError.missingTypeAnnotation
Expand All @@ -51,8 +52,17 @@ public struct PublicMemberwiseInitializerMacro: MemberMacro {
"""
]
}
let initParams = rows.map { (identifier, type) in
"\(identifier.identifier.text): \(type.name)"
let initParams = try rows.map { (identifier, type) in
let typeString: String
if let identifierType = type.as(IdentifierTypeSyntax.self)?.name {
typeString = identifierType.text
} else if let optionalTypeSyntax = type.as(OptionalTypeSyntax.self),
let identifierType = optionalTypeSyntax.wrappedType.as(IdentifierTypeSyntax.self)?.name {
typeString = "\(identifierType.text)?"
} else {
throw PublicMemberwiseInitializerMacroError.unsupportedVarTypeSyntax
}
return "\(identifier.identifier.text): \(typeString)"
}
.joined(separator: ",\n")
let initAssignments = rows.map { (identifier, type) in
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ final class PublicMemberwiseInitializerTests: XCTestCase {
@PublicMemberwiseInitializerMacro
public struct MyStruct {
let myCoolString: String
let myOptionalString: String?
let someInt: Int, anotherInt: Int
let aOptional: Int?, anotherOptional: String?
let a, b: String, c, d: Int
let e = "String"
let f: Int
Expand All @@ -30,24 +32,32 @@ final class PublicMemberwiseInitializerTests: XCTestCase {
expandedSource: """
public struct MyStruct {
let myCoolString: String
let myOptionalString: String?
let someInt: Int, anotherInt: Int
let aOptional: Int?, anotherOptional: String?
let a, b: String, c, d: Int
let e = "String"
let f: Int
public init(
myCoolString: String,
myOptionalString: String?,
someInt: Int,
anotherInt: Int,
aOptional: Int?,
anotherOptional: String?,
a: String,
b: String,
c: Int,
d: Int,
f: Int
) {
self.myCoolString = myCoolString
self.myOptionalString = myOptionalString
self.someInt = someInt
self.anotherInt = anotherInt
self.aOptional = aOptional
self.anotherOptional = anotherOptional
self.a = a
self.b = b
self.c = c
Expand Down

0 comments on commit 3be0758

Please sign in to comment.