diff --git a/Sources/GRPCCodeGen/CodeGenerationRequest.swift b/Sources/GRPCCodeGen/CodeGenerationRequest.swift index 432469d74..5106b3c58 100644 --- a/Sources/GRPCCodeGen/CodeGenerationRequest.swift +++ b/Sources/GRPCCodeGen/CodeGenerationRequest.swift @@ -335,3 +335,17 @@ extension Name { return self.base.replacingOccurrences(of: ".", with: "_") } } + +extension ServiceDescriptor { + var namespacedServicePropertyName: String { + let prefix: String + + if self.namespace.normalizedBase.isEmpty { + prefix = "" + } else { + prefix = self.namespace.normalizedBase + "_" + } + + return prefix + self.name.normalizedBase + } +} diff --git a/Sources/GRPCCodeGen/Internal/StructuredSwift+Server.swift b/Sources/GRPCCodeGen/Internal/StructuredSwift+Server.swift index c46986fa3..cc446825d 100644 --- a/Sources/GRPCCodeGen/Internal/StructuredSwift+Server.swift +++ b/Sources/GRPCCodeGen/Internal/StructuredSwift+Server.swift @@ -97,13 +97,16 @@ extension ExtensionDescription { return ExtensionDescription( onType: extensionName, declarations: [ - .function( - .registerMethods( - accessLevel: accessLevel, - serviceNamespace: serviceNamespace, - methods: methods, - serializer: serializer, - deserializer: deserializer + .guarded( + .grpc, + .function( + .registerMethods( + accessLevel: accessLevel, + serviceNamespace: serviceNamespace, + methods: methods, + serializer: serializer, + deserializer: deserializer + ) ) ) ] diff --git a/Sources/GRPCCodeGen/Internal/StructuredSwift+ServiceMetadata.swift b/Sources/GRPCCodeGen/Internal/StructuredSwift+ServiceMetadata.swift index a71cafd83..62e1933d8 100644 --- a/Sources/GRPCCodeGen/Internal/StructuredSwift+ServiceMetadata.swift +++ b/Sources/GRPCCodeGen/Internal/StructuredSwift+ServiceMetadata.swift @@ -302,3 +302,43 @@ extension EnumDescription { return description } } + +extension [CodeBlock] { + /// ``` + /// enum { + /// ... + /// } + /// + /// extension GRPCCore.ServiceDescriptor { + /// ... + /// } + /// ``` + package static func serviceMetadata( + accessModifier: AccessModifier? = nil, + service: ServiceDescriptor, + client: Bool, + server: Bool + ) -> Self { + var blocks: [CodeBlock] = [] + + let serviceNamespace: EnumDescription = .serviceNamespace( + accessModifier: accessModifier, + name: service.namespacedGeneratedName, + serviceDescriptorProperty: service.namespacedServicePropertyName, + client: client, + server: server, + methods: service.methods + ) + blocks.append(CodeBlock(item: .declaration(.enum(serviceNamespace)))) + + let descriptorExtension: ExtensionDescription = .serviceDescriptor( + accessModifier: accessModifier, + propertyName: service.namespacedServicePropertyName, + literalNamespace: service.namespace.base, + literalService: service.name.base + ) + blocks.append(CodeBlock(item: .declaration(.extension(descriptorExtension)))) + + return blocks + } +} diff --git a/Sources/GRPCCodeGen/Internal/StructuredSwiftRepresentation.swift b/Sources/GRPCCodeGen/Internal/StructuredSwiftRepresentation.swift index a9f9c33d7..16cfdbfe8 100644 --- a/Sources/GRPCCodeGen/Internal/StructuredSwiftRepresentation.swift +++ b/Sources/GRPCCodeGen/Internal/StructuredSwiftRepresentation.swift @@ -100,7 +100,7 @@ struct ImportDescription: Equatable, Codable, Sendable { /// A description of an access modifier. /// /// For example: `public`. -internal enum AccessModifier: String, Sendable, Equatable, Codable, CaseIterable { +package enum AccessModifier: String, Sendable, Equatable, Codable, CaseIterable { /// A declaration accessible outside of the module. case `public` diff --git a/Sources/GRPCCodeGen/Internal/Translator/ClientCodeTranslator.swift b/Sources/GRPCCodeGen/Internal/Translator/ClientCodeTranslator.swift index 3a628cf47..ec8850a17 100644 --- a/Sources/GRPCCodeGen/Internal/Translator/ClientCodeTranslator.swift +++ b/Sources/GRPCCodeGen/Internal/Translator/ClientCodeTranslator.swift @@ -75,624 +75,83 @@ /// } /// } ///``` -struct ClientCodeTranslator: SpecializedTranslator { - var accessLevel: SourceGenerator.Config.AccessLevel - - init(accessLevel: SourceGenerator.Config.AccessLevel) { - self.accessLevel = accessLevel - } - - func translate(from request: CodeGenerationRequest) throws -> [CodeBlock] { - var blocks = [CodeBlock]() - - for service in request.services { - let `protocol` = self.makeClientProtocol(for: service, in: request) - blocks.append(.declaration(.commentable(.preFormatted(service.documentation), `protocol`))) - - let defaultImplementation = self.makeDefaultImplementation(for: service, in: request) - blocks.append(.declaration(defaultImplementation)) - - let sugaredAPI = self.makeSugaredAPI(forService: service, request: request) - blocks.append(.declaration(sugaredAPI)) - - let clientStruct = self.makeClientStruct(for: service, in: request) - blocks.append(.declaration(.commentable(.preFormatted(service.documentation), clientStruct))) - } - - return blocks - } -} - -extension ClientCodeTranslator { - private func makeClientProtocol( - for service: ServiceDescriptor, - in codeGenerationRequest: CodeGenerationRequest - ) -> Declaration { - let methods = service.methods.map { - self.makeClientProtocolMethod( - for: $0, - in: service, - from: codeGenerationRequest, - includeBody: false, - includeDefaultCallOptions: false - ) - } - - let clientProtocol = Declaration.protocol( - ProtocolDescription( - accessModifier: self.accessModifier, - name: "\(service.namespacedGeneratedName)_ClientProtocol", - conformances: ["Sendable"], - members: methods - ) - ) - return .guarded(self.availabilityGuard, clientProtocol) - } - - private func makeDefaultImplementation( - for service: ServiceDescriptor, - in codeGenerationRequest: CodeGenerationRequest - ) -> Declaration { - let methods = service.methods.map { - self.makeClientProtocolMethod( - for: $0, - in: service, - from: codeGenerationRequest, - includeBody: true, - accessModifier: self.accessModifier, - includeDefaultCallOptions: true - ) - } - let clientProtocolExtension = Declaration.extension( - ExtensionDescription( - onType: "\(service.namespacedGeneratedName).ClientProtocol", - declarations: methods - ) - ) - return .guarded( - self.availabilityGuard, - clientProtocolExtension - ) - } - - private func makeSugaredAPI( - forService service: ServiceDescriptor, - request: CodeGenerationRequest - ) -> Declaration { - let sugaredAPIExtension = Declaration.extension( - ExtensionDescription( - onType: "\(service.namespacedGeneratedName).ClientProtocol", - declarations: service.methods.map { method in - self.makeSugaredMethodDeclaration( - method: method, - accessModifier: self.accessModifier - ) - } - ) - ) - - return .guarded(self.availabilityGuard, sugaredAPIExtension) - } - - private func makeSugaredMethodDeclaration( - method: MethodDescriptor, - accessModifier: AccessModifier? - ) -> Declaration { - let signature = FunctionSignatureDescription( - accessModifier: accessModifier, - kind: .function(name: method.name.generatedLowerCase), - generics: [.member("Result")], - parameters: self.makeParametersForSugaredMethodDeclaration(method: method), - keywords: [.async, .throws], - returnType: .identifierPattern("Result"), - whereClause: WhereClause( - requirements: [ - .conformance("Result", "Sendable") - ] - ) - ) - - let functionDescription = FunctionDescription( - signature: signature, - body: self.makeFunctionBodyForSugaredMethodDeclaration(method: method) - ) - - if method.documentation.isEmpty { - return .function(functionDescription) - } else { - return .commentable(.preFormatted(method.documentation), .function(functionDescription)) - } - } - - private func makeParametersForSugaredMethodDeclaration( - method: MethodDescriptor - ) -> [ParameterDescription] { - var parameters = [ParameterDescription]() - - // Unary inputs have a 'message' parameter - if !method.isInputStreaming { - parameters.append( - ParameterDescription( - label: "_", - name: "message", - type: .member([method.inputType]) - ) - ) - } - - parameters.append( - ParameterDescription( - label: "metadata", - type: .member(["GRPCCore", "Metadata"]), - defaultValue: .literal(.dictionary([])) - ) - ) - - parameters.append( - ParameterDescription( - label: "options", - type: .member(["GRPCCore", "CallOptions"]), - defaultValue: .memberAccess(.dot("defaults")) - ) - ) - - // Streaming inputs have a writer callback - if method.isInputStreaming { - parameters.append( - ParameterDescription( - label: "requestProducer", - type: .closure( - ClosureSignatureDescription( - parameters: [ - ParameterDescription( - type: .generic( - wrapper: .member(["GRPCCore", "RPCWriter"]), - wrapped: .member(method.inputType) - ) - ) - ], - keywords: [.async, .throws], - returnType: .identifierPattern("Void"), - sendable: true, - escaping: true - ) - ) - ) - ) - } - - // All methods have a response handler. - var responseHandler = ParameterDescription(label: "onResponse", name: "handleResponse") - let responseKind = method.isOutputStreaming ? "Streaming" : "" - responseHandler.type = .closure( - ClosureSignatureDescription( - parameters: [ - ParameterDescription( - type: .generic( - wrapper: .member(["GRPCCore", "\(responseKind)ClientResponse"]), - wrapped: .member(method.outputType) - ) - ) - ], - keywords: [.async, .throws], - returnType: .identifierPattern("Result"), - sendable: true, - escaping: true - ) - ) - - if !method.isOutputStreaming { - responseHandler.defaultValue = .closureInvocation( - ClosureInvocationDescription( - body: [.expression(.try(.identifierPattern("$0").dot("message")))] - ) - ) - } - - parameters.append(responseHandler) - - return parameters - } - - private func makeFunctionBodyForSugaredMethodDeclaration( - method: MethodDescriptor +struct ClientCodeTranslator { + init() {} + + func translate( + accessModifier: AccessModifier, + services: [ServiceDescriptor], + serializer: (String) -> String, + deserializer: (String) -> String ) -> [CodeBlock] { - // Produces the following: - // - // let request = GRPCCore.ClientRequest(message: message, metadata: metadata) - // return try await method(request: request, options: options, responseHandler: responseHandler) - // - // or: - // - // let request = GRPCCore.StreamingClientRequest(metadata: metadata, producer: writer) - // return try await method(request: request, options: options, responseHandler: responseHandler) - - // First, make the init for the ClientRequest - let requestType = method.isInputStreaming ? "Streaming" : "" - var requestInit = FunctionCallDescription( - calledExpression: .identifier( - .type( - .generic( - wrapper: .member(["GRPCCore", "\(requestType)ClientRequest"]), - wrapped: .member(method.inputType) - ) - ) - ) - ) - - if method.isInputStreaming { - requestInit.arguments.append( - FunctionArgumentDescription( - label: "metadata", - expression: .identifierPattern("metadata") - ) - ) - requestInit.arguments.append( - FunctionArgumentDescription( - label: "producer", - expression: .identifierPattern("requestProducer") - ) - ) - } else { - requestInit.arguments.append( - FunctionArgumentDescription( - label: "message", - expression: .identifierPattern("message") - ) - ) - requestInit.arguments.append( - FunctionArgumentDescription( - label: "metadata", - expression: .identifierPattern("metadata") - ) - ) - } - - // Now declare the request: - // - // let request = - let request = VariableDescription( - kind: .let, - left: .identifier(.pattern("request")), - right: .functionCall(requestInit) - ) - - var blocks = [CodeBlock]() - blocks.append(.declaration(.variable(request))) - - // Finally, call the underlying method. - let methodCall = FunctionCallDescription( - calledExpression: .identifierPattern("self").dot(method.name.generatedLowerCase), - arguments: [ - FunctionArgumentDescription(label: "request", expression: .identifierPattern("request")), - FunctionArgumentDescription(label: "options", expression: .identifierPattern("options")), - FunctionArgumentDescription(expression: .identifierPattern("handleResponse")), - ] - ) - - blocks.append(.expression(.return(.try(.await(.functionCall(methodCall)))))) - return blocks - } - - private func makeClientProtocolMethod( - for method: MethodDescriptor, - in service: ServiceDescriptor, - from codeGenerationRequest: CodeGenerationRequest, - includeBody: Bool, - accessModifier: AccessModifier? = nil, - includeDefaultCallOptions: Bool - ) -> Declaration { - let isProtocolExtension = includeBody - let methodParameters = self.makeParameters( - for: method, - in: service, - from: codeGenerationRequest, - // The serializer/deserializer for the protocol extension method will be auto-generated. - includeSerializationParameters: !isProtocolExtension, - includeDefaultCallOptions: includeDefaultCallOptions, - includeDefaultResponseHandler: isProtocolExtension && !method.isOutputStreaming - ) - let functionSignature = FunctionSignatureDescription( - accessModifier: accessModifier, - kind: .function( - name: method.name.generatedLowerCase, - isStatic: false - ), - generics: [.member("R")], - parameters: methodParameters, - keywords: [.async, .throws], - returnType: .identifierType(.member("R")), - whereClause: WhereClause(requirements: [.conformance("R", "Sendable")]) - ) - - if includeBody { - let body = self.makeClientProtocolMethodCall( - for: method, - in: service, - from: codeGenerationRequest - ) - return .function(signature: functionSignature, body: body) - } else { - return .commentable( - .preFormatted(method.documentation), - .function(signature: functionSignature) + services.flatMap { service in + self.translate( + accessModifier: accessModifier, + service: service, + serializer: serializer, + deserializer: deserializer ) } } - private func makeClientProtocolMethodCall( - for method: MethodDescriptor, - in service: ServiceDescriptor, - from codeGenerationRequest: CodeGenerationRequest + private func translate( + accessModifier: AccessModifier, + service: ServiceDescriptor, + serializer: (String) -> String, + deserializer: (String) -> String ) -> [CodeBlock] { - let functionCall = Expression.functionCall( - calledExpression: .memberAccess( - MemberAccessDescription( - left: .identifierPattern("self"), - right: method.name.generatedLowerCase - ) - ), - arguments: [ - FunctionArgumentDescription(label: "request", expression: .identifierPattern("request")), - FunctionArgumentDescription( - label: "serializer", - expression: .identifierPattern(codeGenerationRequest.lookupSerializer(method.inputType)) - ), - FunctionArgumentDescription( - label: "deserializer", - expression: .identifierPattern( - codeGenerationRequest.lookupDeserializer(method.outputType) - ) - ), - FunctionArgumentDescription(label: "options", expression: .identifierPattern("options")), - FunctionArgumentDescription(expression: .identifierPattern("body")), - ] - ) - let awaitFunctionCall = Expression.unaryKeyword(kind: .await, expression: functionCall) - let tryAwaitFunctionCall = Expression.unaryKeyword(kind: .try, expression: awaitFunctionCall) - - return [CodeBlock(item: .expression(tryAwaitFunctionCall))] - } + var blocks = [CodeBlock]() - private func makeParameters( - for method: MethodDescriptor, - in service: ServiceDescriptor, - from codeGenerationRequest: CodeGenerationRequest, - includeSerializationParameters: Bool, - includeDefaultCallOptions: Bool, - includeDefaultResponseHandler: Bool - ) -> [ParameterDescription] { - var parameters = [ParameterDescription]() + let protocolName = "\(service.namespacedGeneratedName)_ClientProtocol" + let protocolTypealias = "\(service.namespacedGeneratedName).ClientProtocol" + let structName = "\(service.namespacedGeneratedName)_Client" - parameters.append(self.clientRequestParameter(for: method, in: service)) - if includeSerializationParameters { - parameters.append(self.serializerParameter(for: method, in: service)) - parameters.append(self.deserializerParameter(for: method, in: service)) - } - parameters.append( - ParameterDescription( - label: "options", - type: .member(["GRPCCore", "CallOptions"]), - defaultValue: includeDefaultCallOptions - ? .memberAccess(MemberAccessDescription(right: "defaults")) : nil - ) - ) - parameters.append( - self.bodyParameter( - for: method, - in: service, - includeDefaultResponseHandler: includeDefaultResponseHandler - ) - ) - return parameters - } - private func clientRequestParameter( - for method: MethodDescriptor, - in service: ServiceDescriptor - ) -> ParameterDescription { - let requestType = method.isInputStreaming ? "Streaming" : "" - let clientRequestType = ExistingTypeDescription.member([ - "GRPCCore", - "\(requestType)ClientRequest", - ]) - return ParameterDescription( - label: "request", - type: .generic( - wrapper: clientRequestType, - wrapped: .member(method.inputType) - ) + let clientProtocol: ProtocolDescription = .clientProtocol( + accessLevel: accessModifier, + name: protocolName, + methods: service.methods ) - } - - private func serializerParameter( - for method: MethodDescriptor, - in service: ServiceDescriptor - ) -> ParameterDescription { - return ParameterDescription( - label: "serializer", - type: ExistingTypeDescription.some( - .generic( - wrapper: .member(["GRPCCore", "MessageSerializer"]), - wrapped: .member(method.inputType) - ) + blocks.append( + CodeBlock( + comment: .preFormatted(service.documentation), + item: .declaration(.guarded(.grpc, .protocol(clientProtocol))) ) ) - } - private func deserializerParameter( - for method: MethodDescriptor, - in service: ServiceDescriptor - ) -> ParameterDescription { - return ParameterDescription( - label: "deserializer", - type: ExistingTypeDescription.some( - .generic( - wrapper: .member(["GRPCCore", "MessageDeserializer"]), - wrapped: .member(method.outputType) - ) - ) + let extensionWithDefaults: ExtensionDescription = .clientMethodSignatureWithDefaults( + accessLevel: accessModifier, + name: protocolTypealias, + methods: service.methods, + serializer: serializer, + deserializer: deserializer ) - } - - private func bodyParameter( - for method: MethodDescriptor, - in service: ServiceDescriptor, - includeDefaultResponseHandler: Bool - ) -> ParameterDescription { - let clientStreaming = method.isOutputStreaming ? "Streaming" : "" - let closureParameterType = ExistingTypeDescription.generic( - wrapper: .member(["GRPCCore", "\(clientStreaming)ClientResponse"]), - wrapped: .member(method.outputType) + blocks.append( + CodeBlock(item: .declaration(.guarded(.grpc, .extension(extensionWithDefaults)))) ) - let bodyClosure = ClosureSignatureDescription( - parameters: [.init(type: closureParameterType)], - keywords: [.async, .throws], - returnType: .identifierType(.member("R")), - sendable: true, - escaping: true + let extensionWithExplodedAPI: ExtensionDescription = .explodedClientMethods( + accessLevel: accessModifier, + on: protocolTypealias, + methods: service.methods ) - - var defaultResponseHandler: Expression? = nil - - if includeDefaultResponseHandler { - defaultResponseHandler = .closureInvocation( - ClosureInvocationDescription( - body: [.expression(.try(.identifierPattern("$0").dot("message")))] - ) - ) - } - - return ParameterDescription( - name: "body", - type: .closure(bodyClosure), - defaultValue: defaultResponseHandler + blocks.append( + CodeBlock(item: .declaration(.guarded(.grpc, .extension(extensionWithExplodedAPI)))) ) - } - private func makeClientStruct( - for service: ServiceDescriptor, - in codeGenerationRequest: CodeGenerationRequest - ) -> Declaration { - let clientProperty = Declaration.variable( - accessModifier: .private, - kind: .let, - left: "client", - type: .member(["GRPCCore", "GRPCClient"]) + let clientStruct: StructDescription = .client( + accessLevel: accessModifier, + name: structName, + serviceEnum: service.namespacedGeneratedName, + clientProtocol: protocolTypealias, + methods: service.methods ) - let initializer = self.makeClientVariable() - let methods = service.methods.map { - Declaration.commentable( - .preFormatted($0.documentation), - self.makeClientMethod(for: $0, in: service, from: codeGenerationRequest) + blocks.append( + CodeBlock( + comment: .preFormatted(service.documentation), + item: .declaration(.guarded(.grpc, .struct(clientStruct))) ) - } - - return .guarded( - self.availabilityGuard, - .struct( - StructDescription( - accessModifier: self.accessModifier, - name: "\(service.namespacedGeneratedName)_Client", - conformances: ["\(service.namespacedGeneratedName).ClientProtocol"], - members: [clientProperty, initializer] + methods - ) - ) - ) - } - - private func makeClientVariable() -> Declaration { - let initializerBody = Expression.assignment( - left: .memberAccess( - MemberAccessDescription(left: .identifierPattern("self"), right: "client") - ), - right: .identifierPattern("client") - ) - return .function( - signature: .init( - accessModifier: self.accessModifier, - kind: .initializer, - parameters: [ - .init(label: "wrapping", name: "client", type: .member(["GRPCCore", "GRPCClient"])) - ] - ), - body: [CodeBlock(item: .expression(initializerBody))] - ) - } - - private func clientMethod( - isInputStreaming: Bool, - isOutputStreaming: Bool - ) -> String { - switch (isInputStreaming, isOutputStreaming) { - case (true, true): - return "bidirectionalStreaming" - case (true, false): - return "clientStreaming" - case (false, true): - return "serverStreaming" - case (false, false): - return "unary" - } - } - - private func makeClientMethod( - for method: MethodDescriptor, - in service: ServiceDescriptor, - from codeGenerationRequest: CodeGenerationRequest - ) -> Declaration { - let parameters = self.makeParameters( - for: method, - in: service, - from: codeGenerationRequest, - includeSerializationParameters: true, - includeDefaultCallOptions: true, - includeDefaultResponseHandler: !method.isOutputStreaming - ) - let grpcMethodName = self.clientMethod( - isInputStreaming: method.isInputStreaming, - isOutputStreaming: method.isOutputStreaming - ) - let functionCall = Expression.functionCall( - calledExpression: .memberAccess( - MemberAccessDescription(left: .identifierPattern("self.client"), right: "\(grpcMethodName)") - ), - arguments: [ - .init(label: "request", expression: .identifierPattern("request")), - .init( - label: "descriptor", - expression: .identifierPattern( - "\(service.namespacedGeneratedName).Method.\(method.name.generatedUpperCase).descriptor" - ) - ), - .init(label: "serializer", expression: .identifierPattern("serializer")), - .init(label: "deserializer", expression: .identifierPattern("deserializer")), - .init(label: "options", expression: .identifierPattern("options")), - .init(label: "handler", expression: .identifierPattern("body")), - ] - ) - let body = UnaryKeywordDescription( - kind: .try, - expression: .unaryKeyword(kind: .await, expression: functionCall) ) - return .function( - accessModifier: self.accessModifier, - kind: .function( - name: "\(method.name.generatedLowerCase)", - isStatic: false - ), - generics: [.member("R")], - parameters: parameters, - keywords: [.async, .throws], - returnType: .identifierType(.member("R")), - whereClause: WhereClause(requirements: [.conformance("R", "Sendable")]), - body: [.expression(.unaryKeyword(body))] - ) - } - - fileprivate enum InputOutputType { - case input - case output + return blocks } } diff --git a/Sources/GRPCCodeGen/Internal/Translator/IDLToStructuredSwiftTranslator.swift b/Sources/GRPCCodeGen/Internal/Translator/IDLToStructuredSwiftTranslator.swift index fb5474859..0fcbc5388 100644 --- a/Sources/GRPCCodeGen/Internal/Translator/IDLToStructuredSwiftTranslator.swift +++ b/Sources/GRPCCodeGen/Internal/Translator/IDLToStructuredSwiftTranslator.swift @@ -26,24 +26,36 @@ struct IDLToStructuredSwiftTranslator: Translator { server: Bool ) throws -> StructuredSwiftRepresentation { try self.validateInput(codeGenerationRequest) + let accessModifier = AccessModifier(accessLevel) - var codeBlocks = [CodeBlock]() - - let typealiasTranslator = TypealiasTranslator( + let metadataTranslator = MetadataTranslator() + var codeBlocks = metadataTranslator.translate( + accessModifier: accessModifier, + services: codeGenerationRequest.services, client: client, - server: server, - accessLevel: accessLevel + server: server ) - codeBlocks.append(contentsOf: try typealiasTranslator.translate(from: codeGenerationRequest)) if server { - let serverCodeTranslator = ServerCodeTranslator(accessLevel: accessLevel) - codeBlocks.append(contentsOf: try serverCodeTranslator.translate(from: codeGenerationRequest)) + let translator = ServerCodeTranslator() + let blocks = translator.translate( + accessModifier: accessModifier, + services: codeGenerationRequest.services, + serializer: codeGenerationRequest.lookupSerializer, + deserializer: codeGenerationRequest.lookupDeserializer + ) + codeBlocks.append(contentsOf: blocks) } if client { - let clientCodeTranslator = ClientCodeTranslator(accessLevel: accessLevel) - codeBlocks.append(contentsOf: try clientCodeTranslator.translate(from: codeGenerationRequest)) + let translator = ClientCodeTranslator() + let blocks = translator.translate( + accessModifier: accessModifier, + services: codeGenerationRequest.services, + serializer: codeGenerationRequest.lookupSerializer, + deserializer: codeGenerationRequest.lookupDeserializer + ) + codeBlocks.append(contentsOf: blocks) } let fileDescription = FileDescription( diff --git a/Sources/GRPCCodeGen/Internal/Translator/MetadataTranslator.swift b/Sources/GRPCCodeGen/Internal/Translator/MetadataTranslator.swift new file mode 100644 index 000000000..23e68c2cc --- /dev/null +++ b/Sources/GRPCCodeGen/Internal/Translator/MetadataTranslator.swift @@ -0,0 +1,49 @@ +/* + * 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. + */ + +struct MetadataTranslator { + init() {} + + func translate( + accessModifier: AccessModifier, + services: [ServiceDescriptor], + client: Bool, + server: Bool + ) -> [CodeBlock] { + return services.flatMap { service in + self.translate( + accessModifier: accessModifier, + service: service, + client: client, + server: server + ) + } + } + + private func translate( + accessModifier: AccessModifier, + service: ServiceDescriptor, + client: Bool, + server: Bool + ) -> [CodeBlock] { + .serviceMetadata( + accessModifier: accessModifier, + service: service, + client: client, + server: server + ) + } +} diff --git a/Sources/GRPCCodeGen/Internal/Translator/ServerCodeTranslator.swift b/Sources/GRPCCodeGen/Internal/Translator/ServerCodeTranslator.swift index a08431ad9..e769e6b61 100644 --- a/Sources/GRPCCodeGen/Internal/Translator/ServerCodeTranslator.swift +++ b/Sources/GRPCCodeGen/Internal/Translator/ServerCodeTranslator.swift @@ -57,422 +57,114 @@ /// } /// } ///``` -struct ServerCodeTranslator: SpecializedTranslator { - var accessLevel: SourceGenerator.Config.AccessLevel - - init(accessLevel: SourceGenerator.Config.AccessLevel) { - self.accessLevel = accessLevel - } - - func translate(from codeGenerationRequest: CodeGenerationRequest) throws -> [CodeBlock] { - var codeBlocks = [CodeBlock]() - for service in codeGenerationRequest.services { - // Create the streaming protocol that declares the service methods as bidirectional streaming. - let streamingProtocol = CodeBlockItem.declaration(self.makeStreamingProtocol(for: service)) - codeBlocks.append(CodeBlock(item: streamingProtocol)) - - // Create extension for implementing the 'registerRPCs' function which is a 'RegistrableRPCService' requirement. - let conformanceToRPCServiceExtension = CodeBlockItem.declaration( - self.makeConformanceToRPCServiceExtension(for: service, in: codeGenerationRequest) - ) - codeBlocks.append( - CodeBlock( - comment: .doc("Conformance to `GRPCCore.RegistrableRPCService`."), - item: conformanceToRPCServiceExtension - ) - ) - - // Create the service protocol that declares the service methods as they are described in the Source IDL (unary, - // client/server streaming or bidirectional streaming). - let serviceProtocol = CodeBlockItem.declaration(self.makeServiceProtocol(for: service)) - codeBlocks.append(CodeBlock(item: serviceProtocol)) - - // Create extension for partial conformance to the streaming protocol. - let extensionServiceProtocol = CodeBlockItem.declaration( - self.makeExtensionServiceProtocol(for: service) - ) - codeBlocks.append( - CodeBlock( - comment: .doc( - "Partial conformance to `\(self.protocolName(service: service, streaming: true))`." - ), - item: extensionServiceProtocol - ) - ) - } - - return codeBlocks - } -} - -extension ServerCodeTranslator { - private func makeStreamingProtocol( - for service: ServiceDescriptor - ) -> Declaration { - let methods = service.methods.compactMap { - Declaration.commentable( - .preFormatted($0.documentation), - .function( - FunctionDescription( - signature: self.makeStreamingMethodSignature(for: $0, in: service) - ) - ) - ) - } - - let streamingProtocol = Declaration.protocol( - .init( - accessModifier: self.accessModifier, - name: self.protocolName(service: service, streaming: true), - conformances: ["GRPCCore.RegistrableRPCService"], - members: methods - ) - ) - - return .commentable( - .preFormatted(service.documentation), - .guarded(self.availabilityGuard, streamingProtocol) - ) - } - - private func makeStreamingMethodSignature( - for method: MethodDescriptor, - in service: ServiceDescriptor, - accessModifier: AccessModifier? = nil - ) -> FunctionSignatureDescription { - return FunctionSignatureDescription( - accessModifier: accessModifier, - kind: .function(name: method.name.generatedLowerCase), - parameters: [ - .init( - label: "request", - type: .generic( - wrapper: .member(["GRPCCore", "StreamingServerRequest"]), - wrapped: .member(method.inputType) - ) - ), - .init(label: "context", type: .member(["GRPCCore", "ServerContext"])), - ], - keywords: [.async, .throws], - returnType: .identifierType( - .generic( - wrapper: .member(["GRPCCore", "StreamingServerResponse"]), - wrapped: .member(method.outputType) - ) - ) - ) - } - - private func makeConformanceToRPCServiceExtension( - for service: ServiceDescriptor, - in codeGenerationRequest: CodeGenerationRequest - ) -> Declaration { - let streamingProtocol = self.protocolNameTypealias(service: service, streaming: true) - let registerRPCMethod = self.makeRegisterRPCsMethod(for: service, in: codeGenerationRequest) - return .guarded( - self.availabilityGuard, - .extension( - onType: streamingProtocol, - declarations: [registerRPCMethod] - ) - ) - } - - private func makeRegisterRPCsMethod( - for service: ServiceDescriptor, - in codeGenerationRequest: CodeGenerationRequest - ) -> Declaration { - let registerRPCsSignature = FunctionSignatureDescription( - accessModifier: self.accessModifier, - kind: .function(name: "registerMethods"), - parameters: [ - .init( - label: "with", - name: "router", - type: .member(["GRPCCore", "RPCRouter"]), - `inout`: true - ) - ] - ) - let registerRPCsBody = self.makeRegisterRPCsMethodBody(for: service, in: codeGenerationRequest) - return .guarded( - self.availabilityGuard, - .function(signature: registerRPCsSignature, body: registerRPCsBody) - ) - } - - private func makeRegisterRPCsMethodBody( - for service: ServiceDescriptor, - in codeGenerationRequest: CodeGenerationRequest +struct ServerCodeTranslator { + init() {} + + func translate( + accessModifier: AccessModifier, + services: [ServiceDescriptor], + serializer: (String) -> String, + deserializer: (String) -> String ) -> [CodeBlock] { - let registerHandlerCalls = service.methods.compactMap { - CodeBlock.expression( - Expression.functionCall( - calledExpression: .memberAccess( - MemberAccessDescription(left: .identifierPattern("router"), right: "registerHandler") - ), - arguments: self.makeArgumentsForRegisterHandler( - for: $0, - in: service, - from: codeGenerationRequest - ) - ) + return services.flatMap { service in + self.translate( + accessModifier: accessModifier, + service: service, + serializer: serializer, + deserializer: deserializer ) } - - return registerHandlerCalls } - private func makeArgumentsForRegisterHandler( - for method: MethodDescriptor, - in service: ServiceDescriptor, - from codeGenerationRequest: CodeGenerationRequest - ) -> [FunctionArgumentDescription] { - var arguments = [FunctionArgumentDescription]() - arguments.append( - FunctionArgumentDescription( - label: "forMethod", - expression: .identifierPattern(self.methodDescriptorPath(for: method, service: service)) - ) - ) - - arguments.append( - FunctionArgumentDescription( - label: "deserializer", - expression: .identifierPattern(codeGenerationRequest.lookupDeserializer(method.inputType)) - ) - ) + private func translate( + accessModifier: AccessModifier, + service: ServiceDescriptor, + serializer: (String) -> String, + deserializer: (String) -> String + ) -> [CodeBlock] { + var blocks = [CodeBlock]() - arguments.append( - FunctionArgumentDescription( - label: "serializer", - expression: .identifierPattern(codeGenerationRequest.lookupSerializer(method.outputType)) - ) + let serviceProtocolName = self.protocolName(service: service, streaming: false) + let serviceTypealiasName = self.protocolName( + service: service, + streaming: false, + joinedUsing: "." ) - - let rpcFunctionCall = Expression.functionCall( - calledExpression: .memberAccess( - MemberAccessDescription( - left: .identifierPattern("self"), - right: method.name.generatedLowerCase - ) - ), - arguments: [ - FunctionArgumentDescription(label: "request", expression: .identifierPattern("request")), - FunctionArgumentDescription(label: "context", expression: .identifierPattern("context")), - ] + let streamingServiceProtocolName = self.protocolName(service: service, streaming: true) + let streamingServiceTypealiasName = self.protocolName( + service: service, + streaming: true, + joinedUsing: "." ) - let handlerClosureBody = Expression.unaryKeyword( - kind: .try, - expression: .unaryKeyword(kind: .await, expression: rpcFunctionCall) + // protocol _StreamingServiceProtocol { ... } + let streamingServiceProtocol: ProtocolDescription = .streamingService( + accessLevel: accessModifier, + name: streamingServiceProtocolName, + methods: service.methods ) - - arguments.append( - FunctionArgumentDescription( - label: "handler", - expression: .closureInvocation( - ClosureInvocationDescription( - argumentNames: ["request", "context"], - body: [.expression(handlerClosureBody)] - ) - ) + blocks.append( + CodeBlock( + comment: .preFormatted(service.documentation), + item: .declaration(.guarded(.grpc, .protocol(streamingServiceProtocol))) ) ) - return arguments - } - - private func makeServiceProtocol( - for service: ServiceDescriptor - ) -> Declaration { - let methods = service.methods.compactMap { - self.makeServiceProtocolMethod(for: $0, in: service) - } - let protocolName = self.protocolName(service: service, streaming: false) - let streamingProtocol = self.protocolNameTypealias(service: service, streaming: true) - - return .commentable( - .preFormatted(service.documentation), - .guarded( - self.availabilityGuard, - .protocol( - ProtocolDescription( - accessModifier: self.accessModifier, - name: protocolName, - conformances: [streamingProtocol], - members: methods - ) - ) - ) + // extension _StreamingServiceProtocol> { ... } + let registerExtension: ExtensionDescription = .registrableRPCServiceDefaultImplementation( + accessLevel: accessModifier, + on: streamingServiceTypealiasName, + serviceNamespace: service.namespacedGeneratedName, + methods: service.methods, + serializer: serializer, + deserializer: deserializer ) - } - - private func makeServiceProtocolMethod( - for method: MethodDescriptor, - in service: ServiceDescriptor, - accessModifier: AccessModifier? = nil - ) -> Declaration { - let inputStreaming = method.isInputStreaming ? "Streaming" : "" - let outputStreaming = method.isOutputStreaming ? "Streaming" : "" - - let functionSignature = FunctionSignatureDescription( - accessModifier: accessModifier, - kind: .function(name: method.name.generatedLowerCase), - parameters: [ - .init( - label: "request", - type: - .generic( - wrapper: .member(["GRPCCore", "\(inputStreaming)ServerRequest"]), - wrapped: .member(method.inputType) - ) - ), - .init(label: "context", type: .member(["GRPCCore", "ServerContext"])), - ], - keywords: [.async, .throws], - returnType: .identifierType( - .generic( - wrapper: .member(["GRPCCore", "\(outputStreaming)ServerResponse"]), - wrapped: .member(method.outputType) - ) + blocks.append( + CodeBlock( + comment: .doc("Conformance to `GRPCCore.RegistrableRPCService`."), + item: .declaration(.guarded(.grpc, .extension(registerExtension))) ) ) - return .commentable( - .preFormatted(method.documentation), - .function(FunctionDescription(signature: functionSignature)) + // protocol _ServiceProtocol { ... } + let serviceProtocol: ProtocolDescription = .service( + accessLevel: accessModifier, + name: serviceProtocolName, + streamingProtocol: streamingServiceTypealiasName, + methods: service.methods ) - } - - private func makeExtensionServiceProtocol( - for service: ServiceDescriptor - ) -> Declaration { - let methods = service.methods.compactMap { - self.makeServiceProtocolExtensionMethod(for: $0, in: service) - } - - let protocolName = self.protocolNameTypealias(service: service, streaming: false) - return .guarded( - self.availabilityGuard, - .extension( - onType: protocolName, - declarations: methods + blocks.append( + CodeBlock( + comment: .preFormatted(service.documentation), + item: .declaration(.guarded(.grpc, .protocol(serviceProtocol))) ) ) - } - private func makeServiceProtocolExtensionMethod( - for method: MethodDescriptor, - in service: ServiceDescriptor - ) -> Declaration? { - // The method has the same definition in StreamingServiceProtocol and ServiceProtocol. - if method.isInputStreaming && method.isOutputStreaming { - return nil - } - - let response = CodeBlock(item: .declaration(self.makeResponse(for: method))) - let returnStatement = CodeBlock(item: .expression(self.makeReturnStatement(for: method))) - - return .function( - signature: self.makeStreamingMethodSignature( - for: method, - in: service, - accessModifier: self.accessModifier - ), - body: [response, returnStatement] - ) - } - - private func makeResponse( - for method: MethodDescriptor - ) -> Declaration { - let serverRequest: Expression - if !method.isInputStreaming { - // Transform the streaming request into a unary request. - serverRequest = Expression.functionCall( - calledExpression: .identifierType(.member(["GRPCCore", "ServerRequest"])), - arguments: [ - FunctionArgumentDescription(label: "stream", expression: .identifierPattern("request")) - ] + // extension _ServiceProtocol { ... } + let streamingServiceDefaultImplExtension: ExtensionDescription = + .streamingServiceProtocolDefaultImplementation( + accessModifier: accessModifier, + on: serviceTypealiasName, + methods: service.methods ) - } else { - serverRequest = Expression.identifierPattern("request") - } - // Call to the corresponding ServiceProtocol method. - let serviceProtocolMethod = Expression.functionCall( - calledExpression: .memberAccess( - MemberAccessDescription( - left: .identifierPattern("self"), - right: method.name.generatedLowerCase - ) - ), - arguments: [ - FunctionArgumentDescription(label: "request", expression: serverRequest), - FunctionArgumentDescription(label: "context", expression: .identifier(.pattern("context"))), - ] - ) - - let responseValue = Expression.unaryKeyword( - kind: .try, - expression: .unaryKeyword(kind: .await, expression: serviceProtocolMethod) - ) - - return .variable(kind: .let, left: "response", right: responseValue) - } - - private func makeReturnStatement( - for method: MethodDescriptor - ) -> Expression { - let returnValue: Expression - // Transforming the unary response into a streaming one. - if !method.isOutputStreaming { - returnValue = .functionCall( - calledExpression: .identifier(.type(.member(["GRPCCore", "StreamingServerResponse"]))), - arguments: [ - (FunctionArgumentDescription(label: "single", expression: .identifierPattern("response"))) - ] + blocks.append( + CodeBlock( + comment: .doc("Partial conformance to `\(streamingServiceProtocolName)`."), + item: .declaration(.guarded(.grpc, .extension(streamingServiceDefaultImplExtension))) ) - } else { - returnValue = .identifierPattern("response") - } - - return .unaryKeyword(kind: .return, expression: returnValue) - } - - fileprivate enum InputOutputType { - case input - case output - } - - /// Generates the fully qualified name of a method descriptor. - private func methodDescriptorPath( - for method: MethodDescriptor, - service: ServiceDescriptor - ) -> String { - return - "\(service.namespacedGeneratedName).Method.\(method.name.generatedUpperCase).descriptor" - } + ) - /// Generates the fully qualified name of the type alias for a service protocol. - internal func protocolNameTypealias( - service: ServiceDescriptor, - streaming: Bool - ) -> String { - if streaming { - return "\(service.namespacedGeneratedName).StreamingServiceProtocol" - } - return "\(service.namespacedGeneratedName).ServiceProtocol" + return blocks } - /// Generates the name of a service protocol. - internal func protocolName( + private func protocolName( service: ServiceDescriptor, - streaming: Bool + streaming: Bool, + joinedUsing join: String = "_" ) -> String { if streaming { - return "\(service.namespacedGeneratedName)_StreamingServiceProtocol" + return "\(service.namespacedGeneratedName)\(join)StreamingServiceProtocol" } - return "\(service.namespacedGeneratedName)_ServiceProtocol" + return "\(service.namespacedGeneratedName)\(join)ServiceProtocol" } } diff --git a/Sources/GRPCCodeGen/Internal/Translator/TypealiasTranslator.swift b/Sources/GRPCCodeGen/Internal/Translator/TypealiasTranslator.swift deleted file mode 100644 index 8ae83f70e..000000000 --- a/Sources/GRPCCodeGen/Internal/Translator/TypealiasTranslator.swift +++ /dev/null @@ -1,371 +0,0 @@ -/* - * Copyright 2023, 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. - */ - -/// Creates enums containing useful type aliases and static properties for the methods, services and -/// namespaces described in a ``CodeGenerationRequest`` object, using types from -/// ``StructuredSwiftRepresentation``. -/// -/// For example, in the case of the ``Echo`` service, the ``TypealiasTranslator`` will create -/// a representation for the following generated code: -/// ```swift -/// public enum Echo_Echo { -/// public static let descriptor = GRPCCore.ServiceDescriptor.echo_Echo -/// -/// public enum Method { -/// public enum Get { -/// public typealias Input = Echo_EchoRequest -/// public typealias Output = Echo_EchoResponse -/// public static let descriptor = GRPCCore.MethodDescriptor( -/// service: Echo_Echo.descriptor.fullyQualifiedService, -/// method: "Get" -/// ) -/// } -/// -/// public enum Collect { -/// public typealias Input = Echo_EchoRequest -/// public typealias Output = Echo_EchoResponse -/// public static let descriptor = GRPCCore.MethodDescriptor( -/// service: Echo_Echo.descriptor.fullyQualifiedService, -/// method: "Collect" -/// ) -/// } -/// // ... -/// -/// public static let descriptors: [GRPCCore.MethodDescriptor] = [ -/// Get.descriptor, -/// Collect.descriptor, -/// // ... -/// ] -/// } -/// -/// @available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *) -/// public typealias StreamingServiceProtocol = Echo_EchoServiceStreamingProtocol -/// @available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *) -/// public typealias ServiceProtocol = Echo_EchoServiceProtocol -/// -/// } -/// -/// extension GRPCCore.ServiceDescriptor { -/// public static let echo_Echo = Self( -/// package: "echo", -/// service: "Echo" -/// ) -/// } -/// ``` -/// -/// A ``CodeGenerationRequest`` can contain multiple namespaces, so the TypealiasTranslator will create a ``CodeBlock`` -/// for each namespace. -struct TypealiasTranslator: SpecializedTranslator { - let client: Bool - let server: Bool - let accessLevel: SourceGenerator.Config.AccessLevel - - init(client: Bool, server: Bool, accessLevel: SourceGenerator.Config.AccessLevel) { - self.client = client - self.server = server - self.accessLevel = accessLevel - } - - func translate(from codeGenerationRequest: CodeGenerationRequest) throws -> [CodeBlock] { - var codeBlocks = [CodeBlock]() - - for service in codeGenerationRequest.services { - codeBlocks.append( - CodeBlock( - item: .declaration( - try self.makeServiceEnum( - from: service, - named: service.namespacedGeneratedName - ) - ) - ) - ) - - codeBlocks.append( - CodeBlock(item: .declaration(self.makeServiceDescriptorExtension(for: service))) - ) - } - - return codeBlocks - } -} - -extension TypealiasTranslator { - private func makeServiceEnum( - from service: ServiceDescriptor, - named name: String - ) throws -> Declaration { - var serviceEnum = EnumDescription( - accessModifier: self.accessModifier, - name: name - ) - var methodsEnum = EnumDescription(accessModifier: self.accessModifier, name: "Method") - let methods = service.methods - - // Create the method specific enums. - for method in methods { - let methodEnum = self.makeMethodEnum(from: method, in: service) - methodsEnum.members.append(methodEnum) - } - - // Create the method descriptor array. - let methodDescriptorsDeclaration = self.makeMethodDescriptors(for: service) - methodsEnum.members.append(methodDescriptorsDeclaration) - - // Create the static service descriptor property. - let staticServiceDescriptorProperty = self.makeStaticServiceDescriptorProperty(for: service) - - serviceEnum.members.append(.variable(staticServiceDescriptorProperty)) - serviceEnum.members.append(.enum(methodsEnum)) - - if self.server { - // Create the streaming and non-streaming service protocol type aliases. - let serviceProtocols = self.makeServiceProtocolsTypealiases(for: service) - serviceEnum.members.append(contentsOf: serviceProtocols) - } - - if self.client { - // Create the client protocol type alias. - let clientProtocol = self.makeClientProtocolTypealias(for: service) - serviceEnum.members.append(clientProtocol) - - // Create type alias for Client struct. - let clientStruct = self.makeClientStructTypealias(for: service) - serviceEnum.members.append(clientStruct) - } - - return .enum(serviceEnum) - } - - private func makeMethodEnum( - from method: MethodDescriptor, - in service: ServiceDescriptor - ) -> Declaration { - var methodEnum = EnumDescription(name: method.name.generatedUpperCase) - - let inputTypealias = Declaration.typealias( - accessModifier: self.accessModifier, - name: "Input", - existingType: .member([method.inputType]) - ) - let outputTypealias = Declaration.typealias( - accessModifier: self.accessModifier, - name: "Output", - existingType: .member([method.outputType]) - ) - let descriptorVariable = self.makeMethodDescriptor( - from: method, - in: service - ) - methodEnum.members.append(inputTypealias) - methodEnum.members.append(outputTypealias) - methodEnum.members.append(descriptorVariable) - - methodEnum.accessModifier = self.accessModifier - - return .enum(methodEnum) - } - - private func makeMethodDescriptor( - from method: MethodDescriptor, - in service: ServiceDescriptor - ) -> Declaration { - let fullyQualifiedService = MemberAccessDescription( - left: .memberAccess( - MemberAccessDescription( - left: .identifierType(.member([service.namespacedGeneratedName])), - right: "descriptor" - ) - ), - right: "fullyQualifiedService" - ) - - let descriptorDeclarationLeft = Expression.identifier(.pattern("descriptor")) - let descriptorDeclarationRight = Expression.functionCall( - FunctionCallDescription( - calledExpression: .identifierType(.member(["GRPCCore", "MethodDescriptor"])), - arguments: [ - FunctionArgumentDescription( - label: "service", - expression: .memberAccess(fullyQualifiedService) - ), - FunctionArgumentDescription( - label: "method", - expression: .literal(method.name.base) - ), - ] - ) - ) - - return .variable( - accessModifier: self.accessModifier, - isStatic: true, - kind: .let, - left: descriptorDeclarationLeft, - right: descriptorDeclarationRight - ) - } - - private func makeMethodDescriptors( - for service: ServiceDescriptor - ) -> Declaration { - var methodDescriptors = [Expression]() - let methodNames = service.methods.map { $0.name.generatedUpperCase } - - for methodName in methodNames { - let methodDescriptorPath = Expression.memberAccess( - MemberAccessDescription( - left: .identifierType( - .member([methodName]) - ), - right: "descriptor" - ) - ) - methodDescriptors.append(methodDescriptorPath) - } - - return .variable( - accessModifier: self.accessModifier, - isStatic: true, - kind: .let, - left: .identifier(.pattern("descriptors")), - type: .array(.member(["GRPCCore", "MethodDescriptor"])), - right: .literal(.array(methodDescriptors)) - ) - } - - private func makeServiceProtocolsTypealiases( - for service: ServiceDescriptor - ) -> [Declaration] { - let streamingServiceProtocolTypealias = Declaration.typealias( - accessModifier: self.accessModifier, - name: "StreamingServiceProtocol", - existingType: .member("\(service.namespacedGeneratedName)_StreamingServiceProtocol") - ) - let serviceProtocolTypealias = Declaration.typealias( - accessModifier: self.accessModifier, - name: "ServiceProtocol", - existingType: .member("\(service.namespacedGeneratedName)_ServiceProtocol") - ) - - return [ - .guarded( - self.availabilityGuard, - streamingServiceProtocolTypealias - ), - .guarded( - self.availabilityGuard, - serviceProtocolTypealias - ), - ] - } - - private func makeClientProtocolTypealias( - for service: ServiceDescriptor - ) -> Declaration { - return .guarded( - self.availabilityGuard, - .typealias( - accessModifier: self.accessModifier, - name: "ClientProtocol", - existingType: .member("\(service.namespacedGeneratedName)_ClientProtocol") - ) - ) - } - - private func makeClientStructTypealias( - for service: ServiceDescriptor - ) -> Declaration { - return .guarded( - self.availabilityGuard, - .typealias( - accessModifier: self.accessModifier, - name: "Client", - existingType: .member("\(service.namespacedGeneratedName)_Client") - ) - ) - } - - private func makeServiceIdentifier(_ service: ServiceDescriptor) -> String { - let prefix: String - - if service.namespace.normalizedBase.isEmpty { - prefix = "" - } else { - prefix = service.namespace.normalizedBase + "_" - } - - return prefix + service.name.normalizedBase - } - - private func makeStaticServiceDescriptorProperty( - for service: ServiceDescriptor - ) -> VariableDescription { - let serviceIdentifier = makeServiceIdentifier(service) - - return VariableDescription( - accessModifier: self.accessModifier, - isStatic: true, - kind: .let, - left: .identifierPattern("descriptor"), - right: .memberAccess( - MemberAccessDescription( - left: .identifierPattern("GRPCCore.ServiceDescriptor"), - right: serviceIdentifier - ) - ) - ) - } - - private func makeServiceDescriptorExtension( - for service: ServiceDescriptor - ) -> Declaration { - let serviceIdentifier = makeServiceIdentifier(service) - - let serviceDescriptorInitialization = Expression.functionCall( - FunctionCallDescription( - calledExpression: .identifierType(.member("Self")), - arguments: [ - FunctionArgumentDescription( - label: "package", - expression: .literal(service.namespace.base) - ), - FunctionArgumentDescription( - label: "service", - expression: .literal(service.name.base) - ), - ] - ) - ) - - return .extension( - ExtensionDescription( - onType: "GRPCCore.ServiceDescriptor", - declarations: [ - .variable( - VariableDescription( - accessModifier: self.accessModifier, - isStatic: true, - kind: .let, - left: .identifier(.pattern(serviceIdentifier)), - right: serviceDescriptorInitialization - ) - ) - ] - ) - ) - } -} diff --git a/Tests/GRPCCodeGenTests/Internal/Translator/ClientCodeTranslatorSnippetBasedTests.swift b/Tests/GRPCCodeGenTests/Internal/Translator/ClientCodeTranslatorSnippetBasedTests.swift index 6dd72600c..cfb72633c 100644 --- a/Tests/GRPCCodeGenTests/Internal/Translator/ClientCodeTranslatorSnippetBasedTests.swift +++ b/Tests/GRPCCodeGenTests/Internal/Translator/ClientCodeTranslatorSnippetBasedTests.swift @@ -42,29 +42,29 @@ final class ClientCodeTranslatorSnippetBasedTests: XCTestCase { @available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *) public protocol NamespaceA_ServiceA_ClientProtocol: Sendable { /// Documentation for MethodA - func methodA( + func methodA( request: GRPCCore.ClientRequest, serializer: some GRPCCore.MessageSerializer, deserializer: some GRPCCore.MessageDeserializer, options: GRPCCore.CallOptions, - _ body: @Sendable @escaping (GRPCCore.ClientResponse) async throws -> R - ) async throws -> R where R: Sendable + onResponse handleResponse: @Sendable @escaping (GRPCCore.ClientResponse) async throws -> Result + ) async throws -> Result where Result: Sendable } @available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *) extension NamespaceA_ServiceA.ClientProtocol { - public func methodA( + public func methodA( request: GRPCCore.ClientRequest, options: GRPCCore.CallOptions = .defaults, - _ body: @Sendable @escaping (GRPCCore.ClientResponse) async throws -> R = { - try $0.message + onResponse handleResponse: @Sendable @escaping (GRPCCore.ClientResponse) async throws -> Result = { response in + try response.message } - ) async throws -> R where R: Sendable { + ) async throws -> Result where Result: Sendable { try await self.methodA( request: request, serializer: GRPCProtobuf.ProtobufSerializer(), deserializer: GRPCProtobuf.ProtobufDeserializer(), options: options, - body + onResponse: handleResponse ) } } @@ -75,8 +75,8 @@ final class ClientCodeTranslatorSnippetBasedTests: XCTestCase { _ message: NamespaceA_ServiceARequest, metadata: GRPCCore.Metadata = [:], options: GRPCCore.CallOptions = .defaults, - onResponse handleResponse: @Sendable @escaping (GRPCCore.ClientResponse) async throws -> Result = { - try $0.message + onResponse handleResponse: @Sendable @escaping (GRPCCore.ClientResponse) async throws -> Result = { response in + try response.message } ) async throws -> Result where Result: Sendable { let request = GRPCCore.ClientRequest( @@ -86,7 +86,7 @@ final class ClientCodeTranslatorSnippetBasedTests: XCTestCase { return try await self.methodA( request: request, options: options, - handleResponse + onResponse: handleResponse ) } } @@ -100,22 +100,22 @@ final class ClientCodeTranslatorSnippetBasedTests: XCTestCase { } /// Documentation for MethodA - public func methodA( + public func methodA( request: GRPCCore.ClientRequest, serializer: some GRPCCore.MessageSerializer, deserializer: some GRPCCore.MessageDeserializer, options: GRPCCore.CallOptions = .defaults, - _ body: @Sendable @escaping (GRPCCore.ClientResponse) async throws -> R = { - try $0.message + onResponse handleResponse: @Sendable @escaping (GRPCCore.ClientResponse) async throws -> Result = { response in + try response.message } - ) async throws -> R where R: Sendable { + ) async throws -> Result where Result: Sendable { try await self.client.unary( request: request, descriptor: NamespaceA_ServiceA.Method.MethodA.descriptor, serializer: serializer, deserializer: deserializer, options: options, - handler: body + onResponse: handleResponse ) } } @@ -149,29 +149,29 @@ final class ClientCodeTranslatorSnippetBasedTests: XCTestCase { @available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *) public protocol NamespaceA_ServiceA_ClientProtocol: Sendable { /// Documentation for MethodA - func methodA( + func methodA( request: GRPCCore.StreamingClientRequest, serializer: some GRPCCore.MessageSerializer, deserializer: some GRPCCore.MessageDeserializer, options: GRPCCore.CallOptions, - _ body: @Sendable @escaping (GRPCCore.ClientResponse) async throws -> R - ) async throws -> R where R: Sendable + onResponse handleResponse: @Sendable @escaping (GRPCCore.ClientResponse) async throws -> Result + ) async throws -> Result where Result: Sendable } @available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *) extension NamespaceA_ServiceA.ClientProtocol { - public func methodA( + public func methodA( request: GRPCCore.StreamingClientRequest, options: GRPCCore.CallOptions = .defaults, - _ body: @Sendable @escaping (GRPCCore.ClientResponse) async throws -> R = { - try $0.message + onResponse handleResponse: @Sendable @escaping (GRPCCore.ClientResponse) async throws -> Result = { response in + try response.message } - ) async throws -> R where R: Sendable { + ) async throws -> Result where Result: Sendable { try await self.methodA( request: request, serializer: GRPCProtobuf.ProtobufSerializer(), deserializer: GRPCProtobuf.ProtobufDeserializer(), options: options, - body + onResponse: handleResponse ) } } @@ -181,19 +181,19 @@ final class ClientCodeTranslatorSnippetBasedTests: XCTestCase { public func methodA( metadata: GRPCCore.Metadata = [:], options: GRPCCore.CallOptions = .defaults, - requestProducer: @Sendable @escaping (GRPCCore.RPCWriter) async throws -> Void, - onResponse handleResponse: @Sendable @escaping (GRPCCore.ClientResponse) async throws -> Result = { - try $0.message + requestProducer producer: @Sendable @escaping (GRPCCore.RPCWriter) async throws -> Void, + onResponse handleResponse: @Sendable @escaping (GRPCCore.ClientResponse) async throws -> Result = { response in + try response.message } ) async throws -> Result where Result: Sendable { let request = GRPCCore.StreamingClientRequest( metadata: metadata, - producer: requestProducer + producer: producer ) return try await self.methodA( request: request, options: options, - handleResponse + onResponse: handleResponse ) } } @@ -207,22 +207,22 @@ final class ClientCodeTranslatorSnippetBasedTests: XCTestCase { } /// Documentation for MethodA - public func methodA( + public func methodA( request: GRPCCore.StreamingClientRequest, serializer: some GRPCCore.MessageSerializer, deserializer: some GRPCCore.MessageDeserializer, options: GRPCCore.CallOptions = .defaults, - _ body: @Sendable @escaping (GRPCCore.ClientResponse) async throws -> R = { - try $0.message + onResponse handleResponse: @Sendable @escaping (GRPCCore.ClientResponse) async throws -> Result = { response in + try response.message } - ) async throws -> R where R: Sendable { + ) async throws -> Result where Result: Sendable { try await self.client.clientStreaming( request: request, descriptor: NamespaceA_ServiceA.Method.MethodA.descriptor, serializer: serializer, deserializer: deserializer, options: options, - handler: body + onResponse: handleResponse ) } } @@ -256,27 +256,27 @@ final class ClientCodeTranslatorSnippetBasedTests: XCTestCase { @available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *) public protocol NamespaceA_ServiceA_ClientProtocol: Sendable { /// Documentation for MethodA - func methodA( + func methodA( request: GRPCCore.ClientRequest, serializer: some GRPCCore.MessageSerializer, deserializer: some GRPCCore.MessageDeserializer, options: GRPCCore.CallOptions, - _ body: @Sendable @escaping (GRPCCore.StreamingClientResponse) async throws -> R - ) async throws -> R where R: Sendable + onResponse handleResponse: @Sendable @escaping (GRPCCore.StreamingClientResponse) async throws -> Result + ) async throws -> Result where Result: Sendable } @available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *) extension NamespaceA_ServiceA.ClientProtocol { - public func methodA( + public func methodA( request: GRPCCore.ClientRequest, options: GRPCCore.CallOptions = .defaults, - _ body: @Sendable @escaping (GRPCCore.StreamingClientResponse) async throws -> R - ) async throws -> R where R: Sendable { + onResponse handleResponse: @Sendable @escaping (GRPCCore.StreamingClientResponse) async throws -> Result + ) async throws -> Result where Result: Sendable { try await self.methodA( request: request, serializer: GRPCProtobuf.ProtobufSerializer(), deserializer: GRPCProtobuf.ProtobufDeserializer(), options: options, - body + onResponse: handleResponse ) } } @@ -296,7 +296,7 @@ final class ClientCodeTranslatorSnippetBasedTests: XCTestCase { return try await self.methodA( request: request, options: options, - handleResponse + onResponse: handleResponse ) } } @@ -310,20 +310,20 @@ final class ClientCodeTranslatorSnippetBasedTests: XCTestCase { } /// Documentation for MethodA - public func methodA( + public func methodA( request: GRPCCore.ClientRequest, serializer: some GRPCCore.MessageSerializer, deserializer: some GRPCCore.MessageDeserializer, options: GRPCCore.CallOptions = .defaults, - _ body: @Sendable @escaping (GRPCCore.StreamingClientResponse) async throws -> R - ) async throws -> R where R: Sendable { + onResponse handleResponse: @Sendable @escaping (GRPCCore.StreamingClientResponse) async throws -> Result + ) async throws -> Result where Result: Sendable { try await self.client.serverStreaming( request: request, descriptor: NamespaceA_ServiceA.Method.MethodA.descriptor, serializer: serializer, deserializer: deserializer, options: options, - handler: body + onResponse: handleResponse ) } } @@ -357,27 +357,27 @@ final class ClientCodeTranslatorSnippetBasedTests: XCTestCase { @available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *) public protocol NamespaceA_ServiceA_ClientProtocol: Sendable { /// Documentation for MethodA - func methodA( + func methodA( request: GRPCCore.StreamingClientRequest, serializer: some GRPCCore.MessageSerializer, deserializer: some GRPCCore.MessageDeserializer, options: GRPCCore.CallOptions, - _ body: @Sendable @escaping (GRPCCore.StreamingClientResponse) async throws -> R - ) async throws -> R where R: Sendable + onResponse handleResponse: @Sendable @escaping (GRPCCore.StreamingClientResponse) async throws -> Result + ) async throws -> Result where Result: Sendable } @available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *) extension NamespaceA_ServiceA.ClientProtocol { - public func methodA( + public func methodA( request: GRPCCore.StreamingClientRequest, options: GRPCCore.CallOptions = .defaults, - _ body: @Sendable @escaping (GRPCCore.StreamingClientResponse) async throws -> R - ) async throws -> R where R: Sendable { + onResponse handleResponse: @Sendable @escaping (GRPCCore.StreamingClientResponse) async throws -> Result + ) async throws -> Result where Result: Sendable { try await self.methodA( request: request, serializer: GRPCProtobuf.ProtobufSerializer(), deserializer: GRPCProtobuf.ProtobufDeserializer(), options: options, - body + onResponse: handleResponse ) } } @@ -387,17 +387,17 @@ final class ClientCodeTranslatorSnippetBasedTests: XCTestCase { public func methodA( metadata: GRPCCore.Metadata = [:], options: GRPCCore.CallOptions = .defaults, - requestProducer: @Sendable @escaping (GRPCCore.RPCWriter) async throws -> Void, + requestProducer producer: @Sendable @escaping (GRPCCore.RPCWriter) async throws -> Void, onResponse handleResponse: @Sendable @escaping (GRPCCore.StreamingClientResponse) async throws -> Result ) async throws -> Result where Result: Sendable { let request = GRPCCore.StreamingClientRequest( metadata: metadata, - producer: requestProducer + producer: producer ) return try await self.methodA( request: request, options: options, - handleResponse + onResponse: handleResponse ) } } @@ -411,20 +411,20 @@ final class ClientCodeTranslatorSnippetBasedTests: XCTestCase { } /// Documentation for MethodA - public func methodA( + public func methodA( request: GRPCCore.StreamingClientRequest, serializer: some GRPCCore.MessageSerializer, deserializer: some GRPCCore.MessageDeserializer, options: GRPCCore.CallOptions = .defaults, - _ body: @Sendable @escaping (GRPCCore.StreamingClientResponse) async throws -> R - ) async throws -> R where R: Sendable { + onResponse handleResponse: @Sendable @escaping (GRPCCore.StreamingClientResponse) async throws -> Result + ) async throws -> Result where Result: Sendable { try await self.client.bidirectionalStreaming( request: request, descriptor: NamespaceA_ServiceA.Method.MethodA.descriptor, serializer: serializer, deserializer: deserializer, options: options, - handler: body + onResponse: handleResponse ) } } @@ -466,52 +466,52 @@ final class ClientCodeTranslatorSnippetBasedTests: XCTestCase { @available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *) package protocol NamespaceA_ServiceA_ClientProtocol: Sendable { /// Documentation for MethodA - func methodA( + func methodA( request: GRPCCore.StreamingClientRequest, serializer: some GRPCCore.MessageSerializer, deserializer: some GRPCCore.MessageDeserializer, options: GRPCCore.CallOptions, - _ body: @Sendable @escaping (GRPCCore.ClientResponse) async throws -> R - ) async throws -> R where R: Sendable + onResponse handleResponse: @Sendable @escaping (GRPCCore.ClientResponse) async throws -> Result + ) async throws -> Result where Result: Sendable /// Documentation for MethodB - func methodB( + func methodB( request: GRPCCore.ClientRequest, serializer: some GRPCCore.MessageSerializer, deserializer: some GRPCCore.MessageDeserializer, options: GRPCCore.CallOptions, - _ body: @Sendable @escaping (GRPCCore.StreamingClientResponse) async throws -> R - ) async throws -> R where R: Sendable + onResponse handleResponse: @Sendable @escaping (GRPCCore.StreamingClientResponse) async throws -> Result + ) async throws -> Result where Result: Sendable } @available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *) extension NamespaceA_ServiceA.ClientProtocol { - package func methodA( + package func methodA( request: GRPCCore.StreamingClientRequest, options: GRPCCore.CallOptions = .defaults, - _ body: @Sendable @escaping (GRPCCore.ClientResponse) async throws -> R = { - try $0.message + onResponse handleResponse: @Sendable @escaping (GRPCCore.ClientResponse) async throws -> Result = { response in + try response.message } - ) async throws -> R where R: Sendable { + ) async throws -> Result where Result: Sendable { try await self.methodA( request: request, serializer: GRPCProtobuf.ProtobufSerializer(), deserializer: GRPCProtobuf.ProtobufDeserializer(), options: options, - body + onResponse: handleResponse ) } - package func methodB( + package func methodB( request: GRPCCore.ClientRequest, options: GRPCCore.CallOptions = .defaults, - _ body: @Sendable @escaping (GRPCCore.StreamingClientResponse) async throws -> R - ) async throws -> R where R: Sendable { + onResponse handleResponse: @Sendable @escaping (GRPCCore.StreamingClientResponse) async throws -> Result + ) async throws -> Result where Result: Sendable { try await self.methodB( request: request, serializer: GRPCProtobuf.ProtobufSerializer(), deserializer: GRPCProtobuf.ProtobufDeserializer(), options: options, - body + onResponse: handleResponse ) } } @@ -521,19 +521,19 @@ final class ClientCodeTranslatorSnippetBasedTests: XCTestCase { package func methodA( metadata: GRPCCore.Metadata = [:], options: GRPCCore.CallOptions = .defaults, - requestProducer: @Sendable @escaping (GRPCCore.RPCWriter) async throws -> Void, - onResponse handleResponse: @Sendable @escaping (GRPCCore.ClientResponse) async throws -> Result = { - try $0.message + requestProducer producer: @Sendable @escaping (GRPCCore.RPCWriter) async throws -> Void, + onResponse handleResponse: @Sendable @escaping (GRPCCore.ClientResponse) async throws -> Result = { response in + try response.message } ) async throws -> Result where Result: Sendable { let request = GRPCCore.StreamingClientRequest( metadata: metadata, - producer: requestProducer + producer: producer ) return try await self.methodA( request: request, options: options, - handleResponse + onResponse: handleResponse ) } @@ -551,7 +551,7 @@ final class ClientCodeTranslatorSnippetBasedTests: XCTestCase { return try await self.methodB( request: request, options: options, - handleResponse + onResponse: handleResponse ) } } @@ -565,40 +565,40 @@ final class ClientCodeTranslatorSnippetBasedTests: XCTestCase { } /// Documentation for MethodA - package func methodA( + package func methodA( request: GRPCCore.StreamingClientRequest, serializer: some GRPCCore.MessageSerializer, deserializer: some GRPCCore.MessageDeserializer, options: GRPCCore.CallOptions = .defaults, - _ body: @Sendable @escaping (GRPCCore.ClientResponse) async throws -> R = { - try $0.message + onResponse handleResponse: @Sendable @escaping (GRPCCore.ClientResponse) async throws -> Result = { response in + try response.message } - ) async throws -> R where R: Sendable { + ) async throws -> Result where Result: Sendable { try await self.client.clientStreaming( request: request, descriptor: NamespaceA_ServiceA.Method.MethodA.descriptor, serializer: serializer, deserializer: deserializer, options: options, - handler: body + onResponse: handleResponse ) } /// Documentation for MethodB - package func methodB( + package func methodB( request: GRPCCore.ClientRequest, serializer: some GRPCCore.MessageSerializer, deserializer: some GRPCCore.MessageDeserializer, options: GRPCCore.CallOptions = .defaults, - _ body: @Sendable @escaping (GRPCCore.StreamingClientResponse) async throws -> R - ) async throws -> R where R: Sendable { + onResponse handleResponse: @Sendable @escaping (GRPCCore.StreamingClientResponse) async throws -> Result + ) async throws -> Result where Result: Sendable { try await self.client.serverStreaming( request: request, descriptor: NamespaceA_ServiceA.Method.MethodB.descriptor, serializer: serializer, deserializer: deserializer, options: options, - handler: body + onResponse: handleResponse ) } } @@ -632,29 +632,29 @@ final class ClientCodeTranslatorSnippetBasedTests: XCTestCase { @available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *) internal protocol ServiceA_ClientProtocol: Sendable { /// Documentation for MethodA - func methodA( + func methodA( request: GRPCCore.ClientRequest, serializer: some GRPCCore.MessageSerializer, deserializer: some GRPCCore.MessageDeserializer, options: GRPCCore.CallOptions, - _ body: @Sendable @escaping (GRPCCore.ClientResponse) async throws -> R - ) async throws -> R where R: Sendable + onResponse handleResponse: @Sendable @escaping (GRPCCore.ClientResponse) async throws -> Result + ) async throws -> Result where Result: Sendable } @available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *) extension ServiceA.ClientProtocol { - internal func methodA( + internal func methodA( request: GRPCCore.ClientRequest, options: GRPCCore.CallOptions = .defaults, - _ body: @Sendable @escaping (GRPCCore.ClientResponse) async throws -> R = { - try $0.message + onResponse handleResponse: @Sendable @escaping (GRPCCore.ClientResponse) async throws -> Result = { response in + try response.message } - ) async throws -> R where R: Sendable { + ) async throws -> Result where Result: Sendable { try await self.methodA( request: request, serializer: GRPCProtobuf.ProtobufSerializer(), deserializer: GRPCProtobuf.ProtobufDeserializer(), options: options, - body + onResponse: handleResponse ) } } @@ -665,8 +665,8 @@ final class ClientCodeTranslatorSnippetBasedTests: XCTestCase { _ message: ServiceARequest, metadata: GRPCCore.Metadata = [:], options: GRPCCore.CallOptions = .defaults, - onResponse handleResponse: @Sendable @escaping (GRPCCore.ClientResponse) async throws -> Result = { - try $0.message + onResponse handleResponse: @Sendable @escaping (GRPCCore.ClientResponse) async throws -> Result = { response in + try response.message } ) async throws -> Result where Result: Sendable { let request = GRPCCore.ClientRequest( @@ -676,7 +676,7 @@ final class ClientCodeTranslatorSnippetBasedTests: XCTestCase { return try await self.methodA( request: request, options: options, - handleResponse + onResponse: handleResponse ) } } @@ -690,22 +690,22 @@ final class ClientCodeTranslatorSnippetBasedTests: XCTestCase { } /// Documentation for MethodA - internal func methodA( + internal func methodA( request: GRPCCore.ClientRequest, serializer: some GRPCCore.MessageSerializer, deserializer: some GRPCCore.MessageDeserializer, options: GRPCCore.CallOptions = .defaults, - _ body: @Sendable @escaping (GRPCCore.ClientResponse) async throws -> R = { - try $0.message + onResponse handleResponse: @Sendable @escaping (GRPCCore.ClientResponse) async throws -> Result = { response in + try response.message } - ) async throws -> R where R: Sendable { + ) async throws -> Result where Result: Sendable { try await self.client.unary( request: request, descriptor: ServiceA.Method.MethodA.descriptor, serializer: serializer, deserializer: deserializer, options: options, - handler: body + onResponse: handleResponse ) } } @@ -791,14 +791,19 @@ final class ClientCodeTranslatorSnippetBasedTests: XCTestCase { } private func assertClientCodeTranslation( - codeGenerationRequest: CodeGenerationRequest, + codeGenerationRequest request: CodeGenerationRequest, expectedSwift: String, accessLevel: SourceGenerator.Config.AccessLevel, file: StaticString = #filePath, line: UInt = #line ) throws { - let translator = ClientCodeTranslator(accessLevel: accessLevel) - let codeBlocks = try translator.translate(from: codeGenerationRequest) + let translator = ClientCodeTranslator() + let codeBlocks = translator.translate( + accessModifier: AccessModifier(accessLevel), + services: request.services, + serializer: request.lookupSerializer, + deserializer: request.lookupDeserializer + ) let renderer = TextBasedRenderer.default renderer.renderCodeBlocks(codeBlocks) let contents = renderer.renderedContents() diff --git a/Tests/GRPCCodeGenTests/Internal/Translator/ServerCodeTranslatorSnippetBasedTests.swift b/Tests/GRPCCodeGenTests/Internal/Translator/ServerCodeTranslatorSnippetBasedTests.swift index fde5448d9..a7a17c575 100644 --- a/Tests/GRPCCodeGenTests/Internal/Translator/ServerCodeTranslatorSnippetBasedTests.swift +++ b/Tests/GRPCCodeGenTests/Internal/Translator/ServerCodeTranslatorSnippetBasedTests.swift @@ -634,12 +634,17 @@ final class ServerCodeTranslatorSnippetBasedTests: XCTestCase { } private func assertServerCodeTranslation( - codeGenerationRequest: CodeGenerationRequest, + codeGenerationRequest request: CodeGenerationRequest, expectedSwift: String, accessLevel: SourceGenerator.Config.AccessLevel ) throws { - let translator = ServerCodeTranslator(accessLevel: accessLevel) - let codeBlocks = try translator.translate(from: codeGenerationRequest) + let translator = ServerCodeTranslator() + let codeBlocks = translator.translate( + accessModifier: AccessModifier(accessLevel), + services: request.services, + serializer: request.lookupSerializer, + deserializer: request.lookupDeserializer + ) let renderer = TextBasedRenderer.default renderer.renderCodeBlocks(codeBlocks) let contents = renderer.renderedContents() diff --git a/Tests/GRPCCodeGenTests/Internal/Translator/TypealiasTranslatorSnippetBasedTests.swift b/Tests/GRPCCodeGenTests/Internal/Translator/TypealiasTranslatorSnippetBasedTests.swift index a8294ef31..2c43f40c8 100644 --- a/Tests/GRPCCodeGenTests/Internal/Translator/TypealiasTranslatorSnippetBasedTests.swift +++ b/Tests/GRPCCodeGenTests/Internal/Translator/TypealiasTranslatorSnippetBasedTests.swift @@ -348,14 +348,19 @@ final class TypealiasTranslatorSnippetBasedTests: XCTestCase { extension TypealiasTranslatorSnippetBasedTests { private func assertTypealiasTranslation( - codeGenerationRequest: CodeGenerationRequest, + codeGenerationRequest request: CodeGenerationRequest, expectedSwift: String, client: Bool, server: Bool, accessLevel: SourceGenerator.Config.AccessLevel ) throws { - let translator = TypealiasTranslator(client: client, server: server, accessLevel: accessLevel) - let codeBlocks = try translator.translate(from: codeGenerationRequest) + let translator = MetadataTranslator() + let codeBlocks = translator.translate( + accessModifier: AccessModifier(accessLevel), + services: request.services, + client: client, + server: server + ) let renderer = TextBasedRenderer.default renderer.renderCodeBlocks(codeBlocks) let contents = renderer.renderedContents()