Skip to content

Commit

Permalink
#34: Simplify endpoint macro interface- add parent argument
Browse files Browse the repository at this point in the history
  • Loading branch information
tdeleon committed Jan 4, 2024
1 parent 0ea9f31 commit 1308766
Show file tree
Hide file tree
Showing 4 changed files with 14 additions and 80 deletions.
2 changes: 1 addition & 1 deletion Sources/Relax/Macros/APIEndpoint.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@
import Foundation

@attached(extension, conformances: Endpoint, names: named(Parent), named(path))
public macro APIEndpoint<APIComponent>(_ path: String) = #externalMacro(module: "RelaxMacros", type: "APIEndpointMacro")
public macro APIEndpoint(_ path: String, parent: APIComponent.Type) = #externalMacro(module: "RelaxMacros", type: "APIEndpointMacro")
#endif
40 changes: 11 additions & 29 deletions Sources/RelaxMacros/APIEndpointMacro.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,16 @@ public struct APIEndpointMacro: ExtensionMacro {
conformingTo protocols: [TypeSyntax],
in context: some MacroExpansionContext
) throws -> [ExtensionDeclSyntax] {
guard let parent = node
.arguments?.as(LabeledExprListSyntax.self)?
.first(where: { $0.label?.text == "parent" })?
.expression.as(MemberAccessExprSyntax.self)?
.base?
.description
else {
return []
}

guard let path = node
.arguments?.as(LabeledExprListSyntax.self)?
.first?.expression.as(StringLiteralExprSyntax.self)?
Expand All @@ -28,20 +38,8 @@ public struct APIEndpointMacro: ExtensionMacro {
context.diagnose(error)
return []
}
guard let parent = node.attributeName.as(IdentifierTypeSyntax.self)?
.genericArgumentClause?.as(GenericArgumentClauseSyntax.self)?
.arguments
.description else {
let fixIt = FixIt.replace(
message: RelaxFixItMessage.missingParent,
oldNode: Syntax(node),
newNode: FixItRewriter().visit(node)
)
let error = Diagnostic(node: node, message: RelaxMacroDiagnostic.missingParent, fixIt: fixIt)
context.diagnose(error)


return []
}
let decl: DeclSyntax =
"""
extension \(type.trimmed): Endpoint {
Expand All @@ -52,20 +50,4 @@ public struct APIEndpointMacro: ExtensionMacro {
guard let extensionDecl = decl.as(ExtensionDeclSyntax.self) else { return [] }
return [extensionDecl]
}

final private class FixItRewriter: SyntaxRewriter {
override func visitAny(_ node: Syntax) -> Syntax? {
guard let attributeName = node.as(IdentifierTypeSyntax.self),
attributeName.genericArgumentClause == nil
else { return node }
let listSyntax = GenericArgumentListSyntax {
GenericArgumentSyntax(argument: TypeSyntax(stringLiteral: "<#APIComponent#>"))
}
let placeholder = GenericArgumentClauseSyntax(arguments: listSyntax)
guard let genericDecl = placeholder.as(GenericArgumentClauseSyntax.self) else { return node }
var newAttributeName = attributeName
newAttributeName.genericArgumentClause = genericDecl
return newAttributeName.as(Syntax.self)
}
}
}
19 changes: 0 additions & 19 deletions Sources/RelaxMacros/RelaxMacroDiagnostic.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import SwiftDiagnostics
enum RelaxMacroDiagnostic: String, DiagnosticMessage {
case invalidBaseURL
case invalidPath
case missingParent

var severity: DiagnosticSeverity { .error }

Expand All @@ -20,28 +19,10 @@ enum RelaxMacroDiagnostic: String, DiagnosticMessage {
"The base URL is invalid."
case .invalidPath:
"The path is invalid."
case .missingParent:
"The parent APIComponent must be specified as a generic argument."
}
}

var diagnosticID: MessageID {
MessageID(domain: "RelaxMacros", id: rawValue)
}
}

enum RelaxFixItMessage: String, FixItMessage {
case missingParent
var message: String {
switch self {
case .missingParent:
"Add generic argument 'APIComponent'"
}
}

var fixItID: SwiftDiagnostics.MessageID {
MessageID(domain: "RelaxMacros", id: rawValue)
}


}
33 changes: 2 additions & 31 deletions Tests/RelaxMacrosTests/APIEndpointMacroTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ final class APIEndpointMacroTests: XCTestCase {
#if canImport(RelaxMacros)
assertMacroExpansion(
"""
@APIEndpoint<MyService>("path")
@APIEndpoint("path", parent: MyService.self)
enum TestService {
}
""",
Expand All @@ -42,40 +42,11 @@ final class APIEndpointMacroTests: XCTestCase {
#endif
}

func testAPIEndpointNoParent() throws {
#if canImport(RelaxMacros)
assertMacroExpansion(
"""
@APIEndpoint("path")
enum TestService {
}
""",
expandedSource: """
enum TestService {
}
""",
diagnostics: [
DiagnosticSpec(
message: RelaxMacroDiagnostic.missingParent.message,
line: 1,
column: 1,
fixIts: [
FixItSpec(message: RelaxFixItMessage.missingParent.message)
]
)
],
macros: testMacros
)
#else
throw XCTSkip("macros are only supported when running tests for the host platform")
#endif
}

func testAPIEndpointInvalidPath() throws {
#if canImport(RelaxMacros)
assertMacroExpansion(
"""
@APIEndpoint<Parent>("")
@APIEndpoint("", parent: MyService.self)
enum TestService {
}
""",
Expand Down

0 comments on commit 1308766

Please sign in to comment.