diff --git a/Package.swift b/Package.swift index 4760eb8..2188d62 100644 --- a/Package.swift +++ b/Package.swift @@ -35,7 +35,7 @@ let products: [Product] = [ let dependencies: [Package.Dependency] = [ .package( url: "https://github.com/grpc/grpc-swift.git", - from: "2.0.0" + from: "2.1.0" ), .package( url: "https://github.com/apple/swift-protobuf.git", diff --git a/Sources/GRPCProtobufCodeGen/ProtobufCodeGenParser.swift b/Sources/GRPCProtobufCodeGen/ProtobufCodeGenParser.swift index 4b4790e..e8f0d50 100644 --- a/Sources/GRPCProtobufCodeGen/ProtobufCodeGenParser.swift +++ b/Sources/GRPCProtobufCodeGen/ProtobufCodeGenParser.swift @@ -36,15 +36,18 @@ package struct ProtobufCodeGenParser { let extraModuleImports: [String] let protoToModuleMappings: ProtoFileToModuleMappings let accessLevel: CodeGenerator.Config.AccessLevel + let moduleNames: ProtobufCodeGenerator.Config.ModuleNames package init( protoFileModuleMappings: ProtoFileToModuleMappings, extraModuleImports: [String], - accessLevel: CodeGenerator.Config.AccessLevel + accessLevel: CodeGenerator.Config.AccessLevel, + moduleNames: ProtobufCodeGenerator.Config.ModuleNames ) { self.extraModuleImports = extraModuleImports self.protoToModuleMappings = protoFileModuleMappings self.accessLevel = accessLevel + self.moduleNames = moduleNames } package func parse(descriptor: FileDescriptor) throws -> CodeGenerationRequest { @@ -86,10 +89,10 @@ package struct ProtobufCodeGenParser { dependencies: self.codeDependencies(file: descriptor), services: services, makeSerializerCodeSnippet: { messageType in - "GRPCProtobuf.ProtobufSerializer<\(messageType)>()" + "\(self.moduleNames.grpcProtobuf).ProtobufSerializer<\(messageType)>()" }, makeDeserializerCodeSnippet: { messageType in - "GRPCProtobuf.ProtobufDeserializer<\(messageType)>()" + "\(self.moduleNames.grpcProtobuf).ProtobufDeserializer<\(messageType)>()" } ) } @@ -102,7 +105,7 @@ extension ProtobufCodeGenParser { } var codeDependencies: [Dependency] = [ - Dependency(module: "GRPCProtobuf", accessLevel: .internal) + Dependency(module: self.moduleNames.grpcProtobuf, accessLevel: .internal) ] // If there's a dependency on a bundled proto then add the SwiftProtobuf import. // @@ -113,7 +116,11 @@ extension ProtobufCodeGenParser { } if dependsOnBundledProto { - codeDependencies.append(Dependency(module: "SwiftProtobuf", accessLevel: self.accessLevel)) + let dependency = Dependency( + module: self.moduleNames.swiftProtobuf, + accessLevel: self.accessLevel + ) + codeDependencies.append(dependency) } // Adding as dependencies the modules containing generated code or types for diff --git a/Sources/GRPCProtobufCodeGen/ProtobufCodeGenerator.swift b/Sources/GRPCProtobufCodeGen/ProtobufCodeGenerator.swift index 08942fe..a7682dc 100644 --- a/Sources/GRPCProtobufCodeGen/ProtobufCodeGenerator.swift +++ b/Sources/GRPCProtobufCodeGen/ProtobufCodeGenerator.swift @@ -18,10 +18,10 @@ package import GRPCCodeGen package import SwiftProtobufPluginLibrary package struct ProtobufCodeGenerator { - internal var config: GRPCCodeGen.CodeGenerator.Config + internal var config: ProtobufCodeGenerator.Config package init( - config: GRPCCodeGen.CodeGenerator.Config + config: ProtobufCodeGenerator.Config ) { self.config = config } @@ -34,12 +34,58 @@ package struct ProtobufCodeGenerator { let parser = ProtobufCodeGenParser( protoFileModuleMappings: protoFileModuleMappings, extraModuleImports: extraModuleImports, - accessLevel: self.config.accessLevel + accessLevel: self.config.accessLevel, + moduleNames: self.config.moduleNames ) - let codeGenerator = GRPCCodeGen.CodeGenerator(config: self.config) + + var codeGeneratorConfig = GRPCCodeGen.CodeGenerator.Config( + accessLevel: self.config.accessLevel, + accessLevelOnImports: self.config.accessLevelOnImports, + client: self.config.generateClient, + server: self.config.generateServer, + indentation: self.config.indentation + ) + codeGeneratorConfig.grpcCoreModuleName = self.config.moduleNames.grpcCore + let codeGenerator = GRPCCodeGen.CodeGenerator(config: codeGeneratorConfig) let codeGenerationRequest = try parser.parse(descriptor: fileDescriptor) let sourceFile = try codeGenerator.generate(codeGenerationRequest) return sourceFile.contents } } + +extension ProtobufCodeGenerator { + package struct Config { + package var accessLevel: GRPCCodeGen.CodeGenerator.Config.AccessLevel + package var accessLevelOnImports: Bool + + package var generateClient: Bool + package var generateServer: Bool + + package var indentation: Int + package var moduleNames: ModuleNames + + package struct ModuleNames { + package var grpcCore: String + package var grpcProtobuf: String + package var swiftProtobuf: String + + package static let defaults = Self( + grpcCore: "GRPCCore", + grpcProtobuf: "GRPCProtobuf", + swiftProtobuf: "SwiftProtobuf" + ) + } + + package static var defaults: Self { + Self( + accessLevel: .internal, + accessLevelOnImports: false, + generateClient: true, + generateServer: true, + indentation: 4, + moduleNames: .defaults + ) + } + } +} diff --git a/Sources/protoc-gen-grpc-swift/GenerateGRPC.swift b/Sources/protoc-gen-grpc-swift/GenerateGRPC.swift index 0eafbc2..85db944 100644 --- a/Sources/protoc-gen-grpc-swift/GenerateGRPC.swift +++ b/Sources/protoc-gen-grpc-swift/GenerateGRPC.swift @@ -96,8 +96,7 @@ final class GenerateGRPC: SwiftProtobufPluginLibrary.CodeGenerator { fileNamingOption: options.fileNaming ) - let config = CodeGenerator.Config(options: options) - let fileGenerator = ProtobufCodeGenerator(config: config) + let fileGenerator = ProtobufCodeGenerator(config: options.config) let contents = try fileGenerator.generateCode( fileDescriptor: descriptor, protoFileModuleMappings: options.protoToModuleMappings, @@ -181,24 +180,3 @@ private func splitPath(pathname: String) -> (dir: String, base: String, suffix: } return (dir: dir, base: base, suffix: suffix) } - -extension GRPCCodeGen.CodeGenerator.Config { - init(options: GeneratorOptions) { - let accessLevel: GRPCCodeGen.CodeGenerator.Config.AccessLevel - switch options.visibility { - case .internal: - accessLevel = .internal - case .package: - accessLevel = .package - case .public: - accessLevel = .public - } - - self.init( - accessLevel: accessLevel, - accessLevelOnImports: options.useAccessLevelOnImports, - client: options.generateClient, - server: options.generateServer - ) - } -} diff --git a/Sources/protoc-gen-grpc-swift/Options.swift b/Sources/protoc-gen-grpc-swift/Options.swift index 16079d5..6aae0b7 100644 --- a/Sources/protoc-gen-grpc-swift/Options.swift +++ b/Sources/protoc-gen-grpc-swift/Options.swift @@ -14,6 +14,8 @@ * limitations under the License. */ +import GRPCCodeGen +import GRPCProtobufCodeGen import SwiftProtobufPluginLibrary enum GenerationError: Error, CustomStringConvertible { @@ -43,35 +45,13 @@ enum FileNaming: String { } struct GeneratorOptions { - enum Visibility: String { - case `internal` = "Internal" - case `public` = "Public" - case `package` = "Package" - - var sourceSnippet: String { - switch self { - case .internal: - return "internal" - case .public: - return "public" - case .package: - return "package" - } - } - } - - private(set) var visibility = Visibility.internal - - private(set) var generateServer = true - private(set) var generateClient = true - private(set) var protoToModuleMappings = ProtoFileToModuleMappings() private(set) var fileNaming = FileNaming.fullPath private(set) var extraModuleImports: [String] = [] - private(set) var gRPCModuleName = "GRPC" - private(set) var swiftProtobufModuleName = "SwiftProtobuf" + private(set) var generateReflectionData = false - private(set) var useAccessLevelOnImports = false + + private(set) var config: ProtobufCodeGenerator.Config = .defaults init(parameter: any CodeGeneratorParameter) throws { try self.init(pairs: parameter.parsedPairs) @@ -81,22 +61,22 @@ struct GeneratorOptions { for pair in pairs { switch pair.key { case "Visibility": - if let value = Visibility(rawValue: pair.value) { - self.visibility = value + if let value = GRPCCodeGen.CodeGenerator.Config.AccessLevel(protocOption: pair.value) { + self.config.accessLevel = value } else { throw GenerationError.invalidParameterValue(name: pair.key, value: pair.value) } case "Server": if let value = Bool(pair.value.lowercased()) { - self.generateServer = value + self.config.generateServer = value } else { throw GenerationError.invalidParameterValue(name: pair.key, value: pair.value) } case "Client": if let value = Bool(pair.value.lowercased()) { - self.generateClient = value + self.config.generateClient = value } else { throw GenerationError.invalidParameterValue(name: pair.key, value: pair.value) } @@ -129,14 +109,21 @@ struct GeneratorOptions { case "GRPCModuleName": if !pair.value.isEmpty { - self.gRPCModuleName = pair.value + self.config.moduleNames.grpcCore = pair.value + } else { + throw GenerationError.invalidParameterValue(name: pair.key, value: pair.value) + } + + case "GRPCProtobufModuleName": + if !pair.value.isEmpty { + self.config.moduleNames.grpcProtobuf = pair.value } else { throw GenerationError.invalidParameterValue(name: pair.key, value: pair.value) } case "SwiftProtobufModuleName": if !pair.value.isEmpty { - self.swiftProtobufModuleName = pair.value + self.config.moduleNames.swiftProtobuf = pair.value } else { throw GenerationError.invalidParameterValue(name: pair.key, value: pair.value) } @@ -150,7 +137,7 @@ struct GeneratorOptions { case "UseAccessLevelOnImports": if let value = Bool(pair.value.lowercased()) { - self.useAccessLevelOnImports = value + self.config.accessLevelOnImports = value } else { throw GenerationError.invalidParameterValue(name: pair.key, value: pair.value) } @@ -194,3 +181,18 @@ extension String.SubSequence { return String(trimmed) } } + +extension GRPCCodeGen.CodeGenerator.Config.AccessLevel { + fileprivate init?(protocOption value: String) { + switch value { + case "Internal": + self = .internal + case "Public": + self = .public + case "Package": + self = .package + default: + return nil + } + } +} diff --git a/Tests/GRPCProtobufCodeGenTests/Generated/test-service.pb b/Tests/GRPCProtobufCodeGenTests/Generated/test-service.pb index 62fe3c7..1a952f3 100644 Binary files a/Tests/GRPCProtobufCodeGenTests/Generated/test-service.pb and b/Tests/GRPCProtobufCodeGenTests/Generated/test-service.pb differ diff --git a/Tests/GRPCProtobufCodeGenTests/ProtobufCodeGenParserTests.swift b/Tests/GRPCProtobufCodeGenTests/ProtobufCodeGenParserTests.swift index 1159f4d..ccbeea6 100644 --- a/Tests/GRPCProtobufCodeGenTests/ProtobufCodeGenParserTests.swift +++ b/Tests/GRPCProtobufCodeGenTests/ProtobufCodeGenParserTests.swift @@ -61,7 +61,8 @@ struct ProtobufCodeGenParserTests { @Test("Dependencies") func dependencies() { let expected: [GRPCCodeGen.Dependency] = [ - .init(module: "GRPCProtobuf", accessLevel: .internal) // Always an internal import + .init(module: "GRPCProtobuf", accessLevel: .internal), // Always an internal import + .init(module: "SwiftProtobuf", accessLevel: .internal), ] #expect(self.codeGen.dependencies == expected) } diff --git a/Tests/GRPCProtobufCodeGenTests/ProtobufCodeGeneratorTests.swift b/Tests/GRPCProtobufCodeGenTests/ProtobufCodeGeneratorTests.swift index 2f357d1..e10354f 100644 --- a/Tests/GRPCProtobufCodeGenTests/ProtobufCodeGeneratorTests.swift +++ b/Tests/GRPCProtobufCodeGenTests/ProtobufCodeGeneratorTests.swift @@ -27,15 +27,10 @@ struct ProtobufCodeGeneratorTests { @Test("Generate", arguments: [CodeGenerator.Config.AccessLevel.internal]) func generate(accessLevel: GRPCCodeGen.CodeGenerator.Config.AccessLevel) throws { - let generator = ProtobufCodeGenerator( - config: CodeGenerator.Config( - accessLevel: accessLevel, - accessLevelOnImports: false, - client: true, - server: true, - indentation: 2 - ) - ) + var config = ProtobufCodeGenerator.Config.defaults + config.accessLevel = accessLevel + config.indentation = 2 + let generator = ProtobufCodeGenerator(config: config) let access: String switch accessLevel { @@ -69,6 +64,7 @@ struct ProtobufCodeGeneratorTests { import GRPCCore import GRPCProtobuf + import SwiftProtobuf // MARK: - test.TestService @@ -1062,6 +1058,35 @@ struct ProtobufCodeGeneratorTests { #expect(generated == expected) } + + @Test("Generate with different module names") + func generateWithDifferentModuleNames() throws { + var config = ProtobufCodeGenerator.Config.defaults + let defaultNames = config.moduleNames + + config.accessLevel = .public + config.indentation = 2 + config.moduleNames.grpcCore = String(config.moduleNames.grpcCore.reversed()) + config.moduleNames.grpcProtobuf = String(config.moduleNames.grpcProtobuf.reversed()) + config.moduleNames.swiftProtobuf = String(config.moduleNames.swiftProtobuf.reversed()) + + let generator = ProtobufCodeGenerator(config: config) + let generated = try generator.generateCode( + fileDescriptor: Self.fileDescriptor, + protoFileModuleMappings: ProtoFileToModuleMappings(), + extraModuleImports: [] + ) + + // Mustn't contain the default names. + #expect(!generated.contains(defaultNames.grpcCore)) + #expect(!generated.contains(defaultNames.grpcProtobuf)) + #expect(!generated.contains(defaultNames.swiftProtobuf)) + + // Must contain the configured names. + #expect(generated.contains(config.moduleNames.grpcCore)) + #expect(generated.contains(config.moduleNames.grpcProtobuf)) + #expect(generated.contains(config.moduleNames.swiftProtobuf)) + } } @Suite("File-without-services (foo-messages.proto)") @@ -1071,15 +1096,11 @@ struct ProtobufCodeGeneratorTests { @Test("Generate") func generate() throws { - let generator = ProtobufCodeGenerator( - config: CodeGenerator.Config( - accessLevel: .public, - accessLevelOnImports: false, - client: true, - server: true, - indentation: 2 - ) - ) + var config: ProtobufCodeGenerator.Config = .defaults + config.accessLevel = .public + config.indentation = 2 + + let generator = ProtobufCodeGenerator(config: config) let generated = try generator.generateCode( fileDescriptor: Self.fileDescriptor, diff --git a/Tests/GRPCProtobufCodeGenTests/Utilities.swift b/Tests/GRPCProtobufCodeGenTests/Utilities.swift index ab219f5..5bfa684 100644 --- a/Tests/GRPCProtobufCodeGenTests/Utilities.swift +++ b/Tests/GRPCProtobufCodeGenTests/Utilities.swift @@ -76,7 +76,8 @@ func parseDescriptor( let parser = ProtobufCodeGenParser( protoFileModuleMappings: .init(), extraModuleImports: extraModuleImports, - accessLevel: accessLevel + accessLevel: accessLevel, + moduleNames: .defaults ) return try parser.parse(descriptor: descriptor) } diff --git a/dev/protos/generate.sh b/dev/protos/generate.sh index d108251..e9244aa 100755 --- a/dev/protos/generate.sh +++ b/dev/protos/generate.sh @@ -105,7 +105,9 @@ function generate_test_service_descriptor_set { proto_path="$(dirname "$proto")" output="$root/Tests/GRPCProtobufCodeGenTests/Generated/test-service.pb" - invoke_protoc --descriptor_set_out="$output" "$proto" -I "$proto_path" --include_source_info + invoke_protoc --descriptor_set_out="$output" "$proto" -I "$proto_path" \ + --include_imports \ + --include_source_info } function generate_foo_service_descriptor_set { diff --git a/dev/protos/local/test-service.proto b/dev/protos/local/test-service.proto index 6bc50cd..2ff7430 100644 --- a/dev/protos/local/test-service.proto +++ b/dev/protos/local/test-service.proto @@ -3,6 +3,9 @@ syntax = "proto3"; package test; +// Using a WKT forces an "SwiftProtobuf" to be imported in generated code. +import "google/protobuf/any.proto"; + // Service docs. service TestService { // Unary docs. @@ -15,5 +18,8 @@ service TestService { rpc BidirectionalStreaming (stream TestInput) returns (stream TestOutput) {} } -message TestInput {} +message TestInput { + google.protobuf.Any any = 1; +} + message TestOutput {}