Skip to content

Commit d047364

Browse files
committed
Add RequestModifier macro
1 parent 27ccbaa commit d047364

File tree

17 files changed

+211
-56
lines changed

17 files changed

+211
-56
lines changed

Sources/NetworkingCore/Macros/RequestMacro.swift

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,17 @@ import Foundation
4848
/// - Note: This macros is needed for the ``Header`` & ``Parameter``
4949
/// macros to work.
5050
@attached(extension, conformances: Request, ModifiableRequest)
51-
@attached(member, conformances: Request, names: named(modifier), named(id))
51+
@attached(member, conformances: Request, names: named(modifier), named(id), named(_accept))
5252
public macro Request(_ id: String = "") = #externalMacro(
5353
module: "NetworkingCoreMacros",
5454
type: "RequestMacro"
5555
)
5656

5757
// TODO: - Add support for mods builder in init for composable requests
58+
59+
@attached(extension, conformances: RequestModifier)
60+
@attached(member, conformances: RequestModifier, names: named(_accept))
61+
public macro RequestModifier() = #externalMacro(
62+
module: "NetworkingCoreMacros",
63+
type: "RequestModifierMacro"
64+
)

Sources/NetworkingCore/Request/AnyRequest.swift

Lines changed: 51 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,25 +13,17 @@ import Foundation
1313
public typealias Contents = Never
1414

1515
// MARK: - Properties
16-
/// The configuration values available to this instance.
17-
@Configurations private var configurations
16+
/// The request builder used to type erase a request.
17+
private let storage: AnyRequestStorageBase
1818

1919
/// The request's identifier.
2020
public let id: String
2121

22-
/// The request builder used to type erase a request.
23-
private let requestBuilder: (
24-
_ configurations: borrowing ConfigurationValues
25-
) throws -> URLRequest
26-
2722
// MARK: - Initializer
2823
/// Create an instance that type-erases `request`.
2924
public init(_ request: some Request) {
3025
self.id = request.id
31-
self.requestBuilder = { configurations in
32-
request._accept(configurations)
33-
return try request._makeURLRequest()
34-
}
26+
self.storage = AnyRequestStorage(request: request)
3527
}
3628

3729
// MARK: - Functions
@@ -43,7 +35,7 @@ import Foundation
4335
/// - Returns: The configured ``URLRequest``.
4436
/// - Note: This type is prefixed with `_` to indicate that it is not intended for public use.
4537
public func _makeURLRequest() throws -> URLRequest {
46-
return try requestBuilder(configurations)
38+
return try storage.makeURLRequest()
4739
}
4840

4941
/// Applies new configuration values to the erased request.
@@ -54,6 +46,52 @@ import Foundation
5446
/// - Parameter values: The configuration values to apply.
5547
/// - Note: This type is prefixed with `_` to indicate that it is not intended for public use.
5648
public func _accept(_ values: ConfigurationValues) {
57-
_configurations._accept(values)
49+
storage.accept(values)
50+
}
51+
}
52+
53+
extension AnyRequest {
54+
/// An abstract base class for type-erased request storage.
55+
@usableFromInline internal class AnyRequestStorageBase {
56+
/// Creates a ``URLRequest`` from the underlying request.
57+
///
58+
/// - Returns: A configured ``URLRequest``.
59+
internal func makeURLRequest() throws -> URLRequest {
60+
fatalError("Subclasses must override this method.")
61+
}
62+
63+
/// Applies the specified configuration values to the underlying request.
64+
///
65+
/// - Parameter values: The configuration values to apply.
66+
internal func accept(_ values: ConfigurationValues) {
67+
fatalError("Subclasses must override this method.")
68+
}
69+
}
70+
71+
/// A concrete storage type for a specific ``Request`` instance.
72+
fileprivate final class AnyRequestStorage<R: Request>: AnyRequestStorageBase {
73+
/// The wrapped request.
74+
private let request: R
75+
76+
/// Creates a new instance that wraps the given request.
77+
///
78+
/// - Parameter request: The request to wrap.
79+
fileprivate init(request: R) {
80+
self.request = request
81+
}
82+
83+
/// Builds a ``URLRequest`` from the wrapped request.
84+
///
85+
/// - Returns: A configured ``URLRequest``.
86+
fileprivate override func makeURLRequest() throws -> URLRequest {
87+
return try request._makeURLRequest()
88+
}
89+
90+
/// Applies the specified configuration values to the wrapped request.
91+
///
92+
/// - Parameter values: The configuration values to apply.
93+
fileprivate override func accept(_ values: ConfigurationValues) {
94+
request._accept(values)
95+
}
5896
}
5997
}

