Skip to content

Commit 69df9eb

Browse files
committed
Deduplication interceptor pipeline operation
Motivation: ClientInterceptorPipelineOperation is the same as ServerInterceptorPipelineOperation apart from the type of interceptor being used. The duplication here is unnecessary. Modifications: - Add a `ConditionalInterceptor` which is roughly the same as `*InterceptorPipelineOperation` but is generic. - The init is private and can only be initialized via static methods when the generic type is appropriate (i.e. client/server interceptor). Result: Less duplication, less code
1 parent 76073a2 commit 69df9eb

9 files changed

+56
-214
lines changed
Lines changed: 36 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,16 @@
1414
* limitations under the License.
1515
*/
1616

17-
/// A `ClientInterceptorPipelineOperation` describes to which RPCs a client interceptor should be applied.
17+
/// Describes the conditions under which an interceptor should be applied.
1818
///
19-
/// You can configure a client interceptor to be applied to:
19+
/// You can configure interceptors to be applied to:
2020
/// - all RPCs and services;
2121
/// - requests directed only to specific services; or
2222
/// - requests directed only to specific methods (of a specific service).
2323
///
24-
/// - SeeAlso: ``ClientInterceptor`` for more information on client interceptors, and
25-
/// ``ServerInterceptorPipelineOperation`` for the server-side version of this type.
26-
public struct ClientInterceptorPipelineOperation: Sendable {
27-
/// The subject of a ``ClientInterceptorPipelineOperation``.
28-
/// The subject of an interceptor can either be all services and methods, only specific services, or only specific methods.
24+
/// - SeeAlso: ``ClientInterceptor`` and ``ServerInterceptor`` for more information on client and
25+
/// server interceptors, respectively.
26+
public struct ConditionalInterceptor<Interceptor: Sendable>: Sendable {
2927
public struct Subject: Sendable {
3028
internal enum Wrapped: Sendable {
3129
case all
@@ -41,21 +39,19 @@ public struct ClientInterceptorPipelineOperation: Sendable {
4139
/// An operation subject specifying an interceptor that will be applied only to RPCs directed to the specified services.
4240
/// - Parameters:
4341
/// - services: The list of service names for which this interceptor should intercept RPCs.
44-
/// - Returns: A ``ClientInterceptorPipelineOperation``.
4542
public static func services(_ services: Set<ServiceDescriptor>) -> Self {
4643
Self(wrapped: .services(services))
4744
}
4845

4946
/// An operation subject specifying an interceptor that will be applied only to RPCs directed to the specified service methods.
5047
/// - Parameters:
5148
/// - methods: The list of method descriptors for which this interceptor should intercept RPCs.
52-
/// - Returns: A ``ClientInterceptorPipelineOperation``.
5349
public static func methods(_ methods: Set<MethodDescriptor>) -> Self {
5450
Self(wrapped: .methods(methods))
5551
}
5652

5753
@usableFromInline
58-
internal func applies(to descriptor: MethodDescriptor) -> Bool {
54+
package func applies(to descriptor: MethodDescriptor) -> Bool {
5955
switch self.wrapped {
6056
case .all:
6157
return true
@@ -69,24 +65,15 @@ public struct ClientInterceptorPipelineOperation: Sendable {
6965
}
7066
}
7167

72-
/// The interceptor specified for this operation.
73-
public let interceptor: any ClientInterceptor
68+
/// The interceptor.
69+
public let interceptor: Interceptor
7470

7571
@usableFromInline
7672
internal let subject: Subject
7773

78-
private init(interceptor: any ClientInterceptor, appliesTo: Subject) {
74+
fileprivate init(interceptor: Interceptor, subject: Subject) {
7975
self.interceptor = interceptor
80-
self.subject = appliesTo
81-
}
82-
83-
/// Create an operation, specifying which ``ClientInterceptor`` to apply and to which ``Subject``.
84-
/// - Parameters:
85-
/// - interceptor: The ``ClientInterceptor`` to register with the client.
86-
/// - subject: The ``Subject`` to which the `interceptor` applies.
87-
/// - Returns: A ``ClientInterceptorPipelineOperation``.
88-
public static func apply(_ interceptor: any ClientInterceptor, to subject: Subject) -> Self {
89-
Self(interceptor: interceptor, appliesTo: subject)
76+
self.subject = subject
9077
}
9178

9279
/// Returns whether this ``ClientInterceptorPipelineOperation`` applies to the given `descriptor`.
@@ -97,3 +84,29 @@ public struct ClientInterceptorPipelineOperation: Sendable {
9784
self.subject.applies(to: descriptor)
9885
}
9986
}
87+
88+
extension ConditionalInterceptor where Interceptor == any ClientInterceptor {
89+
/// Create an operation, specifying which ``ClientInterceptor`` to apply and to which ``Subject``.
90+
/// - Parameters:
91+
/// - interceptor: The ``ClientInterceptor`` to register with the client.
92+
/// - subject: The ``Subject`` to which the `interceptor` applies.
93+
public static func apply(
94+
_ interceptor: any ClientInterceptor,
95+
to subject: Subject
96+
) -> Self {
97+
Self(interceptor: interceptor, subject: subject)
98+
}
99+
}
100+
101+
extension ConditionalInterceptor where Interceptor == any ServerInterceptor {
102+
/// Create an operation, specifying which ``ServerInterceptor`` to apply and to which ``Subject``.
103+
/// - Parameters:
104+
/// - interceptor: The ``ServerInterceptor`` to register with the client.
105+
/// - target: The ``Subject`` to which the `interceptor` applies.
106+
public static func apply(
107+
_ interceptor: any ServerInterceptor,
108+
to subject: Subject
109+
) -> Self {
110+
Self(interceptor: interceptor, subject: subject)
111+
}
112+
}

Sources/GRPCCore/Call/Server/RPCRouter.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,9 @@ public struct RPCRouter: Sendable {
157157
/// interceptors matters.
158158
/// - SeeAlso: ``ServerInterceptorPipelineOperation``.
159159
@inlinable
160-
public mutating func registerInterceptors(pipeline: [ServerInterceptorPipelineOperation]) {
160+
public mutating func registerInterceptors(
161+
pipeline: [ConditionalInterceptor<any ServerInterceptor>]
162+
) {
161163
for descriptor in self.handlers.keys {
162164
let applicableOperations = pipeline.filter { $0.applies(to: descriptor) }
163165
if !applicableOperations.isEmpty {

Sources/GRPCCore/Call/Server/ServerInterceptorPipelineOperation.swift

Lines changed: 0 additions & 99 deletions
This file was deleted.

Sources/GRPCCore/GRPCClient.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ public final class GRPCClient: Sendable {
129129
private struct StateMachine {
130130
var state: State
131131

132-
private let interceptorPipeline: [ClientInterceptorPipelineOperation]
132+
private let interceptorPipeline: [ConditionalInterceptor<any ClientInterceptor>]
133133

134134
/// A collection of interceptors providing cross-cutting functionality to each accepted RPC, keyed by the method to which they apply.
135135
///
@@ -142,7 +142,7 @@ public final class GRPCClient: Sendable {
142142
/// the appropriate handler.
143143
var interceptorsPerMethod: [MethodDescriptor: [any ClientInterceptor]]
144144

145-
init(interceptorPipeline: [ClientInterceptorPipelineOperation]) {
145+
init(interceptorPipeline: [ConditionalInterceptor<any ClientInterceptor>]) {
146146
self.state = .notStarted
147147
self.interceptorPipeline = interceptorPipeline
148148
self.interceptorsPerMethod = [:]
@@ -195,7 +195,7 @@ public final class GRPCClient: Sendable {
195195
/// The last interceptor added will be the final interceptor to intercept each request before calling the appropriate handler.
196196
public init(
197197
transport: some ClientTransport,
198-
interceptorPipeline: [ClientInterceptorPipelineOperation]
198+
interceptorPipeline: [ConditionalInterceptor<any ClientInterceptor>]
199199
) {
200200
self.transport = transport
201201
self.stateMachine = Mutex(StateMachine(interceptorPipeline: interceptorPipeline))
@@ -423,7 +423,7 @@ public func withGRPCClient<Result: Sendable>(
423423
/// - Returns: The result of the `handleClient` closure.
424424
public func withGRPCClient<Result: Sendable>(
425425
transport: some ClientTransport,
426-
interceptorPipeline: [ClientInterceptorPipelineOperation],
426+
interceptorPipeline: [ConditionalInterceptor<any ClientInterceptor>],
427427
isolation: isolated (any Actor)? = #isolation,
428428
handleClient: (GRPCClient) async throws -> Result
429429
) async throws -> Result {

Sources/GRPCCore/GRPCServer.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ public final class GRPCServer: Sendable {
171171
public convenience init(
172172
transport: any ServerTransport,
173173
services: [any RegistrableRPCService],
174-
interceptorPipeline: [ServerInterceptorPipelineOperation]
174+
interceptorPipeline: [ConditionalInterceptor<any ServerInterceptor>]
175175
) {
176176
var router = RPCRouter()
177177
for service in services {
@@ -290,7 +290,7 @@ public func withGRPCServer<Result: Sendable>(
290290
public func withGRPCServer<Result: Sendable>(
291291
transport: any ServerTransport,
292292
services: [any RegistrableRPCService],
293-
interceptorPipeline: [ServerInterceptorPipelineOperation],
293+
interceptorPipeline: [ConditionalInterceptor<any ServerInterceptor>],
294294
isolation: isolated (any Actor)? = #isolation,
295295
handleServer: (GRPCServer) async throws -> Result
296296
) async throws -> Result {
Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,11 @@
1414
* limitations under the License.
1515
*/
1616

17+
import GRPCCore
1718
import Testing
1819

19-
@testable import GRPCCore
20-
21-
@Suite("ClientInterceptorPipelineOperation")
22-
struct ClientInterceptorPipelineOperationTests {
20+
@Suite("ConditionalInterceptor")
21+
struct ConditionalInterceptorTests {
2322
@Test(
2423
"Applies to",
2524
arguments: [
@@ -38,24 +37,19 @@ struct ClientInterceptorPipelineOperationTests {
3837
[.barFoo],
3938
[.fooBar, .fooBaz, .barBaz]
4039
),
41-
] as [(ClientInterceptorPipelineOperation.Subject, [MethodDescriptor], [MethodDescriptor])]
40+
] as [(ConditionalInterceptor<any Sendable>.Subject, [MethodDescriptor], [MethodDescriptor])]
4241
)
4342
func appliesTo(
44-
operationSubject: ClientInterceptorPipelineOperation.Subject,
43+
target: ConditionalInterceptor<any Sendable>.Subject,
4544
applicableMethods: [MethodDescriptor],
4645
notApplicableMethods: [MethodDescriptor]
4746
) {
48-
let operation = ClientInterceptorPipelineOperation.apply(
49-
.requestCounter(.init()),
50-
to: operationSubject
51-
)
52-
5347
for applicableMethod in applicableMethods {
54-
#expect(operation.applies(to: applicableMethod))
48+
#expect(target.applies(to: applicableMethod))
5549
}
5650

5751
for notApplicableMethod in notApplicableMethods {
58-
#expect(!operation.applies(to: notApplicableMethod))
52+
#expect(!target.applies(to: notApplicableMethod))
5953
}
6054
}
6155
}

Tests/GRPCCoreTests/Call/Server/ServerInterceptorPipelineOperation.swift

Lines changed: 0 additions & 68 deletions
This file was deleted.

0 commit comments

Comments
 (0)