From dfc41f27a4a2c6181aea2d83ecabf8bc697459d9 Mon Sep 17 00:00:00 2001 From: George Barnett Date: Wed, 26 Feb 2025 08:25:20 +0000 Subject: [PATCH 1/3] Add code gen config for the module name Modifications: - Add config to the code generator allowing a different module name to be specified. - Remove unused code Result: The grpc module name can be specified in generated code --- Sources/GRPCCodeGen/CodeGenerator.swift | 6 +- .../IDLToStructuredSwiftTranslator.swift | 13 ++-- .../Translator/SpecializedTranslator.swift | 59 ------------------- .../Internal/Translator/Translator.swift | 37 ------------ .../StructuredSwift+ImportTests.swift | 26 +++++++- ...uredSwiftTranslatorSnippetBasedTests.swift | 27 ++++++--- 6 files changed, 54 insertions(+), 114 deletions(-) delete mode 100644 Sources/GRPCCodeGen/Internal/Translator/SpecializedTranslator.swift delete mode 100644 Sources/GRPCCodeGen/Internal/Translator/Translator.swift diff --git a/Sources/GRPCCodeGen/CodeGenerator.swift b/Sources/GRPCCodeGen/CodeGenerator.swift index adac1a6c9..25d17a6cf 100644 --- a/Sources/GRPCCodeGen/CodeGenerator.swift +++ b/Sources/GRPCCodeGen/CodeGenerator.swift @@ -40,6 +40,8 @@ public struct CodeGenerator: Sendable { public var client: Bool /// Whether or not server code should be generated. public var server: Bool + /// The name of the core gRPC module. + public var grpcCoreModuleName: String /// Creates a new configuration. /// @@ -61,6 +63,7 @@ public struct CodeGenerator: Sendable { self.indentation = indentation self.client = client self.server = server + self.grpcCoreModuleName = "GRPCCore" } /// The possible access levels for the generated code. @@ -96,7 +99,8 @@ public struct CodeGenerator: Sendable { accessLevel: self.config.accessLevel, accessLevelOnImports: self.config.accessLevelOnImports, client: self.config.client, - server: self.config.server + server: self.config.server, + grpcCoreModuleName: self.config.grpcCoreModuleName ) let sourceFile = try textRenderer.render(structured: structuredSwiftRepresentation) diff --git a/Sources/GRPCCodeGen/Internal/Translator/IDLToStructuredSwiftTranslator.swift b/Sources/GRPCCodeGen/Internal/Translator/IDLToStructuredSwiftTranslator.swift index 2d3a7746e..29366bd3d 100644 --- a/Sources/GRPCCodeGen/Internal/Translator/IDLToStructuredSwiftTranslator.swift +++ b/Sources/GRPCCodeGen/Internal/Translator/IDLToStructuredSwiftTranslator.swift @@ -17,7 +17,7 @@ /// Creates a representation for the server and client code, as well as for the enums containing useful type aliases and properties. /// The representation is generated based on the ``CodeGenerationRequest`` object and user specifications, /// using types from ``StructuredSwiftRepresentation``. -package struct IDLToStructuredSwiftTranslator: Translator { +package struct IDLToStructuredSwiftTranslator { package init() {} func translate( @@ -25,7 +25,8 @@ package struct IDLToStructuredSwiftTranslator: Translator { accessLevel: CodeGenerator.Config.AccessLevel, accessLevelOnImports: Bool, client: Bool, - server: Bool + server: Bool, + grpcCoreModuleName: String ) throws -> StructuredSwiftRepresentation { try self.validateInput(codeGenerationRequest) let accessModifier = AccessModifier(accessLevel) @@ -84,7 +85,8 @@ package struct IDLToStructuredSwiftTranslator: Translator { imports = try self.makeImports( dependencies: codeGenerationRequest.dependencies, accessLevel: accessLevel, - accessLevelOnImports: accessLevelOnImports + accessLevelOnImports: accessLevelOnImports, + grpcCoreModuleName: grpcCoreModuleName ) } @@ -102,13 +104,14 @@ package struct IDLToStructuredSwiftTranslator: Translator { package func makeImports( dependencies: [Dependency], accessLevel: CodeGenerator.Config.AccessLevel, - accessLevelOnImports: Bool + accessLevelOnImports: Bool, + grpcCoreModuleName: String ) throws -> [ImportDescription] { var imports: [ImportDescription] = [] imports.append( ImportDescription( accessLevel: accessLevelOnImports ? AccessModifier(accessLevel) : nil, - moduleName: "GRPCCore" + moduleName: grpcCoreModuleName ) ) diff --git a/Sources/GRPCCodeGen/Internal/Translator/SpecializedTranslator.swift b/Sources/GRPCCodeGen/Internal/Translator/SpecializedTranslator.swift deleted file mode 100644 index 1ac3c4bee..000000000 --- a/Sources/GRPCCodeGen/Internal/Translator/SpecializedTranslator.swift +++ /dev/null @@ -1,59 +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. - */ - -/// Represents one responsibility of the ``Translator``: either the type aliases translation, -/// the server code translation or the client code translation. -protocol SpecializedTranslator { - - /// The ``SourceGenerator.Config.AccessLevel`` object used to represent the visibility level used in the generated code. - var accessLevel: CodeGenerator.Config.AccessLevel { get } - - /// Generates an array of ``CodeBlock`` elements that will be part of the ``StructuredSwiftRepresentation`` object - /// created by the ``Translator``. - /// - /// - Parameters: - /// - codeGenerationRequest: The ``CodeGenerationRequest`` object used to represent a Source IDL description of RPCs. - /// - Returns: An array of ``CodeBlock`` elements. - /// - /// - SeeAlso: ``CodeGenerationRequest``, ``Translator``, ``CodeBlock``. - func translate(from codeGenerationRequest: CodeGenerationRequest) throws -> [CodeBlock] -} - -extension SpecializedTranslator { - /// The access modifier that corresponds with the access level from ``SourceGenerator.Configuration``. - internal var accessModifier: AccessModifier { - get { - switch accessLevel.level { - case .internal: - return AccessModifier.internal - case .package: - return AccessModifier.package - case .public: - return AccessModifier.public - } - } - } - - internal var availabilityGuard: AvailabilityDescription { - AvailabilityDescription(osVersions: [ - .init(os: .macOS, version: "15.0"), - .init(os: .iOS, version: "18.0"), - .init(os: .watchOS, version: "11.0"), - .init(os: .tvOS, version: "18.0"), - .init(os: .visionOS, version: "2.0"), - ]) - } -} diff --git a/Sources/GRPCCodeGen/Internal/Translator/Translator.swift b/Sources/GRPCCodeGen/Internal/Translator/Translator.swift deleted file mode 100644 index 9d0a043b0..000000000 --- a/Sources/GRPCCodeGen/Internal/Translator/Translator.swift +++ /dev/null @@ -1,37 +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. - */ - -/// Transforms ``CodeGenerationRequest`` objects into ``StructuredSwiftRepresentation`` objects. -/// -/// It represents the first step of the code generation process for IDL described RPCs. -protocol Translator { - /// Translates the provided ``CodeGenerationRequest`` object, into Swift code representation. - /// - Parameters: - /// - codeGenerationRequest: The IDL described RPCs representation. - /// - accessLevel: The access level that will restrict the protocols, extensions and methods in the generated code. - /// - accessLevelOnImports: Whether access levels should be included on imports. - /// - client: Whether or not client code should be generated from the IDL described RPCs representation. - /// - server: Whether or not server code should be generated from the IDL described RPCs representation. - /// - Returns: A structured Swift representation of the generated code. - /// - Throws: An error if there are issues translating the codeGenerationRequest. - func translate( - codeGenerationRequest: CodeGenerationRequest, - accessLevel: CodeGenerator.Config.AccessLevel, - accessLevelOnImports: Bool, - client: Bool, - server: Bool - ) throws -> StructuredSwiftRepresentation -} diff --git a/Tests/GRPCCodeGenTests/Internal/StructuredSwift+ImportTests.swift b/Tests/GRPCCodeGenTests/Internal/StructuredSwift+ImportTests.swift index c079d5f5c..88a6bc075 100644 --- a/Tests/GRPCCodeGenTests/Internal/StructuredSwift+ImportTests.swift +++ b/Tests/GRPCCodeGenTests/Internal/StructuredSwift+ImportTests.swift @@ -107,7 +107,8 @@ extension StructuredSwiftTests { let imports = try StructuredSwiftTests.Import.translator.makeImports( dependencies: dependencies, accessLevel: accessLevel, - accessLevelOnImports: true + accessLevelOnImports: true, + grpcCoreModuleName: "GRPCCore" ) #expect(render(imports) == expected) @@ -157,7 +158,8 @@ extension StructuredSwiftTests { let imports = try StructuredSwiftTests.Import.translator.makeImports( dependencies: dependencies, accessLevel: accessLevel, - accessLevelOnImports: true + accessLevelOnImports: true, + grpcCoreModuleName: "GRPCCore" ) #expect(render(imports) == expected) @@ -191,11 +193,29 @@ extension StructuredSwiftTests { let imports = try StructuredSwiftTests.Import.translator.makeImports( dependencies: dependencies, accessLevel: accessLevel, - accessLevelOnImports: true + accessLevelOnImports: true, + grpcCoreModuleName: "GRPCCore" ) #expect(render(imports) == expected) } + @Test("gRPC module name") + func grpcModuleName() throws { + let translator = IDLToStructuredSwiftTranslator() + let imports = try translator.makeImports( + dependencies: [], + accessLevel: .public, + accessLevelOnImports: true, + grpcCoreModuleName: "GRPCCoreFoo" + ) + + let expected = + """ + public import GRPCCoreFoo + """ + + #expect(render(imports) == expected) + } } } diff --git a/Tests/GRPCCodeGenTests/Internal/Translator/IDLToStructuredSwiftTranslatorSnippetBasedTests.swift b/Tests/GRPCCodeGenTests/Internal/Translator/IDLToStructuredSwiftTranslatorSnippetBasedTests.swift index 1848df575..5be986dab 100644 --- a/Tests/GRPCCodeGenTests/Internal/Translator/IDLToStructuredSwiftTranslatorSnippetBasedTests.swift +++ b/Tests/GRPCCodeGenTests/Internal/Translator/IDLToStructuredSwiftTranslatorSnippetBasedTests.swift @@ -169,7 +169,8 @@ final class IDLToStructuredSwiftTranslatorSnippetBasedTests: XCTestCase { accessLevel: accessLevel, accessLevelOnImports: true, client: false, - server: server + server: server, + grpcCoreModuleName: "GRPCCore" ) let renderer = TextBasedRenderer.default let sourceFile = try renderer.render(structured: structuredSwift) @@ -197,7 +198,8 @@ final class IDLToStructuredSwiftTranslatorSnippetBasedTests: XCTestCase { accessLevel: .public, accessLevelOnImports: true, client: true, - server: true + server: true, + grpcCoreModuleName: "GRPCCore" ) ) { error in @@ -244,7 +246,8 @@ final class IDLToStructuredSwiftTranslatorSnippetBasedTests: XCTestCase { accessLevel: .public, accessLevelOnImports: true, client: true, - server: true + server: true, + grpcCoreModuleName: "GRPCCore" ) ) { error in @@ -279,7 +282,8 @@ final class IDLToStructuredSwiftTranslatorSnippetBasedTests: XCTestCase { accessLevel: .public, accessLevelOnImports: true, client: true, - server: true + server: true, + grpcCoreModuleName: "GRPCCore" ) ) { error in @@ -325,7 +329,8 @@ final class IDLToStructuredSwiftTranslatorSnippetBasedTests: XCTestCase { accessLevel: .internal, accessLevelOnImports: true, client: true, - server: true + server: true, + grpcCoreModuleName: "GRPCCore" ) ) { error in XCTAssertEqual( @@ -369,7 +374,8 @@ final class IDLToStructuredSwiftTranslatorSnippetBasedTests: XCTestCase { accessLevel: .public, accessLevelOnImports: true, client: true, - server: true + server: true, + grpcCoreModuleName: "GRPCCore" ) ) { error in XCTAssertEqual( @@ -429,7 +435,8 @@ final class IDLToStructuredSwiftTranslatorSnippetBasedTests: XCTestCase { accessLevel: .public, accessLevelOnImports: true, client: true, - server: true + server: true, + grpcCoreModuleName: "GRPCCore" ) ) { error in XCTAssertEqual( @@ -482,7 +489,8 @@ final class IDLToStructuredSwiftTranslatorSnippetBasedTests: XCTestCase { accessLevel: .public, accessLevelOnImports: true, client: true, - server: true + server: true, + grpcCoreModuleName: "GRPCCore" ) ) { error in @@ -528,7 +536,8 @@ final class IDLToStructuredSwiftTranslatorSnippetBasedTests: XCTestCase { accessLevel: .public, accessLevelOnImports: true, client: true, - server: true + server: true, + grpcCoreModuleName: "GRPCCore" ) ) { error in From b503984db5515dd267f08820ad534305e6f11544 Mon Sep 17 00:00:00 2001 From: George Barnett Date: Wed, 26 Feb 2025 10:35:16 +0000 Subject: [PATCH 2/3] add namer --- Sources/GRPCCodeGen/Internal/Namer.swift | 118 ++++++++++++++++++ .../Internal/StructuredSwift+Client.swift | 95 ++++++++------ .../Internal/StructuredSwift+Server.swift | 82 +++++++----- .../StructuredSwift+ServiceMetadata.swift | 66 ++++++---- .../Internal/StructuredSwift+Types.swift | 91 -------------- .../Translator/ClientCodeTranslator.swift | 20 ++- .../IDLToStructuredSwiftTranslator.swift | 7 +- .../Translator/MetadataTranslator.swift | 5 +- .../Translator/ServerCodeTranslator.swift | 17 ++- ...uredSwiftTranslatorSnippetBasedTests.swift | 82 +++++++++++- 10 files changed, 388 insertions(+), 195 deletions(-) create mode 100644 Sources/GRPCCodeGen/Internal/Namer.swift delete mode 100644 Sources/GRPCCodeGen/Internal/StructuredSwift+Types.swift diff --git a/Sources/GRPCCodeGen/Internal/Namer.swift b/Sources/GRPCCodeGen/Internal/Namer.swift new file mode 100644 index 000000000..f5c7db422 --- /dev/null +++ b/Sources/GRPCCodeGen/Internal/Namer.swift @@ -0,0 +1,118 @@ +/* + * 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. + */ + +package struct Namer: Sendable, Hashable { + let grpcCore: String + + package init(grpcCore: String = "GRPCCore") { + self.grpcCore = grpcCore + } + + private func grpcCore(_ typeName: String) -> ExistingTypeDescription { + return .member([self.grpcCore, typeName]) + } + + private func requestResponse( + for type: String?, + isRequest: Bool, + isStreaming: Bool, + isClient: Bool + ) -> ExistingTypeDescription { + let prefix = isStreaming ? "Streaming" : "" + let peer = isClient ? "Client" : "Server" + let kind = isRequest ? "Request" : "Response" + let baseType = self.grpcCore(prefix + peer + kind) + + if let type = type { + return .generic(wrapper: baseType, wrapped: .member(type)) + } else { + return baseType + } + } + + func literalNamespacedType(_ type: String) -> String { + return self.grpcCore + "." + type + } + + func serverRequest(forType type: String?, streaming: Bool) -> ExistingTypeDescription { + return self.requestResponse(for: type, isRequest: true, isStreaming: streaming, isClient: false) + } + + func serverResponse(forType type: String?, streaming: Bool) -> ExistingTypeDescription { + return self.requestResponse( + for: type, + isRequest: false, + isStreaming: streaming, + isClient: false + ) + } + + func clientRequest(forType type: String?, streaming: Bool) -> ExistingTypeDescription { + return self.requestResponse(for: type, isRequest: true, isStreaming: streaming, isClient: true) + } + + func clientResponse(forType type: String?, streaming: Bool) -> ExistingTypeDescription { + return self.requestResponse(for: type, isRequest: false, isStreaming: streaming, isClient: true) + } + + var serverContext: ExistingTypeDescription { + self.grpcCore("ServerContext") + } + + func rpcRouter(genericOver type: String) -> ExistingTypeDescription { + .generic(wrapper: self.grpcCore("RPCRouter"), wrapped: .member(type)) + } + + var serviceDescriptor: ExistingTypeDescription { + self.grpcCore("ServiceDescriptor") + } + + var methodDescriptor: ExistingTypeDescription { + self.grpcCore("MethodDescriptor") + } + + func serializer(forType type: String) -> ExistingTypeDescription { + .generic(wrapper: self.grpcCore("MessageSerializer"), wrapped: .member(type)) + } + + func deserializer(forType type: String) -> ExistingTypeDescription { + .generic(wrapper: self.grpcCore("MessageDeserializer"), wrapped: .member(type)) + } + + func rpcWriter(forType type: String) -> ExistingTypeDescription { + .generic(wrapper: self.grpcCore("RPCWriter"), wrapped: .member(type)) + } + + func rpcAsyncSequence(forType type: String) -> ExistingTypeDescription { + .generic( + wrapper: self.grpcCore("RPCAsyncSequence"), + wrapped: .member(type), + .any(.member(["Swift", "Error"])) + ) + } + + var callOptions: ExistingTypeDescription { + self.grpcCore("CallOptions") + } + + var metadata: ExistingTypeDescription { + self.grpcCore("Metadata") + } + + func grpcClient(genericOver transport: String) -> ExistingTypeDescription { + .generic(wrapper: self.grpcCore("GRPCClient"), wrapped: [.member(transport)]) + } +} diff --git a/Sources/GRPCCodeGen/Internal/StructuredSwift+Client.swift b/Sources/GRPCCodeGen/Internal/StructuredSwift+Client.swift index 511724702..5054c557a 100644 --- a/Sources/GRPCCodeGen/Internal/StructuredSwift+Client.swift +++ b/Sources/GRPCCodeGen/Internal/StructuredSwift+Client.swift @@ -46,7 +46,8 @@ extension FunctionSignatureDescription { streamingInput: Bool, streamingOutput: Bool, includeDefaults: Bool, - includeSerializers: Bool + includeSerializers: Bool, + namer: Namer = Namer() ) -> Self { var signature = FunctionSignatureDescription( accessModifier: accessLevel, @@ -61,7 +62,7 @@ extension FunctionSignatureDescription { signature.parameters.append( ParameterDescription( label: "request", - type: .clientRequest(forType: input, streaming: streamingInput) + type: namer.clientRequest(forType: input, streaming: streamingInput) ) ) @@ -70,14 +71,14 @@ extension FunctionSignatureDescription { ParameterDescription( label: "serializer", // Type is optional, so be explicit about which 'some' to use - type: ExistingTypeDescription.some(.serializer(forType: input)) + type: ExistingTypeDescription.some(namer.serializer(forType: input)) ) ) signature.parameters.append( ParameterDescription( label: "deserializer", // Type is optional, so be explicit about which 'some' to use - type: ExistingTypeDescription.some(.deserializer(forType: output)) + type: ExistingTypeDescription.some(namer.deserializer(forType: output)) ) ) } @@ -85,7 +86,7 @@ extension FunctionSignatureDescription { signature.parameters.append( ParameterDescription( label: "options", - type: .callOptions, + type: namer.callOptions, defaultValue: includeDefaults ? .memberAccess(.dot("defaults")) : nil ) ) @@ -98,7 +99,7 @@ extension FunctionSignatureDescription { ClosureSignatureDescription( parameters: [ ParameterDescription( - type: .clientResponse(forType: output, streaming: streamingOutput) + type: namer.clientResponse(forType: output, streaming: streamingOutput) ) ], keywords: [.async, .throws], @@ -141,7 +142,8 @@ extension FunctionDescription { streamingInput: Bool, streamingOutput: Bool, serializer: Expression, - deserializer: Expression + deserializer: Expression, + namer: Namer = Namer() ) -> Self { FunctionDescription( signature: .clientMethod( @@ -152,7 +154,8 @@ extension FunctionDescription { streamingInput: streamingInput, streamingOutput: streamingOutput, includeDefaults: true, - includeSerializers: false + includeSerializers: false, + namer: namer ), body: [ .expression( @@ -202,7 +205,8 @@ extension ProtocolDescription { static func clientProtocol( accessLevel: AccessModifier? = nil, name: String, - methods: [MethodDescriptor] + methods: [MethodDescriptor], + namer: Namer = Namer() ) -> Self { ProtocolDescription( accessModifier: accessLevel, @@ -219,7 +223,8 @@ extension ProtocolDescription { streamingInput: method.isInputStreaming, streamingOutput: method.isOutputStreaming, includeDefaults: false, - includeSerializers: true + includeSerializers: true, + namer: namer ) ) ) @@ -245,6 +250,7 @@ extension ExtensionDescription { accessLevel: AccessModifier? = nil, name: String, methods: [MethodDescriptor], + namer: Namer = Namer(), serializer: (String) -> String, deserializer: (String) -> String ) -> Self { @@ -262,7 +268,8 @@ extension ExtensionDescription { streamingInput: method.isInputStreaming, streamingOutput: method.isOutputStreaming, serializer: .identifierPattern(serializer(method.inputType)), - deserializer: .identifierPattern(deserializer(method.outputType)) + deserializer: .identifierPattern(deserializer(method.outputType)), + namer: namer ) ) ) @@ -288,7 +295,8 @@ extension FunctionSignatureDescription { input: String, output: String, streamingInput: Bool, - streamingOutput: Bool + streamingOutput: Bool, + namer: Namer = Namer() ) -> Self { var signature = FunctionSignatureDescription( accessModifier: accessLevel, @@ -310,7 +318,7 @@ extension FunctionSignatureDescription { signature.parameters.append( ParameterDescription( label: "metadata", - type: .metadata, + type: namer.metadata, defaultValue: .literal(.dictionary([])) ) ) @@ -319,7 +327,7 @@ extension FunctionSignatureDescription { signature.parameters.append( ParameterDescription( label: "options", - type: .callOptions, + type: namer.callOptions, defaultValue: .dot("defaults") ) ) @@ -331,7 +339,7 @@ extension FunctionSignatureDescription { name: "producer", type: .closure( ClosureSignatureDescription( - parameters: [ParameterDescription(type: .rpcWriter(forType: input))], + parameters: [ParameterDescription(type: namer.rpcWriter(forType: input))], keywords: [.async, .throws], returnType: .identifierPattern("Void"), sendable: true, @@ -350,7 +358,7 @@ extension FunctionSignatureDescription { ClosureSignatureDescription( parameters: [ ParameterDescription( - type: .clientResponse(forType: output, streaming: streamingOutput) + type: namer.clientResponse(forType: output, streaming: streamingOutput) ) ], keywords: [.async, .throws], @@ -382,7 +390,8 @@ extension [CodeBlock] { static func clientMethodExploded( name: String, input: String, - streamingInput: Bool + streamingInput: Bool, + namer: Namer = Namer() ) -> Self { func arguments(streaming: Bool) -> [FunctionArgumentDescription] { let metadata = FunctionArgumentDescription( @@ -414,7 +423,7 @@ extension [CodeBlock] { left: .identifierPattern("request"), right: .functionCall( calledExpression: .identifierType( - .clientRequest(forType: input, streaming: streamingInput) + namer.clientRequest(forType: input, streaming: streamingInput) ), arguments: arguments(streaming: streamingInput) ) @@ -471,7 +480,8 @@ extension FunctionDescription { input: String, output: String, streamingInput: Bool, - streamingOutput: Bool + streamingOutput: Bool, + namer: Namer = Namer() ) -> Self { FunctionDescription( signature: .clientMethodExploded( @@ -480,9 +490,15 @@ extension FunctionDescription { input: input, output: output, streamingInput: streamingInput, - streamingOutput: streamingOutput + streamingOutput: streamingOutput, + namer: namer ), - body: .clientMethodExploded(name: name, input: input, streamingInput: streamingInput) + body: .clientMethodExploded( + name: name, + input: input, + streamingInput: streamingInput, + namer: namer + ) ) } } @@ -496,7 +512,8 @@ extension ExtensionDescription { static func explodedClientMethods( accessLevel: AccessModifier? = nil, on extensionName: String, - methods: [MethodDescriptor] + methods: [MethodDescriptor], + namer: Namer = Namer() ) -> ExtensionDescription { return ExtensionDescription( onType: extensionName, @@ -510,7 +527,8 @@ extension ExtensionDescription { input: method.inputType, output: method.outputType, streamingInput: method.isInputStreaming, - streamingOutput: method.isOutputStreaming + streamingOutput: method.isOutputStreaming, + namer: namer ) ) ) @@ -539,7 +557,8 @@ extension FunctionDescription { serviceEnum: String, methodEnum: String, streamingInput: Bool, - streamingOutput: Bool + streamingOutput: Bool, + namer: Namer = Namer() ) -> Self { let underlyingMethod: String switch (streamingInput, streamingOutput) { @@ -560,21 +579,21 @@ extension FunctionDescription { parameters: [ ParameterDescription( label: "request", - type: .clientRequest(forType: input, streaming: streamingInput) + type: namer.clientRequest(forType: input, streaming: streamingInput) ), ParameterDescription( label: "serializer", // Be explicit: 'type' is optional and '.some' resolves to Optional.some by default. - type: ExistingTypeDescription.some(.serializer(forType: input)) + type: ExistingTypeDescription.some(namer.serializer(forType: input)) ), ParameterDescription( label: "deserializer", // Be explicit: 'type' is optional and '.some' resolves to Optional.some by default. - type: ExistingTypeDescription.some(.deserializer(forType: output)) + type: ExistingTypeDescription.some(namer.deserializer(forType: output)) ), ParameterDescription( label: "options", - type: .callOptions, + type: namer.callOptions, defaultValue: .dot("defaults") ), ParameterDescription( @@ -584,7 +603,7 @@ extension FunctionDescription { ClosureSignatureDescription( parameters: [ ParameterDescription( - type: .clientResponse(forType: output, streaming: streamingOutput) + type: namer.clientResponse(forType: output, streaming: streamingOutput) ) ], keywords: [.async, .throws], @@ -660,30 +679,31 @@ extension StructDescription { name: String, serviceEnum: String, clientProtocol: String, - methods: [MethodDescriptor] + methods: [MethodDescriptor], + namer: Namer = Namer() ) -> Self { - StructDescription( + return StructDescription( accessModifier: accessLevel, name: name, generics: [.member("Transport")], conformances: [clientProtocol], whereClause: WhereClause( - requirements: [.conformance("Transport", "GRPCCore.ClientTransport")] + requirements: [.conformance("Transport", namer.literalNamespacedType("ClientTransport"))] ), members: [ .variable( accessModifier: .private, kind: .let, left: "client", - type: .grpcClient(genericOver: "Transport") + type: namer.grpcClient(genericOver: "Transport") ), .commentable( .preFormatted( """ - /// Creates a new client wrapping the provided `GRPCCore.GRPCClient`. + /// Creates a new client wrapping the provided `\(namer.literalNamespacedType("GRPCClient"))`. /// /// - Parameters: - /// - client: A `GRPCCore.GRPCClient` providing a communication channel to the service. + /// - client: A `\(namer.literalNamespacedType("GRPCClient"))` providing a communication channel to the service. """ ), .function( @@ -693,7 +713,7 @@ extension StructDescription { ParameterDescription( label: "wrapping", name: "client", - type: .grpcClient( + type: namer.grpcClient( genericOver: "Transport" ) ) @@ -722,7 +742,8 @@ extension StructDescription { serviceEnum: serviceEnum, methodEnum: method.name.typeName, streamingInput: method.isInputStreaming, - streamingOutput: method.isOutputStreaming + streamingOutput: method.isOutputStreaming, + namer: namer ) ) ) diff --git a/Sources/GRPCCodeGen/Internal/StructuredSwift+Server.swift b/Sources/GRPCCodeGen/Internal/StructuredSwift+Server.swift index a3525c13e..7da41d2d9 100644 --- a/Sources/GRPCCodeGen/Internal/StructuredSwift+Server.swift +++ b/Sources/GRPCCodeGen/Internal/StructuredSwift+Server.swift @@ -27,7 +27,8 @@ extension FunctionSignatureDescription { input: String, output: String, streamingInput: Bool, - streamingOutput: Bool + streamingOutput: Bool, + namer: Namer = Namer() ) -> Self { return FunctionSignatureDescription( accessModifier: accessLevel, @@ -35,12 +36,12 @@ extension FunctionSignatureDescription { parameters: [ ParameterDescription( label: "request", - type: .serverRequest(forType: input, streaming: streamingInput) + type: namer.serverRequest(forType: input, streaming: streamingInput) ), - ParameterDescription(label: "context", type: .serverContext), + ParameterDescription(label: "context", type: namer.serverContext), ], keywords: [.async, .throws], - returnType: .identifierType(.serverResponse(forType: output, streaming: streamingOutput)) + returnType: .identifierType(namer.serverResponse(forType: output, streaming: streamingOutput)) ) } } @@ -54,7 +55,8 @@ extension ProtocolDescription { static func streamingService( accessLevel: AccessModifier? = nil, name: String, - methods: [MethodDescriptor] + methods: [MethodDescriptor], + namer: Namer = Namer() ) -> Self { func docs(for method: MethodDescriptor) -> String { let summary = """ @@ -77,7 +79,7 @@ extension ProtocolDescription { return ProtocolDescription( accessModifier: accessLevel, name: name, - conformances: ["GRPCCore.RegistrableRPCService"], + conformances: [namer.literalNamespacedType("RegistrableRPCService")], members: methods.map { method in .commentable( .preFormatted(docs(for: method)), @@ -87,7 +89,8 @@ extension ProtocolDescription { input: method.inputType, output: method.outputType, streamingInput: true, - streamingOutput: true + streamingOutput: true, + namer: namer ) ) ) @@ -109,6 +112,7 @@ extension ExtensionDescription { on extensionName: String, serviceNamespace: String, methods: [MethodDescriptor], + namer: Namer = Namer(), serializer: (String) -> String, deserializer: (String) -> String ) -> Self { @@ -120,6 +124,7 @@ extension ExtensionDescription { accessLevel: accessLevel, serviceNamespace: serviceNamespace, methods: methods, + namer: namer, serializer: serializer, deserializer: deserializer ) @@ -139,7 +144,8 @@ extension ProtocolDescription { accessLevel: AccessModifier? = nil, name: String, streamingProtocol: String, - methods: [MethodDescriptor] + methods: [MethodDescriptor], + namer: Namer = Namer() ) -> Self { func docs(for method: MethodDescriptor) -> String { let summary = """ @@ -186,7 +192,8 @@ extension ProtocolDescription { input: method.inputType, output: method.outputType, streamingInput: method.isInputStreaming, - streamingOutput: method.isOutputStreaming + streamingOutput: method.isOutputStreaming, + namer: namer ) ) ) @@ -305,6 +312,7 @@ extension FunctionDescription { accessLevel: AccessModifier? = nil, serviceNamespace: String, methods: [MethodDescriptor], + namer: Namer = Namer(), serializer: (String) -> String, deserializer: (String) -> String ) -> Self { @@ -316,13 +324,13 @@ extension FunctionDescription { ParameterDescription( label: "with", name: "router", - type: .rpcRouter(genericOver: "Transport"), + type: namer.rpcRouter(genericOver: "Transport"), `inout`: true ) ], whereClause: WhereClause( requirements: [ - .conformance("Transport", "GRPCCore.ServerTransport") + .conformance("Transport", namer.literalNamespacedType("ServerTransport")) ] ), body: methods.map { method in @@ -359,7 +367,8 @@ extension FunctionDescription { input: String, output: String, streamingInput: Bool, - streamingOutput: Bool + streamingOutput: Bool, + namer: Namer = Namer() ) -> FunctionDescription { let signature: FunctionSignatureDescription = .serverMethod( accessLevel: accessLevel, @@ -368,7 +377,8 @@ extension FunctionDescription { output: output, // This method converts from the fully streamed version to the specified version. streamingInput: true, - streamingOutput: true + streamingOutput: true, + namer: namer ) // Call the underlying function. @@ -385,7 +395,9 @@ extension FunctionDescription { expression: streamingInput ? .identifierPattern("request") : .functionCall( - calledExpression: .identifierType(.serverRequest(forType: nil, streaming: false)), + calledExpression: .identifierType( + namer.serverRequest(forType: nil, streaming: false) + ), arguments: [ FunctionArgumentDescription( label: "stream", @@ -420,7 +432,7 @@ extension FunctionDescription { expression: streamingOutput ? .identifierPattern("response") : .functionCall( - calledExpression: .identifierType(.serverResponse(forType: nil, streaming: true)), + calledExpression: .identifierType(namer.serverResponse(forType: nil, streaming: true)), arguments: [ FunctionArgumentDescription( label: "single", @@ -456,7 +468,8 @@ extension ExtensionDescription { static func streamingServiceProtocolDefaultImplementation( accessModifier: AccessModifier? = nil, on extensionName: String, - methods: [MethodDescriptor] + methods: [MethodDescriptor], + namer: Namer = Namer() ) -> Self { return ExtensionDescription( onType: extensionName, @@ -472,7 +485,8 @@ extension ExtensionDescription { input: method.inputType, output: method.outputType, streamingInput: method.isInputStreaming, - streamingOutput: method.isOutputStreaming + streamingOutput: method.isOutputStreaming, + namer: namer ) ) } @@ -501,20 +515,26 @@ extension FunctionSignatureDescription { input: String, output: String, streamingInput: Bool, - streamingOutput: Bool + streamingOutput: Bool, + namer: Namer = Namer() ) -> Self { var parameters: [ParameterDescription] = [ ParameterDescription( label: "request", - type: streamingInput ? .rpcAsyncSequence(forType: input) : .member(input) + type: streamingInput ? namer.rpcAsyncSequence(forType: input) : .member(input) ) ] if streamingOutput { - parameters.append(ParameterDescription(label: "response", type: .rpcWriter(forType: output))) + parameters.append( + ParameterDescription( + label: "response", + type: namer.rpcWriter(forType: output) + ) + ) } - parameters.append(ParameterDescription(label: "context", type: .serverContext)) + parameters.append(ParameterDescription(label: "context", type: namer.serverContext)) return FunctionSignatureDescription( accessModifier: accessLevel, @@ -536,7 +556,8 @@ extension ProtocolDescription { accessModifier: AccessModifier? = nil, name: String, serviceProtocol: String, - methods: [MethodDescriptor] + methods: [MethodDescriptor], + namer: Namer = Namer() ) -> Self { func docs(for method: MethodDescriptor) -> String { let summary = """ @@ -591,7 +612,8 @@ extension ProtocolDescription { input: method.inputType, output: method.outputType, streamingInput: method.isInputStreaming, - streamingOutput: method.isOutputStreaming + streamingOutput: method.isOutputStreaming, + namer: namer ) ) ) @@ -666,7 +688,8 @@ extension FunctionDescription { input: String, output: String, streamingInput: Bool, - streamingOutput: Bool + streamingOutput: Bool, + namer: Namer = Namer() ) -> Self { func makeUnaryOutputArguments() -> [FunctionArgumentDescription] { return [ @@ -719,14 +742,15 @@ extension FunctionDescription { input: input, output: output, streamingInput: streamingInput, - streamingOutput: streamingOutput + streamingOutput: streamingOutput, + namer: namer ), body: [ .expression( .functionCall( calledExpression: .return( .identifierType( - .serverResponse(forType: output, streaming: streamingOutput) + namer.serverResponse(forType: output, streaming: streamingOutput) ) ), arguments: streamingOutput ? makeStreamingOutputArguments() : makeUnaryOutputArguments() @@ -746,7 +770,8 @@ extension ExtensionDescription { static func serviceProtocolDefaultImplementation( accessModifier: AccessModifier? = nil, on extensionName: String, - methods: [MethodDescriptor] + methods: [MethodDescriptor], + namer: Namer = Namer() ) -> Self { ExtensionDescription( onType: extensionName, @@ -758,7 +783,8 @@ extension ExtensionDescription { input: method.inputType, output: method.outputType, streamingInput: method.isInputStreaming, - streamingOutput: method.isOutputStreaming + streamingOutput: method.isOutputStreaming, + namer: namer ) ) } diff --git a/Sources/GRPCCodeGen/Internal/StructuredSwift+ServiceMetadata.swift b/Sources/GRPCCodeGen/Internal/StructuredSwift+ServiceMetadata.swift index 18502832a..dd51eb496 100644 --- a/Sources/GRPCCodeGen/Internal/StructuredSwift+ServiceMetadata.swift +++ b/Sources/GRPCCodeGen/Internal/StructuredSwift+ServiceMetadata.swift @@ -50,7 +50,8 @@ extension VariableDescription { package static func methodDescriptor( accessModifier: AccessModifier? = nil, literalFullyQualifiedService: String, - literalMethodName: String + literalMethodName: String, + namer: Namer = Namer() ) -> Self { return VariableDescription( accessModifier: accessModifier, @@ -59,13 +60,14 @@ extension VariableDescription { left: .identifier(.pattern("descriptor")), right: .functionCall( FunctionCallDescription( - calledExpression: .identifierType(.methodDescriptor), + calledExpression: .identifierType(namer.methodDescriptor), arguments: [ FunctionArgumentDescription( label: "service", expression: .functionCall( .serviceDescriptor( - literalFullyQualifiedService: literalFullyQualifiedService + literalFullyQualifiedService: literalFullyQualifiedService, + namer: namer ) ) ), @@ -84,24 +86,26 @@ extension VariableDescription { /// ``` package static func serviceDescriptor( accessModifier: AccessModifier? = nil, - literalFullyQualifiedService name: String + literalFullyQualifiedService name: String, + namer: Namer = Namer() ) -> Self { return VariableDescription( accessModifier: accessModifier, isStatic: true, kind: .let, left: .identifierPattern("descriptor"), - right: .functionCall(.serviceDescriptor(literalFullyQualifiedService: name)) + right: .functionCall(.serviceDescriptor(literalFullyQualifiedService: name, namer: namer)) ) } } extension FunctionCallDescription { package static func serviceDescriptor( - literalFullyQualifiedService: String + literalFullyQualifiedService: String, + namer: Namer = Namer() ) -> Self { FunctionCallDescription( - calledExpression: .identifier(.type(.serviceDescriptor)), + calledExpression: .identifier(.type(namer.serviceDescriptor)), arguments: [ FunctionArgumentDescription( label: "fullyQualifiedService", @@ -123,10 +127,11 @@ extension ExtensionDescription { package static func serviceDescriptor( accessModifier: AccessModifier? = nil, propertyName: String, - literalFullyQualifiedService: String + literalFullyQualifiedService: String, + namer: Namer = Namer() ) -> ExtensionDescription { return ExtensionDescription( - onType: "GRPCCore.ServiceDescriptor", + onType: namer.literalNamespacedType("ServiceDescriptor"), declarations: [ .commentable( .doc("Service descriptor for the \"\(literalFullyQualifiedService)\" service."), @@ -136,7 +141,10 @@ extension ExtensionDescription { kind: .let, left: .identifier(.pattern(propertyName)), right: .functionCall( - .serviceDescriptor(literalFullyQualifiedService: literalFullyQualifiedService) + .serviceDescriptor( + literalFullyQualifiedService: literalFullyQualifiedService, + namer: namer + ) ) ) ) @@ -151,14 +159,15 @@ extension VariableDescription { /// ``` package static func methodDescriptorsArray( accessModifier: AccessModifier? = nil, - methodNamespaceNames names: [String] + methodNamespaceNames names: [String], + namer: Namer = Namer() ) -> Self { return VariableDescription( accessModifier: accessModifier, isStatic: true, kind: .let, left: .identifier(.pattern("descriptors")), - type: .array(.methodDescriptor), + type: .array(namer.methodDescriptor), right: .literal(.array(names.map { name in .identifierPattern(name).dot("descriptor") })) ) } @@ -181,7 +190,8 @@ extension EnumDescription { literalMethod: String, literalFullyQualifiedService: String, inputType: String, - outputType: String + outputType: String, + namer: Namer = Namer() ) -> Self { return EnumDescription( accessModifier: accessModifier, @@ -201,7 +211,8 @@ extension EnumDescription { .methodDescriptor( accessModifier: accessModifier, literalFullyQualifiedService: literalFullyQualifiedService, - literalMethodName: literalMethod + literalMethodName: literalMethod, + namer: namer ) ) ), @@ -229,7 +240,8 @@ extension EnumDescription { package static func methodsNamespace( accessModifier: AccessModifier? = nil, literalFullyQualifiedService: String, - methods: [MethodDescriptor] + methods: [MethodDescriptor], + namer: Namer = Namer() ) -> EnumDescription { var description = EnumDescription(accessModifier: accessModifier, name: "Method") @@ -244,7 +256,8 @@ extension EnumDescription { literalMethod: method.name.identifyingName, literalFullyQualifiedService: literalFullyQualifiedService, inputType: method.inputType, - outputType: method.outputType + outputType: method.outputType, + namer: namer ) ) ) @@ -254,7 +267,8 @@ extension EnumDescription { // Add an array of method descriptors let methodDescriptorsArray: VariableDescription = .methodDescriptorsArray( accessModifier: accessModifier, - methodNamespaceNames: methods.map { $0.name.typeName } + methodNamespaceNames: methods.map { $0.name.typeName }, + namer: namer ) description.members.append( .commentable( @@ -278,14 +292,16 @@ extension EnumDescription { accessModifier: AccessModifier? = nil, name: String, literalFullyQualifiedService: String, - methods: [MethodDescriptor] + methods: [MethodDescriptor], + namer: Namer = Namer() ) -> EnumDescription { var description = EnumDescription(accessModifier: accessModifier, name: name) // static let descriptor = GRPCCore.ServiceDescriptor(fullyQualifiedService: "...") let descriptor = VariableDescription.serviceDescriptor( accessModifier: accessModifier, - literalFullyQualifiedService: literalFullyQualifiedService + literalFullyQualifiedService: literalFullyQualifiedService, + namer: namer ) description.members.append( .commentable( @@ -298,7 +314,8 @@ extension EnumDescription { let methodsNamespace: EnumDescription = .methodsNamespace( accessModifier: accessModifier, literalFullyQualifiedService: literalFullyQualifiedService, - methods: methods + methods: methods, + namer: namer ) description.members.append( .commentable( @@ -323,7 +340,8 @@ extension [CodeBlock] { /// ``` package static func serviceMetadata( accessModifier: AccessModifier? = nil, - service: ServiceDescriptor + service: ServiceDescriptor, + namer: Namer = Namer() ) -> Self { var blocks: [CodeBlock] = [] @@ -331,7 +349,8 @@ extension [CodeBlock] { accessModifier: accessModifier, name: service.name.typeName, literalFullyQualifiedService: service.name.identifyingName, - methods: service.methods + methods: service.methods, + namer: namer ) blocks.append( CodeBlock( @@ -345,7 +364,8 @@ extension [CodeBlock] { let descriptorExtension: ExtensionDescription = .serviceDescriptor( accessModifier: accessModifier, propertyName: service.name.propertyName, - literalFullyQualifiedService: service.name.identifyingName + literalFullyQualifiedService: service.name.identifyingName, + namer: namer ) blocks.append(CodeBlock(item: .declaration(.extension(descriptorExtension)))) diff --git a/Sources/GRPCCodeGen/Internal/StructuredSwift+Types.swift b/Sources/GRPCCodeGen/Internal/StructuredSwift+Types.swift deleted file mode 100644 index 86abaaf71..000000000 --- a/Sources/GRPCCodeGen/Internal/StructuredSwift+Types.swift +++ /dev/null @@ -1,91 +0,0 @@ -/* - * 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 ExistingTypeDescription { - fileprivate static func grpcCore(_ typeName: String) -> Self { - return .member(["GRPCCore", typeName]) - } - - fileprivate static func requestResponse( - for type: String?, - isRequest: Bool, - isStreaming: Bool, - isClient: Bool - ) -> Self { - let prefix = isStreaming ? "Streaming" : "" - let peer = isClient ? "Client" : "Server" - let kind = isRequest ? "Request" : "Response" - let baseType: Self = .grpcCore(prefix + peer + kind) - - if let type = type { - return .generic(wrapper: baseType, wrapped: .member(type)) - } else { - return baseType - } - } - - package static func serverRequest(forType type: String?, streaming: Bool) -> Self { - return .requestResponse(for: type, isRequest: true, isStreaming: streaming, isClient: false) - } - - package static func serverResponse(forType type: String?, streaming: Bool) -> Self { - return .requestResponse(for: type, isRequest: false, isStreaming: streaming, isClient: false) - } - - package static func clientRequest(forType type: String?, streaming: Bool) -> Self { - return .requestResponse(for: type, isRequest: true, isStreaming: streaming, isClient: true) - } - - package static func clientResponse(forType type: String?, streaming: Bool) -> Self { - return .requestResponse(for: type, isRequest: false, isStreaming: streaming, isClient: true) - } - - package static let serverContext: Self = .grpcCore("ServerContext") - - package static func rpcRouter(genericOver type: String) -> Self { - .generic(wrapper: .grpcCore("RPCRouter"), wrapped: .member(type)) - } - - package static let serviceDescriptor: Self = .grpcCore("ServiceDescriptor") - package static let methodDescriptor: Self = .grpcCore("MethodDescriptor") - - package static func serializer(forType type: String) -> Self { - .generic(wrapper: .grpcCore("MessageSerializer"), wrapped: .member(type)) - } - - package static func deserializer(forType type: String) -> Self { - .generic(wrapper: .grpcCore("MessageDeserializer"), wrapped: .member(type)) - } - - package static func rpcWriter(forType type: String) -> Self { - .generic(wrapper: .grpcCore("RPCWriter"), wrapped: .member(type)) - } - - package static func rpcAsyncSequence(forType type: String) -> Self { - .generic( - wrapper: .grpcCore("RPCAsyncSequence"), - wrapped: .member(type), - .any(.member(["Swift", "Error"])) - ) - } - - package static let callOptions: Self = .grpcCore("CallOptions") - package static let metadata: Self = .grpcCore("Metadata") - - package static func grpcClient(genericOver transport: String) -> Self { - .generic(wrapper: .grpcCore("GRPCClient"), wrapped: [.member(transport)]) - } -} diff --git a/Sources/GRPCCodeGen/Internal/Translator/ClientCodeTranslator.swift b/Sources/GRPCCodeGen/Internal/Translator/ClientCodeTranslator.swift index 611c09078..d4d9f87ad 100644 --- a/Sources/GRPCCodeGen/Internal/Translator/ClientCodeTranslator.swift +++ b/Sources/GRPCCodeGen/Internal/Translator/ClientCodeTranslator.swift @@ -81,6 +81,7 @@ struct ClientCodeTranslator { func translate( accessModifier: AccessModifier, service: ServiceDescriptor, + namer: Namer = Namer(), serializer: (String) -> String, deserializer: (String) -> String ) -> [CodeBlock] { @@ -101,7 +102,8 @@ struct ClientCodeTranslator { .clientProtocol( accessLevel: accessModifier, name: "ClientProtocol", - methods: service.methods + methods: service.methods, + namer: namer ) ) ), @@ -110,7 +112,10 @@ struct ClientCodeTranslator { .commentable( .preFormatted( Docs.suffix( - self.clientDocs(serviceName: service.name.identifyingName), + self.clientDocs( + serviceName: service.name.identifyingName, + moduleName: namer.grpcCore + ), withDocs: service.documentation ) ), @@ -120,7 +125,8 @@ struct ClientCodeTranslator { name: "Client", serviceEnum: service.name.typeName, clientProtocol: "ClientProtocol", - methods: service.methods + methods: service.methods, + namer: namer ) ) ), @@ -132,6 +138,7 @@ struct ClientCodeTranslator { accessLevel: accessModifier, name: "\(service.name.typeName).ClientProtocol", methods: service.methods, + namer: namer, serializer: serializer, deserializer: deserializer ) @@ -145,7 +152,8 @@ struct ClientCodeTranslator { let extensionWithExplodedAPI: ExtensionDescription = .explodedClientMethods( accessLevel: accessModifier, on: "\(service.name.typeName).ClientProtocol", - methods: service.methods + methods: service.methods, + namer: namer ) blocks.append( CodeBlock( @@ -166,12 +174,12 @@ struct ClientCodeTranslator { """ } - private func clientDocs(serviceName: String) -> String { + private func clientDocs(serviceName: String, moduleName: String) -> String { return """ /// Generated client for the "\(serviceName)" service. /// /// The ``Client`` provides an implementation of ``ClientProtocol`` which wraps - /// a `GRPCCore.GRPCCClient`. The underlying `GRPCClient` provides the long-lived + /// a `\(moduleName).GRPCCClient`. The underlying `GRPCClient` provides the long-lived /// means of communication with the remote peer. """ } diff --git a/Sources/GRPCCodeGen/Internal/Translator/IDLToStructuredSwiftTranslator.swift b/Sources/GRPCCodeGen/Internal/Translator/IDLToStructuredSwiftTranslator.swift index 29366bd3d..e83fa34c7 100644 --- a/Sources/GRPCCodeGen/Internal/Translator/IDLToStructuredSwiftTranslator.swift +++ b/Sources/GRPCCodeGen/Internal/Translator/IDLToStructuredSwiftTranslator.swift @@ -36,6 +36,8 @@ package struct IDLToStructuredSwiftTranslator { let serverTranslator = ServerCodeTranslator() let clientTranslator = ClientCodeTranslator() + let namer = Namer(grpcCore: grpcCoreModuleName) + for service in codeGenerationRequest.services { codeBlocks.append( CodeBlock(comment: .mark("\(service.name.identifyingName)", sectionBreak: true)) @@ -43,7 +45,8 @@ package struct IDLToStructuredSwiftTranslator { let metadata = metadataTranslator.translate( accessModifier: accessModifier, - service: service + service: service, + namer: namer ) codeBlocks.append(contentsOf: metadata) @@ -55,6 +58,7 @@ package struct IDLToStructuredSwiftTranslator { let blocks = serverTranslator.translate( accessModifier: accessModifier, service: service, + namer: namer, serializer: codeGenerationRequest.makeSerializerCodeSnippet, deserializer: codeGenerationRequest.makeDeserializerCodeSnippet ) @@ -68,6 +72,7 @@ package struct IDLToStructuredSwiftTranslator { let blocks = clientTranslator.translate( accessModifier: accessModifier, service: service, + namer: namer, serializer: codeGenerationRequest.makeSerializerCodeSnippet, deserializer: codeGenerationRequest.makeDeserializerCodeSnippet ) diff --git a/Sources/GRPCCodeGen/Internal/Translator/MetadataTranslator.swift b/Sources/GRPCCodeGen/Internal/Translator/MetadataTranslator.swift index a7b91132e..41252995e 100644 --- a/Sources/GRPCCodeGen/Internal/Translator/MetadataTranslator.swift +++ b/Sources/GRPCCodeGen/Internal/Translator/MetadataTranslator.swift @@ -19,8 +19,9 @@ struct MetadataTranslator { func translate( accessModifier: AccessModifier, - service: ServiceDescriptor + service: ServiceDescriptor, + namer: Namer = Namer() ) -> [CodeBlock] { - .serviceMetadata(accessModifier: accessModifier, service: service) + .serviceMetadata(accessModifier: accessModifier, service: service, namer: namer) } } diff --git a/Sources/GRPCCodeGen/Internal/Translator/ServerCodeTranslator.swift b/Sources/GRPCCodeGen/Internal/Translator/ServerCodeTranslator.swift index e563981e6..2bf06757f 100644 --- a/Sources/GRPCCodeGen/Internal/Translator/ServerCodeTranslator.swift +++ b/Sources/GRPCCodeGen/Internal/Translator/ServerCodeTranslator.swift @@ -63,6 +63,7 @@ struct ServerCodeTranslator { func translate( accessModifier: AccessModifier, service: ServiceDescriptor, + namer: Namer = Namer(), serializer: (String) -> String, deserializer: (String) -> String ) -> [CodeBlock] { @@ -83,7 +84,8 @@ struct ServerCodeTranslator { .streamingService( accessLevel: accessModifier, name: "StreamingServiceProtocol", - methods: service.methods + methods: service.methods, + namer: namer ) ) ), @@ -101,7 +103,8 @@ struct ServerCodeTranslator { accessLevel: accessModifier, name: "ServiceProtocol", streamingProtocol: "\(service.name.typeName).StreamingServiceProtocol", - methods: service.methods + methods: service.methods, + namer: namer ) ) ), @@ -119,7 +122,8 @@ struct ServerCodeTranslator { accessModifier: accessModifier, name: "SimpleServiceProtocol", serviceProtocol: "\(service.name.typeName).ServiceProtocol", - methods: service.methods + methods: service.methods, + namer: namer ) ) ), @@ -133,6 +137,7 @@ struct ServerCodeTranslator { on: "\(service.name.typeName).StreamingServiceProtocol", serviceNamespace: service.name.typeName, methods: service.methods, + namer: namer, serializer: serializer, deserializer: deserializer ) @@ -148,7 +153,8 @@ struct ServerCodeTranslator { .streamingServiceProtocolDefaultImplementation( accessModifier: accessModifier, on: "\(service.name.typeName).ServiceProtocol", - methods: service.methods + methods: service.methods, + namer: namer ) blocks.append( CodeBlock( @@ -163,7 +169,8 @@ struct ServerCodeTranslator { let serviceDefaultImplExtension: ExtensionDescription = .serviceProtocolDefaultImplementation( accessModifier: accessModifier, on: "\(service.name.typeName).SimpleServiceProtocol", - methods: service.methods + methods: service.methods, + namer: namer ) blocks.append( CodeBlock( diff --git a/Tests/GRPCCodeGenTests/Internal/Translator/IDLToStructuredSwiftTranslatorSnippetBasedTests.swift b/Tests/GRPCCodeGenTests/Internal/Translator/IDLToStructuredSwiftTranslatorSnippetBasedTests.swift index 5be986dab..05375ae4f 100644 --- a/Tests/GRPCCodeGenTests/Internal/Translator/IDLToStructuredSwiftTranslatorSnippetBasedTests.swift +++ b/Tests/GRPCCodeGenTests/Internal/Translator/IDLToStructuredSwiftTranslatorSnippetBasedTests.swift @@ -139,6 +139,83 @@ final class IDLToStructuredSwiftTranslatorSnippetBasedTests: XCTestCase { ) } + func testGenerateWithDifferentModuleName() throws { + let service = ServiceDescriptor( + documentation: "/// Documentation for FooService\n", + name: ServiceName( + identifyingName: "foo.FooService", + typeName: "Foo_FooService", + propertyName: "foo_FooService" + ), + methods: [ + MethodDescriptor( + documentation: "", + name: MethodName( + identifyingName: "Unary", + typeName: "Unary", + functionName: "unary" + ), + isInputStreaming: false, + isOutputStreaming: false, + inputType: "Foo", + outputType: "Bar" + ), + MethodDescriptor( + documentation: "", + name: MethodName( + identifyingName: "ClientStreaming", + typeName: "ClientStreaming", + functionName: "clientStreaming" + ), + isInputStreaming: true, + isOutputStreaming: false, + inputType: "Foo", + outputType: "Bar" + ), + MethodDescriptor( + documentation: "", + name: MethodName( + identifyingName: "ServerStreaming", + typeName: "ServerStreaming", + functionName: "serverStreaming" + ), + isInputStreaming: false, + isOutputStreaming: true, + inputType: "Foo", + outputType: "Bar" + ), + MethodDescriptor( + documentation: "", + name: MethodName( + identifyingName: "BidiStreaming", + typeName: "BidiStreaming", + functionName: "bidiStreaming" + ), + isInputStreaming: true, + isOutputStreaming: true, + inputType: "Foo", + outputType: "Bar" + ), + ] + ) + + let request = makeCodeGenerationRequest(services: [service]) + let translator = IDLToStructuredSwiftTranslator() + let structuredSwift = try translator.translate( + codeGenerationRequest: request, + accessLevel: .internal, + accessLevelOnImports: false, + client: true, + server: true, + grpcCoreModuleName: String("GRPCCore".reversed()) + ) + let renderer = TextBasedRenderer.default + let sourceFile = try renderer.render(structured: structuredSwift) + let contents = sourceFile.contents + + XCTAssertFalse(contents.contains("GRPCCore")) + } + func testEmptyFileGeneration() throws { let expectedSwift = """ @@ -161,7 +238,8 @@ final class IDLToStructuredSwiftTranslatorSnippetBasedTests: XCTestCase { codeGenerationRequest: CodeGenerationRequest, expectedSwift: String, accessLevel: CodeGenerator.Config.AccessLevel, - server: Bool = false + server: Bool = false, + grpcCoreModuleName: String = "GRPCCore" ) throws { let translator = IDLToStructuredSwiftTranslator() let structuredSwift = try translator.translate( @@ -170,7 +248,7 @@ final class IDLToStructuredSwiftTranslatorSnippetBasedTests: XCTestCase { accessLevelOnImports: true, client: false, server: server, - grpcCoreModuleName: "GRPCCore" + grpcCoreModuleName: grpcCoreModuleName ) let renderer = TextBasedRenderer.default let sourceFile = try renderer.render(structured: structuredSwift) From 8fa87bf271d8b7d12e8ebca9143db6e5fe2d7803 Mon Sep 17 00:00:00 2001 From: George Barnett Date: Fri, 28 Feb 2025 13:28:00 +0000 Subject: [PATCH 3/3] streaming -> isStreaming --- Sources/GRPCCodeGen/Internal/Namer.swift | 31 ++++++++++++++----- .../Internal/StructuredSwift+Client.swift | 12 +++---- .../Internal/StructuredSwift+Server.swift | 12 ++++--- 3 files changed, 36 insertions(+), 19 deletions(-) diff --git a/Sources/GRPCCodeGen/Internal/Namer.swift b/Sources/GRPCCodeGen/Internal/Namer.swift index f5c7db422..560d0b579 100644 --- a/Sources/GRPCCodeGen/Internal/Namer.swift +++ b/Sources/GRPCCodeGen/Internal/Namer.swift @@ -47,25 +47,40 @@ package struct Namer: Sendable, Hashable { return self.grpcCore + "." + type } - func serverRequest(forType type: String?, streaming: Bool) -> ExistingTypeDescription { - return self.requestResponse(for: type, isRequest: true, isStreaming: streaming, isClient: false) + func serverRequest(forType type: String?, isStreaming: Bool) -> ExistingTypeDescription { + return self.requestResponse( + for: type, + isRequest: true, + isStreaming: isStreaming, + isClient: false + ) } - func serverResponse(forType type: String?, streaming: Bool) -> ExistingTypeDescription { + func serverResponse(forType type: String?, isStreaming: Bool) -> ExistingTypeDescription { return self.requestResponse( for: type, isRequest: false, - isStreaming: streaming, + isStreaming: isStreaming, isClient: false ) } - func clientRequest(forType type: String?, streaming: Bool) -> ExistingTypeDescription { - return self.requestResponse(for: type, isRequest: true, isStreaming: streaming, isClient: true) + func clientRequest(forType type: String?, isStreaming: Bool) -> ExistingTypeDescription { + return self.requestResponse( + for: type, + isRequest: true, + isStreaming: isStreaming, + isClient: true + ) } - func clientResponse(forType type: String?, streaming: Bool) -> ExistingTypeDescription { - return self.requestResponse(for: type, isRequest: false, isStreaming: streaming, isClient: true) + func clientResponse(forType type: String?, isStreaming: Bool) -> ExistingTypeDescription { + return self.requestResponse( + for: type, + isRequest: false, + isStreaming: isStreaming, + isClient: true + ) } var serverContext: ExistingTypeDescription { diff --git a/Sources/GRPCCodeGen/Internal/StructuredSwift+Client.swift b/Sources/GRPCCodeGen/Internal/StructuredSwift+Client.swift index 5054c557a..99fddbede 100644 --- a/Sources/GRPCCodeGen/Internal/StructuredSwift+Client.swift +++ b/Sources/GRPCCodeGen/Internal/StructuredSwift+Client.swift @@ -62,7 +62,7 @@ extension FunctionSignatureDescription { signature.parameters.append( ParameterDescription( label: "request", - type: namer.clientRequest(forType: input, streaming: streamingInput) + type: namer.clientRequest(forType: input, isStreaming: streamingInput) ) ) @@ -99,7 +99,7 @@ extension FunctionSignatureDescription { ClosureSignatureDescription( parameters: [ ParameterDescription( - type: namer.clientResponse(forType: output, streaming: streamingOutput) + type: namer.clientResponse(forType: output, isStreaming: streamingOutput) ) ], keywords: [.async, .throws], @@ -358,7 +358,7 @@ extension FunctionSignatureDescription { ClosureSignatureDescription( parameters: [ ParameterDescription( - type: namer.clientResponse(forType: output, streaming: streamingOutput) + type: namer.clientResponse(forType: output, isStreaming: streamingOutput) ) ], keywords: [.async, .throws], @@ -423,7 +423,7 @@ extension [CodeBlock] { left: .identifierPattern("request"), right: .functionCall( calledExpression: .identifierType( - namer.clientRequest(forType: input, streaming: streamingInput) + namer.clientRequest(forType: input, isStreaming: streamingInput) ), arguments: arguments(streaming: streamingInput) ) @@ -579,7 +579,7 @@ extension FunctionDescription { parameters: [ ParameterDescription( label: "request", - type: namer.clientRequest(forType: input, streaming: streamingInput) + type: namer.clientRequest(forType: input, isStreaming: streamingInput) ), ParameterDescription( label: "serializer", @@ -603,7 +603,7 @@ extension FunctionDescription { ClosureSignatureDescription( parameters: [ ParameterDescription( - type: namer.clientResponse(forType: output, streaming: streamingOutput) + type: namer.clientResponse(forType: output, isStreaming: streamingOutput) ) ], keywords: [.async, .throws], diff --git a/Sources/GRPCCodeGen/Internal/StructuredSwift+Server.swift b/Sources/GRPCCodeGen/Internal/StructuredSwift+Server.swift index 7da41d2d9..a012f0b67 100644 --- a/Sources/GRPCCodeGen/Internal/StructuredSwift+Server.swift +++ b/Sources/GRPCCodeGen/Internal/StructuredSwift+Server.swift @@ -36,12 +36,14 @@ extension FunctionSignatureDescription { parameters: [ ParameterDescription( label: "request", - type: namer.serverRequest(forType: input, streaming: streamingInput) + type: namer.serverRequest(forType: input, isStreaming: streamingInput) ), ParameterDescription(label: "context", type: namer.serverContext), ], keywords: [.async, .throws], - returnType: .identifierType(namer.serverResponse(forType: output, streaming: streamingOutput)) + returnType: .identifierType( + namer.serverResponse(forType: output, isStreaming: streamingOutput) + ) ) } } @@ -396,7 +398,7 @@ extension FunctionDescription { ? .identifierPattern("request") : .functionCall( calledExpression: .identifierType( - namer.serverRequest(forType: nil, streaming: false) + namer.serverRequest(forType: nil, isStreaming: false) ), arguments: [ FunctionArgumentDescription( @@ -432,7 +434,7 @@ extension FunctionDescription { expression: streamingOutput ? .identifierPattern("response") : .functionCall( - calledExpression: .identifierType(namer.serverResponse(forType: nil, streaming: true)), + calledExpression: .identifierType(namer.serverResponse(forType: nil, isStreaming: true)), arguments: [ FunctionArgumentDescription( label: "single", @@ -750,7 +752,7 @@ extension FunctionDescription { .functionCall( calledExpression: .return( .identifierType( - namer.serverResponse(forType: output, streaming: streamingOutput) + namer.serverResponse(forType: output, isStreaming: streamingOutput) ) ), arguments: streamingOutput ? makeStreamingOutputArguments() : makeUnaryOutputArguments()