Sources/NetworkingCore/Request/HTTPRequest.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ import Foundation
6262
private let path: String?
6363

6464
/// The request modifier that defines the request’s behavior.
65-
public let modifier: Modifier
65+
private let modifier: Modifier
6666
}
6767

6868
extension HTTPRequest {
@@ -130,7 +130,7 @@ extension HTTPRequest: Request {
130130

131131
/// Applies the given configuration values to the request.
132132
///
133-
/// This method forwards the provided `ConfigurationValues` to the associated
133+
/// This method forwards the provided ``ConfigurationValues`` to the associated
134134
/// request modifier and to this instance’s internal configuration storage.
135135
/// Use this to propagate inherited configuration values through the request
136136
/// chain.

Sources/NetworkingCore/Request/ModifiableRequest.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ extension ModifiableRequest {
117117
/// ```
118118
@frozen public struct ModifiedRequest<Content: Request, Modifier: RequestModifier>: ModifiableRequest {
119119
/// The identifier of the underlying request.
120-
@inlinable public var id: String {
120+
public var id: String {
121121
return request.id
122122
}
123123

Sources/NetworkingCore/Request/RequestModifier.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,6 @@ extension Request {
5656
@inlinable public func modifier<Modifier: RequestModifier>(
5757
_ modifier: Modifier
5858
) -> some Request {
59-
ModifiedRequest(request: self, modifier: modifier)
59+
return ModifiedRequest(request: self, modifier: modifier)
6060
}
6161
}

Sources/NetworkingCore/RequestModifiers/CachePolicyRequestModifier.swift

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import Foundation
1010
/// Request modifier for setting the cache policy for a ``URLRequest``.
1111
///
1212
/// - Note: Use ``Request/cachePolicy(_:)`` instead of directly using this.
13-
@usableFromInline internal struct CachePolicyRequestModifier {
13+
@RequestModifier @usableFromInline internal struct CachePolicyRequestModifier {
1414
/// The cache policy to apply to the request.
1515
private let cachePolicy: URLRequest.CachePolicy
1616

@@ -20,10 +20,7 @@ import Foundation
2020
@usableFromInline internal init(_ cachePolicy: URLRequest.CachePolicy) {
2121
self.cachePolicy = cachePolicy
2222
}
23-
}
2423

25-
// MARK: - RequestModifier
26-
extension CachePolicyRequestModifier: RequestModifier {
2724
/// Modifies the given ``URLRequest`` by setting its cache policy.
2825
///
2926
/// - Parameters:

Sources/NetworkingCore/RequestModifiers/HTTPMethodRequestModifier.swift

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import Foundation
1010
/// Request modifier for setting the HTTP method of a ``URLRequest``.
1111
///
1212
/// - Note: Use ``Request/method(_:)`` instead of directly using this.
13-
@usableFromInline internal struct HTTPMethodRequestModifier {
13+
@RequestModifier @usableFromInline internal struct HTTPMethodRequestModifier {
1414
/// The HTTP method to apply to the request.
1515
private let httpMethod: RequestMethod
1616

@@ -20,10 +20,7 @@ import Foundation
2020
@usableFromInline internal init(_ httpMethod: RequestMethod) {
2121
self.httpMethod = httpMethod
2222
}
23-
}
2423

25-
// MARK: - RequestModifier
26-
extension HTTPMethodRequestModifier: RequestModifier {
2724
/// Modifies the given ``URLRequest`` by setting its HTTP method.
2825
///
2926
/// - Parameters:

Sources/NetworkingCore/RequestModifiers/PathRequestModifier.swift

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import Foundation
1313
/// or ``Request/appending(path:)``,
1414
/// or ``Request/appending(paths:)``
1515
/// instead of directly using this.
16-
@usableFromInline internal struct PathRequestModifier {
16+
@RequestModifier @usableFromInline internal struct PathRequestModifier {
1717
/// The list of paths to append.
1818
private let paths: [String]
1919

@@ -23,10 +23,7 @@ import Foundation
2323
@usableFromInline internal init(_ paths: [String]) {
2424
self.paths = paths
2525
}
26-
}
2726

28-
// MARK: - RequestModifier
29-
extension PathRequestModifier: RequestModifier {
3027
/// Modifies the given ``URLRequest`` by appending paths to its URL.
3128
///
3229
/// - Parameters:

Sources/NetworkingCore/RequestModifiers/TimeoutRequestModifier.swift

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import Foundation
1010
/// Request modifier for setting the timeout interval for a ``URLRequest``
1111
///
1212
/// - Note: Use ``Request/timeout(_:)`` instead of directly using this.
13-
@usableFromInline internal struct TimeoutRequestModifier {
13+
@RequestModifier @usableFromInline internal struct TimeoutRequestModifier {
1414
/// The timeout interval in seconds.
1515
private let timeoutInterval: TimeInterval
1616

@@ -20,10 +20,7 @@ import Foundation
2020
@usableFromInline internal init(_ timeoutInterval: TimeInterval) {
2121
self.timeoutInterval = timeoutInterval
2222
}
23-
}
2423

25-
// MARK: - RequestModifier
26-
extension TimeoutRequestModifier: RequestModifier {
2724
/// Modifies the given ``URLRequest`` by setting its timeout interval.
2825
///
2926
/// - Parameters:
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
//
2+
// DynamicConfigurionDeclFactory.swift
3+
// Networking
4+
//
5+
// Created by Joe Maghzal on 25/05/2025.
6+
//
7+
8+
import SwiftSyntax
9+
10+
enum DynamicConfigDeclFactory {
11+
private static func getConfigsDeclName(
12+
declaration: some DeclGroupSyntax
13+
) -> TokenSyntax? {
14+
for member in declaration.memberBlock.members {
15+
guard let decl = member.decl.as(VariableDeclSyntax.self) else {continue}
16+
17+
let hasConfigAttr = decl.attributes.contains { attribute in
18+
return attribute.name?.trimmed.text == "Configurations"
19+
}
20+
21+
if hasConfigAttr {
22+
return decl.name
23+
}
24+
}
25+
return nil
26+
}
27+
28+
static func make(
29+
for declaration: some DeclGroupSyntax
30+
) -> FunctionDeclSyntax? {
31+
guard let declName = getConfigsDeclName(declaration: declaration) else {
32+
return nil
33+
}
34+
let body: ExprSyntax = """
35+
_\(declName)._accept(values)
36+
"""
37+
let type = IdentifierTypeSyntax(name: "ConfigurationValues")
38+
let modifiers = declaration.modifiers.filter({$0.name.isAccessLevel})
39+
let funcDecl = FunctionDeclSyntax(
40+
modifiers: modifiers,
41+
name: "_accept",
42+
signature: FunctionSignatureSyntax(
43+
parameterClause: FunctionParameterClauseSyntax(
44+
parameters: [
45+
FunctionParameterSyntax(
46+
firstName: "_",
47+
secondName: "values",
48+
type: type
49+
)
50+
]
51+
)
52+
),
53+
body: CodeBlockSyntax(
54+
statements: CodeBlockItemListSyntax(
55+
[CodeBlockItemSyntax(item: .expr(body))]
56+
)
57+
)
58+
)
59+
return funcDecl
60+
}
61+
}
62+
63+
extension TokenSyntax {
64+
var isAccessLevel: Bool {
65+
switch tokenKind {
66+
case .keyword(let keyword):
67+
return keyword == .public || keyword == .internal || keyword == .fileprivate || keyword == .private || keyword == .fileprivate || keyword == .open || keyword == .package
68+
default:
69+
return false
70+
}
71+
}
72+
}

0 commit comments

Comments
 (0)