feat: add support for bodyField and namespaced type

This commit is contained in:
Kyle Fang
2023-07-27 10:43:25 +08:00
parent 2807472982
commit a5d3184167
6 changed files with 82 additions and 3 deletions

View File

@@ -10,5 +10,8 @@ public macro Header(_ name: String = "") = #externalMacro(module: "SwiftRequestM
@attached(member)
public macro FormField(_ name: String = "") = #externalMacro(module: "SwiftRequestMacros", type: "ParamMacro")
@attached(member)
public macro BodyField(_ name: String = "") = #externalMacro(module: "SwiftRequestMacros", type: "ParamMacro")
@attached(member)
public macro Body() = #externalMacro(module: "SwiftRequestMacros", type: "BodyMacro")

View File

@@ -34,6 +34,8 @@ public extension Request {
} else {
request.httpBody = try JSONEncoder().encode(body)
}
} else if let bodyFields {
request.httpBody = try JSONSerialization.data(withJSONObject: bodyFields)
}
if let headers {

View File

@@ -6,9 +6,11 @@ public struct Request {
let queryParams: Params?
let headers: Params?
let formFields: Params?
let bodyFields: BodyParams?
let body: (any Encodable)?
public typealias Params = [String: (any CustomStringConvertible)?]
public typealias BodyParams = [String: (any Encodable)?]
public init(
url: URL,
@@ -16,6 +18,7 @@ public struct Request {
queryParams: Params? = nil,
headers: Params? = nil,
formFields: Params? = nil,
bodyFields: BodyParams? = nil,
body: (any Encodable)? = nil
) {
self.url = url
@@ -23,6 +26,7 @@ public struct Request {
self.queryParams = queryParams
self.headers = headers
self.formFields = formFields
self.bodyFields = bodyFields
self.body = body
}
}

View File

@@ -47,7 +47,7 @@ public class MethodMacro: PeerMacro {
outputType: TypeSyntax,
in context: some MacroExpansionContext
) -> Bool {
if outputType.is(SimpleTypeIdentifierSyntax.self) {
if outputType.is(SimpleTypeIdentifierSyntax.self) || outputType.is(MemberTypeIdentifierSyntax.self) {
return true
}

View File

@@ -103,6 +103,10 @@ class ServiceMethodExpander {
TupleExprElementSyntax(label: "formFields", expression: formFields)
}
if let bodyFields = expandParameter("BodyField", of: declaration, in: context) {
TupleExprElementSyntax(label: "bodyFields", expression: bodyFields)
}
if let body = expandBody(of: declaration, in: context) {
TupleExprElementSyntax(label: "body", expression: IdentifierExprSyntax(identifier: body))
}

View File

@@ -7,6 +7,9 @@ final class ServiceTests: XCTestCase {
let testMacros: [String: Macro.Type] = [
"Service": ServiceMacro.self,
"GET": MethodMacro.self,
"POST": MethodMacro.self,
"BodyField": ParamMacro.self,
"QueryParam": ParamMacro.self,
]
func testMacro() {
@@ -32,12 +35,75 @@ final class ServiceTests: XCTestCase {
func getRandomQuotes(limit: Int? = nil) async throws -> [Quote] {
let request = Request(url: resourceURL.appendingPathComponent("random"), queryParams: ["limit": limit])
return try await session.execute(request)
return try await executor(request)
}
func getQuote(by id: String) async throws -> Quote {
let request = Request(url: resourceURL.appendingPathComponent("\\(id)"))
return try await session.execute(request)
return try await executor(request)
}
}
""",
macros: testMacros
)
}
func testBodyFieldMacro() {
assertMacroExpansion(
"""
@Service(resource: "quotes")
protocol QuoteService {
@POST("random")
func getRandomQuotes(@BodyField limit: Int?) async throws -> [Quote]
}
""",
expandedSource: """
protocol QuoteService {
func getRandomQuotes(@BodyField limit: Int?) async throws -> [Quote]
}
class QuoteServiceImpl: Service, QuoteService {
private lazy var resourceURL: URL = baseURL.appendingPathComponent("quotes")
func getRandomQuotes(limit: Int? = nil) async throws -> [Quote] {
let request = Request(url: resourceURL.appendingPathComponent("random"), method: "POST", bodyFields: ["limit": limit])
return try await executor(request)
}
}
""",
macros: testMacros
)
}
func testNestedTypeMacro() {
assertMacroExpansion(
"""
struct Namespace {
struct Output: Codable {
let hello: String
}
}
@Service(resource: "quotes")
protocol QuoteService {
@POST("random")
func getRandomQuotes(@BodyField limit: Int?) async throws -> Namespace.Output
}
""",
expandedSource: """
struct Namespace {
struct Output: Codable {
let hello: String
}
}
protocol QuoteService {
func getRandomQuotes(@BodyField limit: Int?) async throws -> Namespace.Output
}
class QuoteServiceImpl: Service, QuoteService {
private lazy var resourceURL: URL = baseURL.appendingPathComponent("quotes")
func getRandomQuotes(limit: Int? = nil) async throws -> Namespace.Output {
let request = Request(url: resourceURL.appendingPathComponent("random"), method: "POST", bodyFields: ["limit": limit])
return try await executor(request)
}
}
""",