diff --git a/Sources/GRPCCodeGen/Internal/StructuredSwift+ServiceMetadata.swift b/Sources/GRPCCodeGen/Internal/StructuredSwift+ServiceMetadata.swift index 7305f4e50..5efa44749 100644 --- a/Sources/GRPCCodeGen/Internal/StructuredSwift+ServiceMetadata.swift +++ b/Sources/GRPCCodeGen/Internal/StructuredSwift+ServiceMetadata.swift @@ -43,12 +43,12 @@ extension TypealiasDescription { extension VariableDescription { /// ``` /// static let descriptor = GRPCCore.MethodDescriptor( - /// service: .descriptor.fullyQualifiedService, + /// service: GRPCCore.ServiceDescriptor(fullyQualifiedServiceName: ""), /// method: "" /// ``` package static func methodDescriptor( accessModifier: AccessModifier? = nil, - serviceNamespace: String, + literalFullyQualifiedService: String, literalMethodName: String ) -> Self { return VariableDescription( @@ -62,9 +62,11 @@ extension VariableDescription { arguments: [ FunctionArgumentDescription( label: "service", - expression: .identifierType( - .member([serviceNamespace, "descriptor"]) - ).dot("fullyQualifiedService") + expression: .functionCall( + .serviceDescriptor( + literalFullyQualifiedService: literalFullyQualifiedService + ) + ) ), FunctionArgumentDescription( label: "method", @@ -77,18 +79,34 @@ extension VariableDescription { } /// ``` - /// static let descriptor = GRPCCore.ServiceDescriptor. + /// static let descriptor = GRPCCore.ServiceDescriptor(fullyQualifiedService: ) /// ``` package static func serviceDescriptor( accessModifier: AccessModifier? = nil, - namespacedProperty: String + literalFullyQualifiedService name: String ) -> Self { return VariableDescription( accessModifier: accessModifier, isStatic: true, kind: .let, left: .identifierPattern("descriptor"), - right: .identifier(.type(.serviceDescriptor)).dot(namespacedProperty) + right: .functionCall(.serviceDescriptor(literalFullyQualifiedService: name)) + ) + } +} + +extension FunctionCallDescription { + package static func serviceDescriptor( + literalFullyQualifiedService: String + ) -> Self { + FunctionCallDescription( + calledExpression: .identifier(.type(.serviceDescriptor)), + arguments: [ + FunctionArgumentDescription( + label: "fullyQualifiedService", + expression: .literal(literalFullyQualifiedService) + ) + ] ) } } @@ -97,16 +115,14 @@ extension ExtensionDescription { /// ``` /// extension GRPCCore.ServiceDescriptor { /// static let = Self( - /// package: "", - /// service: "" + /// fullyQualifiedService: /// ) /// } /// ``` package static func serviceDescriptor( accessModifier: AccessModifier? = nil, propertyName: String, - literalNamespace: String, - literalService: String + literalFullyQualifiedService: String ) -> ExtensionDescription { return ExtensionDescription( onType: "GRPCCore.ServiceDescriptor", @@ -117,17 +133,7 @@ extension ExtensionDescription { kind: .let, left: .identifier(.pattern(propertyName)), right: .functionCall( - calledExpression: .identifierType(.member("Self")), - arguments: [ - FunctionArgumentDescription( - label: "package", - expression: .literal(literalNamespace) - ), - FunctionArgumentDescription( - label: "service", - expression: .literal(literalService) - ), - ] + .serviceDescriptor(literalFullyQualifiedService: literalFullyQualifiedService) ) ) ] @@ -169,7 +175,7 @@ extension EnumDescription { accessModifier: AccessModifier? = nil, name: String, literalMethod: String, - serviceNamespace: String, + literalFullyQualifiedService: String, inputType: String, outputType: String ) -> Self { @@ -182,7 +188,7 @@ extension EnumDescription { .variable( .methodDescriptor( accessModifier: accessModifier, - serviceNamespace: serviceNamespace, + literalFullyQualifiedService: literalFullyQualifiedService, literalMethodName: literalMethod ) ), @@ -209,7 +215,7 @@ extension EnumDescription { /// ``` package static func methodsNamespace( accessModifier: AccessModifier? = nil, - serviceNamespace: String, + literalFullyQualifiedService: String, methods: [MethodDescriptor] ) -> EnumDescription { var description = EnumDescription(accessModifier: accessModifier, name: "Method") @@ -221,7 +227,7 @@ extension EnumDescription { accessModifier: accessModifier, name: method.name.base, literalMethod: method.name.base, - serviceNamespace: serviceNamespace, + literalFullyQualifiedService: literalFullyQualifiedService, inputType: method.inputType, outputType: method.outputType ) @@ -245,57 +251,31 @@ extension EnumDescription { /// enum Method { /// ... /// } - /// @available(...) - /// typealias StreamingServiceProtocol = ... - /// @available(...) - /// typealias ServiceProtocol = ... - /// ... /// } /// ``` package static func serviceNamespace( accessModifier: AccessModifier? = nil, name: String, - serviceDescriptorProperty: String, - client: Bool, - server: Bool, + literalFullyQualifiedService: String, methods: [MethodDescriptor] ) -> EnumDescription { var description = EnumDescription(accessModifier: accessModifier, name: name) - // static let descriptor = GRPCCore.ServiceDescriptor. + // static let descriptor = GRPCCore.ServiceDescriptor(fullyQualifiedService: "...") let descriptor = VariableDescription.serviceDescriptor( accessModifier: accessModifier, - namespacedProperty: serviceDescriptorProperty + literalFullyQualifiedService: literalFullyQualifiedService ) description.members.append(.variable(descriptor)) // enum Method { ... } let methodsNamespace: EnumDescription = .methodsNamespace( accessModifier: accessModifier, - serviceNamespace: name, + literalFullyQualifiedService: literalFullyQualifiedService, methods: methods ) description.members.append(.enum(methodsNamespace)) - // Typealiases for the various protocols. - var typealiasNames: [String] = [] - if server { - typealiasNames.append("StreamingServiceProtocol") - typealiasNames.append("ServiceProtocol") - } - if client { - typealiasNames.append("ClientProtocol") - typealiasNames.append("Client") - } - let typealiases: [Declaration] = typealiasNames.map { alias in - .typealias( - accessModifier: accessModifier, - name: alias, - existingType: .member(name + "_" + alias) - ) - } - description.members.append(contentsOf: typealiases) - return description } } @@ -312,18 +292,14 @@ extension [CodeBlock] { /// ``` package static func serviceMetadata( accessModifier: AccessModifier? = nil, - service: ServiceDescriptor, - client: Bool, - server: Bool + service: ServiceDescriptor ) -> Self { var blocks: [CodeBlock] = [] let serviceNamespace: EnumDescription = .serviceNamespace( accessModifier: accessModifier, name: service.namespacedGeneratedName, - serviceDescriptorProperty: service.namespacedServicePropertyName, - client: client, - server: server, + literalFullyQualifiedService: service.fullyQualifiedName, methods: service.methods ) blocks.append(CodeBlock(item: .declaration(.enum(serviceNamespace)))) @@ -331,8 +307,7 @@ extension [CodeBlock] { let descriptorExtension: ExtensionDescription = .serviceDescriptor( accessModifier: accessModifier, propertyName: service.namespacedServicePropertyName, - literalNamespace: service.namespace.base, - literalService: service.name.base + literalFullyQualifiedService: service.fullyQualifiedName ) blocks.append(CodeBlock(item: .declaration(.extension(descriptorExtension)))) diff --git a/Sources/GRPCCodeGen/Internal/Translator/ClientCodeTranslator.swift b/Sources/GRPCCodeGen/Internal/Translator/ClientCodeTranslator.swift index c5b49622b..d994fb6a0 100644 --- a/Sources/GRPCCodeGen/Internal/Translator/ClientCodeTranslator.swift +++ b/Sources/GRPCCodeGen/Internal/Translator/ClientCodeTranslator.swift @@ -86,25 +86,41 @@ struct ClientCodeTranslator { ) -> [CodeBlock] { var blocks = [CodeBlock]() - let protocolName = "\(service.namespacedGeneratedName)_ClientProtocol" - let protocolTypealias = "\(service.namespacedGeneratedName).ClientProtocol" - let structName = "\(service.namespacedGeneratedName)_Client" + let `extension` = ExtensionDescription( + onType: service.namespacedGeneratedName, + declarations: [ + // protocol ClientProtocol { ... } + .commentable( + .preFormatted(service.documentation), + .protocol( + .clientProtocol( + accessLevel: accessModifier, + name: "ClientProtocol", + methods: service.methods + ) + ) + ), - let clientProtocol: ProtocolDescription = .clientProtocol( - accessLevel: accessModifier, - name: protocolName, - methods: service.methods - ) - blocks.append( - CodeBlock( - comment: .preFormatted(service.documentation), - item: .declaration(.protocol(clientProtocol)) - ) + // struct Client: ClientProtocol { ... } + .commentable( + .preFormatted(service.documentation), + .struct( + .client( + accessLevel: accessModifier, + name: "Client", + serviceEnum: service.namespacedGeneratedName, + clientProtocol: "ClientProtocol", + methods: service.methods + ) + ) + ), + ] ) + blocks.append(.declaration(.extension(`extension`))) let extensionWithDefaults: ExtensionDescription = .clientMethodSignatureWithDefaults( accessLevel: accessModifier, - name: protocolTypealias, + name: "\(service.namespacedGeneratedName).ClientProtocol", methods: service.methods, serializer: serializer, deserializer: deserializer @@ -115,27 +131,13 @@ struct ClientCodeTranslator { let extensionWithExplodedAPI: ExtensionDescription = .explodedClientMethods( accessLevel: accessModifier, - on: protocolTypealias, + on: "\(service.namespacedGeneratedName).ClientProtocol", methods: service.methods ) blocks.append( CodeBlock(item: .declaration(.extension(extensionWithExplodedAPI))) ) - let clientStruct: StructDescription = .client( - accessLevel: accessModifier, - name: structName, - serviceEnum: service.namespacedGeneratedName, - clientProtocol: protocolTypealias, - methods: service.methods - ) - blocks.append( - CodeBlock( - comment: .preFormatted(service.documentation), - item: .declaration(.struct(clientStruct)) - ) - ) - return blocks } } diff --git a/Sources/GRPCCodeGen/Internal/Translator/IDLToStructuredSwiftTranslator.swift b/Sources/GRPCCodeGen/Internal/Translator/IDLToStructuredSwiftTranslator.swift index 2aedea18e..839ef0fa1 100644 --- a/Sources/GRPCCodeGen/Internal/Translator/IDLToStructuredSwiftTranslator.swift +++ b/Sources/GRPCCodeGen/Internal/Translator/IDLToStructuredSwiftTranslator.swift @@ -40,9 +40,7 @@ struct IDLToStructuredSwiftTranslator: Translator { let metadata = metadataTranslator.translate( accessModifier: accessModifier, - service: service, - client: client, - server: server + service: service ) codeBlocks.append(contentsOf: metadata) diff --git a/Sources/GRPCCodeGen/Internal/Translator/MetadataTranslator.swift b/Sources/GRPCCodeGen/Internal/Translator/MetadataTranslator.swift index 80675fe1f..a7b91132e 100644 --- a/Sources/GRPCCodeGen/Internal/Translator/MetadataTranslator.swift +++ b/Sources/GRPCCodeGen/Internal/Translator/MetadataTranslator.swift @@ -19,15 +19,8 @@ struct MetadataTranslator { func translate( accessModifier: AccessModifier, - service: ServiceDescriptor, - client: Bool, - server: Bool + service: ServiceDescriptor ) -> [CodeBlock] { - .serviceMetadata( - accessModifier: accessModifier, - service: service, - client: client, - server: server - ) + .serviceMetadata(accessModifier: accessModifier, service: service) } } diff --git a/Sources/GRPCCodeGen/Internal/Translator/ServerCodeTranslator.swift b/Sources/GRPCCodeGen/Internal/Translator/ServerCodeTranslator.swift index 5757b2ea4..20f78d3b3 100644 --- a/Sources/GRPCCodeGen/Internal/Translator/ServerCodeTranslator.swift +++ b/Sources/GRPCCodeGen/Internal/Translator/ServerCodeTranslator.swift @@ -68,36 +68,41 @@ struct ServerCodeTranslator { ) -> [CodeBlock] { var blocks = [CodeBlock]() - let serviceProtocolName = self.protocolName(service: service, streaming: false) - let serviceTypealiasName = self.protocolName( - service: service, - streaming: false, - joinedUsing: "." - ) - let streamingServiceProtocolName = self.protocolName(service: service, streaming: true) - let streamingServiceTypealiasName = self.protocolName( - service: service, - streaming: true, - joinedUsing: "." - ) + let `extension` = ExtensionDescription( + onType: service.namespacedGeneratedName, + declarations: [ + // protocol StreamingServiceProtocol { ... } + .commentable( + .preFormatted(service.documentation), + .protocol( + .streamingService( + accessLevel: accessModifier, + name: "StreamingServiceProtocol", + methods: service.methods + ) + ) + ), - // protocol _StreamingServiceProtocol { ... } - let streamingServiceProtocol: ProtocolDescription = .streamingService( - accessLevel: accessModifier, - name: streamingServiceProtocolName, - methods: service.methods - ) - blocks.append( - CodeBlock( - comment: .preFormatted(service.documentation), - item: .declaration(.protocol(streamingServiceProtocol)) - ) + // protocol ServiceProtocol { ... } + .commentable( + .preFormatted(service.documentation), + .protocol( + .service( + accessLevel: accessModifier, + name: "ServiceProtocol", + streamingProtocol: "\(service.namespacedGeneratedName).StreamingServiceProtocol", + methods: service.methods + ) + ) + ), + ] ) + blocks.append(.declaration(.extension(`extension`))) - // extension _StreamingServiceProtocol> { ... } + // extension .StreamingServiceProtocol> { ... } let registerExtension: ExtensionDescription = .registrableRPCServiceDefaultImplementation( accessLevel: accessModifier, - on: streamingServiceTypealiasName, + on: "\(service.namespacedGeneratedName).StreamingServiceProtocol", serviceNamespace: service.namespacedGeneratedName, methods: service.methods, serializer: serializer, @@ -110,45 +115,15 @@ struct ServerCodeTranslator { ) ) - // protocol _ServiceProtocol { ... } - let serviceProtocol: ProtocolDescription = .service( - accessLevel: accessModifier, - name: serviceProtocolName, - streamingProtocol: streamingServiceTypealiasName, - methods: service.methods - ) - blocks.append( - CodeBlock( - comment: .preFormatted(service.documentation), - item: .declaration(.protocol(serviceProtocol)) - ) - ) - // extension _ServiceProtocol { ... } let streamingServiceDefaultImplExtension: ExtensionDescription = .streamingServiceProtocolDefaultImplementation( accessModifier: accessModifier, - on: serviceTypealiasName, + on: "\(service.namespacedGeneratedName).ServiceProtocol", methods: service.methods ) - blocks.append( - CodeBlock( - comment: .doc("Partial conformance to `\(streamingServiceProtocolName)`."), - item: .declaration(.extension(streamingServiceDefaultImplExtension)) - ) - ) + blocks.append(.declaration(.extension(streamingServiceDefaultImplExtension))) return blocks } - - private func protocolName( - service: ServiceDescriptor, - streaming: Bool, - joinedUsing join: String = "_" - ) -> String { - if streaming { - return "\(service.namespacedGeneratedName)\(join)StreamingServiceProtocol" - } - return "\(service.namespacedGeneratedName)\(join)ServiceProtocol" - } } diff --git a/Tests/GRPCCodeGenTests/Internal/StructuredSwift+MetadataTests.swift b/Tests/GRPCCodeGenTests/Internal/StructuredSwift+MetadataTests.swift index d1b4ef147..6169098e8 100644 --- a/Tests/GRPCCodeGenTests/Internal/StructuredSwift+MetadataTests.swift +++ b/Tests/GRPCCodeGenTests/Internal/StructuredSwift+MetadataTests.swift @@ -42,13 +42,13 @@ extension StructuedSwiftTests { func staticMethodDescriptorProperty(access: AccessModifier) { let decl: VariableDescription = .methodDescriptor( accessModifier: access, - serviceNamespace: "FooService", + literalFullyQualifiedService: "foo.Foo", literalMethodName: "Bar" ) let expected = """ \(access) static let descriptor = GRPCCore.MethodDescriptor( - service: FooService.descriptor.fullyQualifiedService, + service: GRPCCore.ServiceDescriptor(fullyQualifiedService: "foo.Foo"), method: "Bar" ) """ @@ -56,16 +56,18 @@ extension StructuedSwiftTests { } @Test( - "static let descriptor = GRPCCore.ServiceDescriptor.", + "static let descriptor = GRPCCore.ServiceDescriptor(fullyQualifiedService:)", arguments: AccessModifier.allCases ) func staticServiceDescriptorProperty(access: AccessModifier) { let decl: VariableDescription = .serviceDescriptor( accessModifier: access, - namespacedProperty: "foo" + literalFullyQualifiedService: "foo.Bar" ) - let expected = "\(access) static let descriptor = GRPCCore.ServiceDescriptor.foo" + let expected = """ + \(access) static let descriptor = GRPCCore.ServiceDescriptor(fullyQualifiedService: "foo.Bar") + """ #expect(render(.variable(decl)) == expected) } @@ -74,16 +76,12 @@ extension StructuedSwiftTests { let decl: ExtensionDescription = .serviceDescriptor( accessModifier: access, propertyName: "foo", - literalNamespace: "echo", - literalService: "EchoService" + literalFullyQualifiedService: "echo.EchoService" ) let expected = """ extension GRPCCore.ServiceDescriptor { - \(access) static let foo = Self( - package: "echo", - service: "EchoService" - ) + \(access) static let foo = GRPCCore.ServiceDescriptor(fullyQualifiedService: "echo.EchoService") } """ #expect(render(.extension(decl)) == expected) @@ -115,7 +113,7 @@ extension StructuedSwiftTests { accessModifier: access, name: "Foo", literalMethod: "Foo", - serviceNamespace: "Bar_Baz", + literalFullyQualifiedService: "bar.Bar", inputType: "FooInput", outputType: "FooOutput" ) @@ -125,7 +123,7 @@ extension StructuedSwiftTests { \(access) typealias Input = FooInput \(access) typealias Output = FooOutput \(access) static let descriptor = GRPCCore.MethodDescriptor( - service: Bar_Baz.descriptor.fullyQualifiedService, + service: GRPCCore.ServiceDescriptor(fullyQualifiedService: "bar.Bar"), method: "Foo" ) } @@ -137,7 +135,7 @@ extension StructuedSwiftTests { func methodsNamespaceEnum(access: AccessModifier) { let decl: EnumDescription = .methodsNamespace( accessModifier: access, - serviceNamespace: "Bar_Baz", + literalFullyQualifiedService: "bar.Bar", methods: [ .init( documentation: "", @@ -156,7 +154,7 @@ extension StructuedSwiftTests { \(access) typealias Input = FooInput \(access) typealias Output = FooOutput \(access) static let descriptor = GRPCCore.MethodDescriptor( - service: Bar_Baz.descriptor.fullyQualifiedService, + service: GRPCCore.ServiceDescriptor(fullyQualifiedService: "bar.Bar"), method: "Foo" ) } @@ -172,7 +170,7 @@ extension StructuedSwiftTests { func methodsNamespaceEnumNoMethods(access: AccessModifier) { let decl: EnumDescription = .methodsNamespace( accessModifier: access, - serviceNamespace: "Bar_Baz", + literalFullyQualifiedService: "bar.Bar", methods: [] ) @@ -189,9 +187,7 @@ extension StructuedSwiftTests { let decl: EnumDescription = .serviceNamespace( accessModifier: access, name: "Foo", - serviceDescriptorProperty: "foo", - client: false, - server: false, + literalFullyQualifiedService: "Foo", methods: [ .init( documentation: "", @@ -206,13 +202,13 @@ extension StructuedSwiftTests { let expected = """ \(access) enum Foo { - \(access) static let descriptor = GRPCCore.ServiceDescriptor.foo + \(access) static let descriptor = GRPCCore.ServiceDescriptor(fullyQualifiedService: "Foo") \(access) enum Method { \(access) enum Bar { \(access) typealias Input = BarInput \(access) typealias Output = BarOutput \(access) static let descriptor = GRPCCore.MethodDescriptor( - service: Foo.descriptor.fullyQualifiedService, + service: GRPCCore.ServiceDescriptor(fullyQualifiedService: "Foo"), method: "Bar" ) } @@ -225,53 +221,23 @@ extension StructuedSwiftTests { #expect(render(.enum(decl)) == expected) } - @Test( - "enum { ... } (no methods)", - arguments: AccessModifier.allCases, - [(true, true), (false, false), (true, false), (false, true)] - ) - func serviceNamespaceEnumNoMethods(access: AccessModifier, config: (client: Bool, server: Bool)) - { + @Test("enum { ... } (no methods)", arguments: AccessModifier.allCases) + func serviceNamespaceEnumNoMethods(access: AccessModifier) { let decl: EnumDescription = .serviceNamespace( accessModifier: access, name: "Foo", - serviceDescriptorProperty: "foo", - client: config.client, - server: config.server, + literalFullyQualifiedService: "Foo", methods: [] ) - var expected = """ + let expected = """ \(access) enum Foo { - \(access) static let descriptor = GRPCCore.ServiceDescriptor.foo + \(access) static let descriptor = GRPCCore.ServiceDescriptor(fullyQualifiedService: "Foo") \(access) enum Method { \(access) static let descriptors: [GRPCCore.MethodDescriptor] = [] - }\n - """ - - if config.server { - expected += """ - \(access) typealias StreamingServiceProtocol = Foo_StreamingServiceProtocol - \(access) typealias ServiceProtocol = Foo_ServiceProtocol - """ - } - - if config.client { - if config.server { - expected += "\n" + } } - - expected += """ - \(access) typealias ClientProtocol = Foo_ClientProtocol - \(access) typealias Client = Foo_Client - """ - } - - if config.client || config.server { - expected += "\n}" - } else { - expected += "}" - } + """ #expect(render(.enum(decl)) == expected) } diff --git a/Tests/GRPCCodeGenTests/Internal/Translator/ClientCodeTranslatorSnippetBasedTests.swift b/Tests/GRPCCodeGenTests/Internal/Translator/ClientCodeTranslatorSnippetBasedTests.swift index e6fb41c7d..3516f4d67 100644 --- a/Tests/GRPCCodeGenTests/Internal/Translator/ClientCodeTranslatorSnippetBasedTests.swift +++ b/Tests/GRPCCodeGenTests/Internal/Translator/ClientCodeTranslatorSnippetBasedTests.swift @@ -39,16 +39,47 @@ struct ClientCodeTranslatorSnippetBasedTests { ) let expectedSwift = """ - /// Documentation for ServiceA - public protocol NamespaceA_ServiceA_ClientProtocol: Sendable { - /// Documentation for MethodA - func methodA( - request: GRPCCore.ClientRequest, - serializer: some GRPCCore.MessageSerializer, - deserializer: some GRPCCore.MessageDeserializer, - options: GRPCCore.CallOptions, - onResponse handleResponse: @Sendable @escaping (GRPCCore.ClientResponse) async throws -> Result - ) async throws -> Result where Result: Sendable + extension NamespaceA_ServiceA { + /// Documentation for ServiceA + public protocol ClientProtocol: Sendable { + /// Documentation for MethodA + func methodA( + request: GRPCCore.ClientRequest, + serializer: some GRPCCore.MessageSerializer, + deserializer: some GRPCCore.MessageDeserializer, + options: GRPCCore.CallOptions, + onResponse handleResponse: @Sendable @escaping (GRPCCore.ClientResponse) async throws -> Result + ) async throws -> Result where Result: Sendable + } + + /// Documentation for ServiceA + public struct Client: ClientProtocol { + private let client: GRPCCore.GRPCClient + + public init(wrapping client: GRPCCore.GRPCClient) { + self.client = client + } + + /// Documentation for MethodA + public func methodA( + request: GRPCCore.ClientRequest, + serializer: some GRPCCore.MessageSerializer, + deserializer: some GRPCCore.MessageDeserializer, + options: GRPCCore.CallOptions = .defaults, + onResponse handleResponse: @Sendable @escaping (GRPCCore.ClientResponse) async throws -> Result = { response in + try response.message + } + ) 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, + onResponse: handleResponse + ) + } + } } extension NamespaceA_ServiceA.ClientProtocol { public func methodA( @@ -88,34 +119,6 @@ struct ClientCodeTranslatorSnippetBasedTests { ) } } - /// Documentation for ServiceA - public struct NamespaceA_ServiceA_Client: NamespaceA_ServiceA.ClientProtocol { - private let client: GRPCCore.GRPCClient - - public init(wrapping client: GRPCCore.GRPCClient) { - self.client = client - } - - /// Documentation for MethodA - public func methodA( - request: GRPCCore.ClientRequest, - serializer: some GRPCCore.MessageSerializer, - deserializer: some GRPCCore.MessageDeserializer, - options: GRPCCore.CallOptions = .defaults, - onResponse handleResponse: @Sendable @escaping (GRPCCore.ClientResponse) async throws -> Result = { response in - try response.message - } - ) 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, - onResponse: handleResponse - ) - } - } """ let rendered = self.render(accessLevel: .public, service: service) diff --git a/Tests/GRPCCodeGenTests/Internal/Translator/IDLToStructuredSwiftTranslatorSnippetBasedTests.swift b/Tests/GRPCCodeGenTests/Internal/Translator/IDLToStructuredSwiftTranslatorSnippetBasedTests.swift index 0f185d4ff..7ed4727bb 100644 --- a/Tests/GRPCCodeGenTests/Internal/Translator/IDLToStructuredSwiftTranslatorSnippetBasedTests.swift +++ b/Tests/GRPCCodeGenTests/Internal/Translator/IDLToStructuredSwiftTranslatorSnippetBasedTests.swift @@ -215,35 +215,31 @@ final class IDLToStructuredSwiftTranslatorSnippetBasedTests: XCTestCase { // MARK: - namespaceA.ServiceA public enum NamespaceA_ServiceA { - public static let descriptor = GRPCCore.ServiceDescriptor.namespaceA_ServiceA + public static let descriptor = GRPCCore.ServiceDescriptor(fullyQualifiedService: "namespaceA.ServiceA") public enum Method { public static let descriptors: [GRPCCore.MethodDescriptor] = [] } - public typealias StreamingServiceProtocol = NamespaceA_ServiceA_StreamingServiceProtocol - public typealias ServiceProtocol = NamespaceA_ServiceA_ServiceProtocol } extension GRPCCore.ServiceDescriptor { - public static let namespaceA_ServiceA = Self( - package: "namespaceA", - service: "ServiceA" - ) + public static let namespaceA_ServiceA = GRPCCore.ServiceDescriptor(fullyQualifiedService: "namespaceA.ServiceA") } // MARK: namespaceA.ServiceA (server) - /// Documentation for AService - public protocol NamespaceA_ServiceA_StreamingServiceProtocol: GRPCCore.RegistrableRPCService {} + extension NamespaceA_ServiceA { + /// Documentation for AService + public protocol StreamingServiceProtocol: GRPCCore.RegistrableRPCService {} + + /// Documentation for AService + public protocol ServiceProtocol: NamespaceA_ServiceA.StreamingServiceProtocol {} + } /// Conformance to `GRPCCore.RegistrableRPCService`. extension NamespaceA_ServiceA.StreamingServiceProtocol { public func registerMethods(with router: inout GRPCCore.RPCRouter) {} } - /// Documentation for AService - public protocol NamespaceA_ServiceA_ServiceProtocol: NamespaceA_ServiceA.StreamingServiceProtocol {} - - /// Partial conformance to `NamespaceA_ServiceA_StreamingServiceProtocol`. extension NamespaceA_ServiceA.ServiceProtocol { } """ diff --git a/Tests/GRPCCodeGenTests/Internal/Translator/ServerCodeTranslatorSnippetBasedTests.swift b/Tests/GRPCCodeGenTests/Internal/Translator/ServerCodeTranslatorSnippetBasedTests.swift index 36cc93396..7cb57cc18 100644 --- a/Tests/GRPCCodeGenTests/Internal/Translator/ServerCodeTranslatorSnippetBasedTests.swift +++ b/Tests/GRPCCodeGenTests/Internal/Translator/ServerCodeTranslatorSnippetBasedTests.swift @@ -47,13 +47,24 @@ final class ServerCodeTranslatorSnippetBasedTests { ) let expectedSwift = """ - /// Documentation for ServiceA - public protocol NamespaceA_ServiceA_StreamingServiceProtocol: GRPCCore.RegistrableRPCService { - /// Documentation for unaryMethod - func unary( - request: GRPCCore.StreamingServerRequest, - context: GRPCCore.ServerContext - ) async throws -> GRPCCore.StreamingServerResponse + extension NamespaceA_ServiceA { + /// Documentation for ServiceA + public protocol StreamingServiceProtocol: GRPCCore.RegistrableRPCService { + /// Documentation for unaryMethod + func unary( + request: GRPCCore.StreamingServerRequest, + context: GRPCCore.ServerContext + ) async throws -> GRPCCore.StreamingServerResponse + } + + /// Documentation for ServiceA + public protocol ServiceProtocol: NamespaceA_ServiceA.StreamingServiceProtocol { + /// Documentation for unaryMethod + func unary( + request: GRPCCore.ServerRequest, + context: GRPCCore.ServerContext + ) async throws -> GRPCCore.ServerResponse + } } /// Conformance to `GRPCCore.RegistrableRPCService`. extension NamespaceA_ServiceA.StreamingServiceProtocol { @@ -71,15 +82,6 @@ final class ServerCodeTranslatorSnippetBasedTests { ) } } - /// Documentation for ServiceA - public protocol NamespaceA_ServiceA_ServiceProtocol: NamespaceA_ServiceA.StreamingServiceProtocol { - /// Documentation for unaryMethod - func unary( - request: GRPCCore.ServerRequest, - context: GRPCCore.ServerContext - ) async throws -> GRPCCore.ServerResponse - } - /// Partial conformance to `NamespaceA_ServiceA_StreamingServiceProtocol`. extension NamespaceA_ServiceA.ServiceProtocol { public func unary( request: GRPCCore.StreamingServerRequest, diff --git a/Tests/GRPCCodeGenTests/Internal/Translator/TypealiasTranslatorSnippetBasedTests.swift b/Tests/GRPCCodeGenTests/Internal/Translator/TypealiasTranslatorSnippetBasedTests.swift index c0ec7771a..504dbcec3 100644 --- a/Tests/GRPCCodeGenTests/Internal/Translator/TypealiasTranslatorSnippetBasedTests.swift +++ b/Tests/GRPCCodeGenTests/Internal/Translator/TypealiasTranslatorSnippetBasedTests.swift @@ -14,13 +14,13 @@ * limitations under the License. */ -#if os(macOS) || os(Linux) // swift-format doesn't like canImport(Foundation.Process) - -import XCTest +import Testing @testable import GRPCCodeGen -final class TypealiasTranslatorSnippetBasedTests: XCTestCase { +@Suite +struct TypealiasTranslatorSnippetBasedTests { + @Test func testTypealiasTranslator() throws { let method = MethodDescriptor( documentation: "Documentation for MethodA", @@ -40,222 +40,16 @@ final class TypealiasTranslatorSnippetBasedTests: XCTestCase { ), methods: [method] ) - let expectedSwift = - """ + + let expectedSwift = """ public enum NamespaceA_ServiceA { - public static let descriptor = GRPCCore.ServiceDescriptor.namespaceA_ServiceA + public static let descriptor = GRPCCore.ServiceDescriptor(fullyQualifiedService: "namespaceA.ServiceA") public enum Method { public enum MethodA { public typealias Input = NamespaceA_ServiceARequest public typealias Output = NamespaceA_ServiceAResponse public static let descriptor = GRPCCore.MethodDescriptor( - service: NamespaceA_ServiceA.descriptor.fullyQualifiedService, - method: "MethodA" - ) - } - public static let descriptors: [GRPCCore.MethodDescriptor] = [ - MethodA.descriptor - ] - } - public typealias StreamingServiceProtocol = NamespaceA_ServiceA_StreamingServiceProtocol - public typealias ServiceProtocol = NamespaceA_ServiceA_ServiceProtocol - public typealias ClientProtocol = NamespaceA_ServiceA_ClientProtocol - public typealias Client = NamespaceA_ServiceA_Client - } - extension GRPCCore.ServiceDescriptor { - public static let namespaceA_ServiceA = Self( - package: "namespaceA", - service: "ServiceA" - ) - } - """ - - try self.assertTypealiasTranslation( - codeGenerationRequest: makeCodeGenerationRequest(services: [service]), - expectedSwift: expectedSwift, - client: true, - server: true, - accessLevel: .public - ) - } - - func testTypealiasTranslatorNoMethodsServiceClientAndServer() throws { - let service = ServiceDescriptor( - documentation: "Documentation for ServiceA", - name: Name(base: "ServiceA", generatedUpperCase: "ServiceA", generatedLowerCase: "serviceA"), - namespace: Name( - base: "namespaceA", - generatedUpperCase: "NamespaceA", - generatedLowerCase: "namespaceA" - ), - methods: [] - ) - let expectedSwift = - """ - public enum NamespaceA_ServiceA { - public static let descriptor = GRPCCore.ServiceDescriptor.namespaceA_ServiceA - public enum Method { - public static let descriptors: [GRPCCore.MethodDescriptor] = [] - } - public typealias StreamingServiceProtocol = NamespaceA_ServiceA_StreamingServiceProtocol - public typealias ServiceProtocol = NamespaceA_ServiceA_ServiceProtocol - public typealias ClientProtocol = NamespaceA_ServiceA_ClientProtocol - public typealias Client = NamespaceA_ServiceA_Client - } - extension GRPCCore.ServiceDescriptor { - public static let namespaceA_ServiceA = Self( - package: "namespaceA", - service: "ServiceA" - ) - } - """ - - try self.assertTypealiasTranslation( - codeGenerationRequest: makeCodeGenerationRequest(services: [service]), - expectedSwift: expectedSwift, - client: true, - server: true, - accessLevel: .public - ) - } - - func testTypealiasTranslatorServer() throws { - let service = ServiceDescriptor( - documentation: "Documentation for ServiceA", - name: Name(base: "ServiceA", generatedUpperCase: "ServiceA", generatedLowerCase: "serviceA"), - namespace: Name( - base: "namespaceA", - generatedUpperCase: "NamespaceA", - generatedLowerCase: "namespaceA" - ), - methods: [] - ) - let expectedSwift = - """ - public enum NamespaceA_ServiceA { - public static let descriptor = GRPCCore.ServiceDescriptor.namespaceA_ServiceA - public enum Method { - public static let descriptors: [GRPCCore.MethodDescriptor] = [] - } - public typealias StreamingServiceProtocol = NamespaceA_ServiceA_StreamingServiceProtocol - public typealias ServiceProtocol = NamespaceA_ServiceA_ServiceProtocol - } - extension GRPCCore.ServiceDescriptor { - public static let namespaceA_ServiceA = Self( - package: "namespaceA", - service: "ServiceA" - ) - } - """ - - try self.assertTypealiasTranslation( - codeGenerationRequest: makeCodeGenerationRequest(services: [service]), - expectedSwift: expectedSwift, - client: false, - server: true, - accessLevel: .public - ) - } - - func testTypealiasTranslatorClient() throws { - let service = ServiceDescriptor( - documentation: "Documentation for ServiceA", - name: Name(base: "ServiceA", generatedUpperCase: "ServiceA", generatedLowerCase: "serviceA"), - namespace: Name( - base: "namespaceA", - generatedUpperCase: "NamespaceA", - generatedLowerCase: "namespaceA" - ), - methods: [] - ) - let expectedSwift = - """ - public enum NamespaceA_ServiceA { - public static let descriptor = GRPCCore.ServiceDescriptor.namespaceA_ServiceA - public enum Method { - public static let descriptors: [GRPCCore.MethodDescriptor] = [] - } - public typealias ClientProtocol = NamespaceA_ServiceA_ClientProtocol - public typealias Client = NamespaceA_ServiceA_Client - } - extension GRPCCore.ServiceDescriptor { - public static let namespaceA_ServiceA = Self( - package: "namespaceA", - service: "ServiceA" - ) - } - """ - - try self.assertTypealiasTranslation( - codeGenerationRequest: makeCodeGenerationRequest(services: [service]), - expectedSwift: expectedSwift, - client: true, - server: false, - accessLevel: .public - ) - } - - func testTypealiasTranslatorNoClientNoServer() throws { - let service = ServiceDescriptor( - documentation: "Documentation for ServiceA", - name: Name(base: "ServiceA", generatedUpperCase: "ServiceA", generatedLowerCase: "serviceA"), - namespace: Name( - base: "namespaceA", - generatedUpperCase: "NamespaceA", - generatedLowerCase: "namespaceA" - ), - methods: [] - ) - let expectedSwift = - """ - public enum NamespaceA_ServiceA { - public static let descriptor = GRPCCore.ServiceDescriptor.namespaceA_ServiceA - public enum Method { - public static let descriptors: [GRPCCore.MethodDescriptor] = [] - } - } - extension GRPCCore.ServiceDescriptor { - public static let namespaceA_ServiceA = Self( - package: "namespaceA", - service: "ServiceA" - ) - } - """ - - try self.assertTypealiasTranslation( - codeGenerationRequest: makeCodeGenerationRequest(services: [service]), - expectedSwift: expectedSwift, - client: false, - server: false, - accessLevel: .public - ) - } - - func testTypealiasTranslatorEmptyNamespace() throws { - let method = MethodDescriptor( - documentation: "Documentation for MethodA", - name: Name(base: "MethodA", generatedUpperCase: "MethodA", generatedLowerCase: "methodA"), - isInputStreaming: false, - isOutputStreaming: false, - inputType: "ServiceARequest", - outputType: "ServiceAResponse" - ) - let service = ServiceDescriptor( - documentation: "Documentation for ServiceA", - name: Name(base: "ServiceA", generatedUpperCase: "ServiceA", generatedLowerCase: "serviceA"), - namespace: Name(base: "", generatedUpperCase: "", generatedLowerCase: ""), - methods: [method] - ) - let expectedSwift = - """ - public enum ServiceA { - public static let descriptor = GRPCCore.ServiceDescriptor.ServiceA - public enum Method { - public enum MethodA { - public typealias Input = ServiceARequest - public typealias Output = ServiceAResponse - public static let descriptor = GRPCCore.MethodDescriptor( - service: ServiceA.descriptor.fullyQualifiedService, + service: GRPCCore.ServiceDescriptor(fullyQualifiedService: "namespaceA.ServiceA"), method: "MethodA" ) } @@ -263,91 +57,29 @@ final class TypealiasTranslatorSnippetBasedTests: XCTestCase { MethodA.descriptor ] } - public typealias StreamingServiceProtocol = ServiceA_StreamingServiceProtocol - public typealias ServiceProtocol = ServiceA_ServiceProtocol - public typealias ClientProtocol = ServiceA_ClientProtocol - public typealias Client = ServiceA_Client } extension GRPCCore.ServiceDescriptor { - public static let ServiceA = Self( - package: "", - service: "ServiceA" - ) + public static let namespaceA_ServiceA = GRPCCore.ServiceDescriptor(fullyQualifiedService: "namespaceA.ServiceA") } """ - try self.assertTypealiasTranslation( - codeGenerationRequest: makeCodeGenerationRequest(services: [service]), - expectedSwift: expectedSwift, - client: true, - server: true, - accessLevel: .public - ) - } - - func testTypealiasTranslatorNoMethodsService() throws { - let service = ServiceDescriptor( - documentation: "Documentation for ServiceA", - name: Name(base: "ServiceA", generatedUpperCase: "ServiceA", generatedLowerCase: "serviceA"), - namespace: Name( - base: "namespaceA", - generatedUpperCase: "NamespaceA", - generatedLowerCase: "namespaceA" - ), - methods: [] - ) - let expectedSwift = - """ - package enum NamespaceA_ServiceA { - package static let descriptor = GRPCCore.ServiceDescriptor.namespaceA_ServiceA - package enum Method { - package static let descriptors: [GRPCCore.MethodDescriptor] = [] - } - package typealias StreamingServiceProtocol = NamespaceA_ServiceA_StreamingServiceProtocol - package typealias ServiceProtocol = NamespaceA_ServiceA_ServiceProtocol - package typealias ClientProtocol = NamespaceA_ServiceA_ClientProtocol - package typealias Client = NamespaceA_ServiceA_Client - } - extension GRPCCore.ServiceDescriptor { - package static let namespaceA_ServiceA = Self( - package: "namespaceA", - service: "ServiceA" - ) - } - """ - - try self.assertTypealiasTranslation( - codeGenerationRequest: makeCodeGenerationRequest(services: [service]), - expectedSwift: expectedSwift, - client: true, - server: true, - accessLevel: .package - ) + #expect(self.render(accessLevel: .public, service: service) == expectedSwift) } } extension TypealiasTranslatorSnippetBasedTests { - private func assertTypealiasTranslation( - codeGenerationRequest request: CodeGenerationRequest, - expectedSwift: String, - client: Bool, - server: Bool, - accessLevel: SourceGenerator.Config.AccessLevel - ) throws { + func render( + accessLevel: SourceGenerator.Config.AccessLevel, + service: ServiceDescriptor + ) -> String { let translator = MetadataTranslator() - let codeBlocks = request.services.flatMap { service in - translator.translate( - accessModifier: AccessModifier(accessLevel), - service: service, - client: client, - server: server - ) - } + let codeBlocks = translator.translate( + accessModifier: AccessModifier(accessLevel), + service: service + ) + let renderer = TextBasedRenderer.default renderer.renderCodeBlocks(codeBlocks) - let contents = renderer.renderedContents() - try XCTAssertEqualWithDiff(contents, expectedSwift) + return renderer.renderedContents() } } - -#endif // os(macOS) || os(Linux)