/* * Copyright 2024, gRPC Authors All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ extension FunctionSignatureDescription { /// ``` /// func ( /// request: GRPCCore.ServerRequest, /// context: GRPCCore.ServerContext /// ) async throws -> GRPCCore.ServerResponse /// ``` static func serverMethod( accessLevel: AccessModifier? = nil, name: String, input: String, output: String, streamingInput: Bool, streamingOutput: Bool ) -> Self { return FunctionSignatureDescription( accessModifier: accessLevel, kind: .function(name: name), parameters: [ ParameterDescription( label: "request", type: .serverRequest(forType: input, streaming: streamingInput) ), ParameterDescription(label: "context", type: .serverContext), ], keywords: [.async, .throws], returnType: .identifierType(.serverResponse(forType: output, streaming: streamingOutput)) ) } } extension ProtocolDescription { /// ``` /// protocol : GRPCCore.RegistrableRPCService { /// ... /// } /// ``` static func streamingService( accessLevel: AccessModifier? = nil, name: String, methods: [MethodDescriptor] ) -> Self { return ProtocolDescription( accessModifier: accessLevel, name: name, conformances: ["GRPCCore.RegistrableRPCService"], members: methods.map { method in .commentable( .preFormatted(method.documentation), .function( signature: .serverMethod( name: method.name.generatedLowerCase, input: method.inputType, output: method.outputType, streamingInput: true, streamingOutput: true ) ) ) } ) } } extension ExtensionDescription { /// ``` /// extension { /// func registerMethods(with router: inout GRPCCore.RPCRouter) { /// // ... /// } /// } /// ``` static func registrableRPCServiceDefaultImplementation( accessLevel: AccessModifier? = nil, on extensionName: String, serviceNamespace: String, methods: [MethodDescriptor], serializer: (String) -> String, deserializer: (String) -> String ) -> Self { return ExtensionDescription( onType: extensionName, declarations: [ .function( .registerMethods( accessLevel: accessLevel, serviceNamespace: serviceNamespace, methods: methods, serializer: serializer, deserializer: deserializer ) ) ] ) } } extension ProtocolDescription { /// ``` /// protocol : { /// ... /// } /// ``` static func service( accessLevel: AccessModifier? = nil, name: String, streamingProtocol: String, methods: [MethodDescriptor] ) -> Self { return ProtocolDescription( accessModifier: accessLevel, name: name, conformances: [streamingProtocol], members: methods.map { method in .commentable( .preFormatted(method.documentation), .function( signature: .serverMethod( name: method.name.generatedLowerCase, input: method.inputType, output: method.outputType, streamingInput: method.isInputStreaming, streamingOutput: method.isOutputStreaming ) ) ) } ) } } extension FunctionCallDescription { /// ``` /// self.(request: request, context: context) /// ``` static func serverMethodCallOnSelf( name: String, requestArgument: Expression = .identifierPattern("request") ) -> Self { return FunctionCallDescription( calledExpression: .memberAccess( MemberAccessDescription( left: .identifierPattern("self"), right: name ) ), arguments: [ FunctionArgumentDescription( label: "request", expression: requestArgument ), FunctionArgumentDescription( label: "context", expression: .identifierPattern("context") ), ] ) } } extension ClosureInvocationDescription { /// ``` /// { router, context in /// try await self.( /// request: request, /// context: context /// ) /// } /// ``` static func routerHandlerInvokingRPC(method: String) -> Self { return ClosureInvocationDescription( argumentNames: ["request", "context"], body: [ .expression( .unaryKeyword( kind: .try, expression: .unaryKeyword( kind: .await, expression: .functionCall(.serverMethodCallOnSelf(name: method)) ) ) ) ] ) } } /// ``` /// router.registerHandler( /// forMethod: ..., /// deserializer: ... /// serializer: ... /// handler: { request, context in /// // ... /// } /// ) /// ``` extension FunctionCallDescription { static func registerWithRouter( serviceNamespace: String, methodNamespace: String, methodName: String, inputDeserializer: String, outputSerializer: String ) -> Self { return FunctionCallDescription( calledExpression: .memberAccess( .init(left: .identifierPattern("router"), right: "registerHandler") ), arguments: [ FunctionArgumentDescription( label: "forMethod", expression: .identifierPattern("\(serviceNamespace).Method.\(methodNamespace).descriptor") ), FunctionArgumentDescription( label: "deserializer", expression: .identifierPattern(inputDeserializer) ), FunctionArgumentDescription( label: "serializer", expression: .identifierPattern(outputSerializer) ), FunctionArgumentDescription( label: "handler", expression: .closureInvocation(.routerHandlerInvokingRPC(method: methodName)) ), ] ) } } extension FunctionDescription { /// ``` /// func registerMethods(with router: inout GRPCCore.RPCRouter) { /// // ... /// } /// ``` static func registerMethods( accessLevel: AccessModifier? = nil, serviceNamespace: String, methods: [MethodDescriptor], serializer: (String) -> String, deserializer: (String) -> String ) -> Self { return FunctionDescription( accessModifier: accessLevel, kind: .function(name: "registerMethods"), parameters: [ ParameterDescription( label: "with", name: "router", type: .rpcRouter, `inout`: true ) ], body: methods.map { method in .functionCall( .registerWithRouter( serviceNamespace: serviceNamespace, methodNamespace: method.name.generatedUpperCase, methodName: method.name.generatedLowerCase, inputDeserializer: deserializer(method.inputType), outputSerializer: serializer(method.outputType) ) ) } ) } } extension FunctionDescription { /// ``` /// func ( /// request: GRPCCore.StreamingServerRequest /// context: GRPCCore.ServerContext /// ) async throws -> GRPCCore.StreamingServerResponse { /// let response = try await self.( /// request: GRPCCore.ServerRequest(stream: request), /// context: context /// ) /// return GRPCCore.StreamingServerResponse(single: response) /// } /// ``` static func serverStreamingMethodsCallingMethod( accessLevel: AccessModifier? = nil, name: String, input: String, output: String, streamingInput: Bool, streamingOutput: Bool ) -> FunctionDescription { let signature: FunctionSignatureDescription = .serverMethod( accessLevel: accessLevel, name: name, input: input, output: output, // This method converts from the fully streamed version to the specified version. streamingInput: true, streamingOutput: true ) // Call the underlying function. let functionCall: Expression = .functionCall( calledExpression: .memberAccess( MemberAccessDescription( left: .identifierPattern("self"), right: name ) ), arguments: [ FunctionArgumentDescription( label: "request", expression: streamingInput ? .identifierPattern("request") : .functionCall( calledExpression: .identifierType(.serverRequest(forType: nil, streaming: false)), arguments: [ FunctionArgumentDescription( label: "stream", expression: .identifierPattern("request") ) ] ) ), FunctionArgumentDescription( label: "context", expression: .identifierPattern("context") ), ] ) // Call the function and assign to 'response'. let response: Declaration = .variable( kind: .let, left: "response", right: .unaryKeyword( kind: .try, expression: .unaryKeyword( kind: .await, expression: functionCall ) ) ) // Build the return statement. let returnExpression: Expression = .unaryKeyword( kind: .return, expression: streamingOutput ? .identifierPattern("response") : .functionCall( calledExpression: .identifierType(.serverResponse(forType: nil, streaming: true)), arguments: [ FunctionArgumentDescription( label: "single", expression: .identifierPattern("response") ) ] ) ) return Self( signature: signature, body: [.declaration(response), .expression(returnExpression)] ) } } extension ExtensionDescription { /// ``` /// extension { /// func ( /// request: GRPCCore.StreamingServerRequest /// context: GRPCCore.ServerContext /// ) async throws -> GRPCCore.StreamingServerResponse { /// let response = try await self.( /// request: GRPCCore.ServerRequest(stream: request), /// context: context /// ) /// return GRPCCore.StreamingServerResponse(single: response) /// } /// ... /// } /// ``` static func streamingServiceProtocolDefaultImplementation( accessModifier: AccessModifier? = nil, on extensionName: String, methods: [MethodDescriptor] ) -> Self { return ExtensionDescription( onType: extensionName, declarations: methods.compactMap { method -> Declaration? in // Bidirectional streaming methods don't need a default implementation as their signatures // match across the two protocols. if method.isInputStreaming, method.isOutputStreaming { return nil } return .function( .serverStreamingMethodsCallingMethod( accessLevel: accessModifier, name: method.name.generatedLowerCase, input: method.inputType, output: method.outputType, streamingInput: method.isInputStreaming, streamingOutput: method.isOutputStreaming ) ) } ) } }