Browse Source

Allow generating test stubs separately from the client implementation (#402)

* Split out test-stub generation from impl code

Fix unary test stub

* Applying suggested fixes

* Incorporate feedback
Martin Petrov 6 years ago
parent
commit
869c168d06

+ 2 - 0
README.md

@@ -102,8 +102,10 @@ separated from the output directory by a colon.
 | `Client` |  `true`/`false` | `true` | Whether to generate client code |
 | `Client` |  `true`/`false` | `true` | Whether to generate client code |
 | `Async` |  `true`/`false` | `true` | Whether to generate asynchronous code |
 | `Async` |  `true`/`false` | `true` | Whether to generate asynchronous code |
 | `Sync` |  `true`/`false` | `true` | Whether to generate synchronous code |
 | `Sync` |  `true`/`false` | `true` | Whether to generate synchronous code |
+| `Implementations` |  `true`/`false` | `true` | Whether to generate protocols and non-test service code. Toggling this to `false` is mostly useful when combined with `TestStubs=true` to generate files containing only test stub code |
 | `TestStubs` |  `true`/`false` | `false` | Whether to generate test stub code |
 | `TestStubs` |  `true`/`false` | `false` | Whether to generate test stub code |
 | `FileNaming` | `FullPath`/`PathToUnderscores`/`DropPath` | `FullPath` | How to handle the naming of generated sources |
 | `FileNaming` | `FullPath`/`PathToUnderscores`/`DropPath` | `FullPath` | How to handle the naming of generated sources |
+| `ExtraModuleImports` |  `String` | `` | Extra module to import in generated code. This parameter may be included multiple times to import more than one module |
 
 
 Example:
 Example:
 
 

+ 1 - 1
Sources/Examples/Echo/Generated/echo.grpc.swift

@@ -20,8 +20,8 @@
 // See the License for the specific language governing permissions and
 // See the License for the specific language governing permissions and
 // limitations under the License.
 // limitations under the License.
 //
 //
-import Foundation
 import Dispatch
 import Dispatch
+import Foundation
 import SwiftGRPC
 import SwiftGRPC
 import SwiftProtobuf
 import SwiftProtobuf
 
 

+ 2 - 6
Sources/protoc-gen-swiftgrpc/Generator-Client.swift

@@ -25,10 +25,6 @@ extension Generator {
     } else {
     } else {
       printCGRPCClient(asynchronousCode: asynchronousCode,
       printCGRPCClient(asynchronousCode: asynchronousCode,
                        synchronousCode: synchronousCode)
                        synchronousCode: synchronousCode)
-      if options.generateTestStubs {
-        printCGRPCClientTestStubs(asynchronousCode: asynchronousCode,
-                                 synchronousCode: synchronousCode)
-      }
     }
     }
   }
   }
 
 
@@ -58,8 +54,8 @@ extension Generator {
                                      synchronousCode: synchronousCode)
                                      synchronousCode: synchronousCode)
   }
   }
 
 
-  private func printCGRPCClientTestStubs(asynchronousCode: Bool,
-                                         synchronousCode: Bool) {
+  func printCGRPCClientTestStubs(asynchronousCode: Bool,
+                                 synchronousCode: Bool) {
     for method in service.methods {
     for method in service.methods {
       self.method = method
       self.method = method
       switch streamingType(method) {
       switch streamingType(method) {

+ 22 - 6
Sources/protoc-gen-swiftgrpc/Generator.swift

@@ -88,7 +88,7 @@ class Generator {
         "SwiftGRPC",
         "SwiftGRPC",
         "SwiftProtobuf"]
         "SwiftProtobuf"]
     }
     }
-    for moduleName in moduleNames {
+    for moduleName in (moduleNames + options.extraModuleImports).sorted() {
       println("import \(moduleName)")
       println("import \(moduleName)")
     }
     }
     // Build systems like Bazel will generate the Swift service definitions in a different module
     // Build systems like Bazel will generate the Swift service definitions in a different module
@@ -105,11 +105,24 @@ class Generator {
     }
     }
     println()
     println()
     
     
-    if options.generateClient {      
-      for service in file.services {
-        self.service = service
-        printClient(asynchronousCode: options.generateAsynchronous,
-                    synchronousCode: options.generateSynchronous)
+    if options.generateClient {
+      if options.generateImplementations {
+        for service in file.services {
+          self.service = service
+          printClient(asynchronousCode: options.generateAsynchronous,
+                      synchronousCode: options.generateSynchronous)
+        }
+      }
+
+      if options.generateTestStubs {
+        if options.generateNIOImplementation {
+          fatalError("Generating test stubs is not yet supported for SwiftGRPC-NIO.")
+        }
+        for service in file.services {
+          self.service = service
+          printCGRPCClientTestStubs(asynchronousCode: options.generateAsynchronous,
+                                    synchronousCode: options.generateSynchronous)
+        }
       }
       }
     }
     }
     println()
     println()
@@ -118,6 +131,9 @@ class Generator {
       if options.generateTestStubs && options.generateNIOImplementation {
       if options.generateTestStubs && options.generateNIOImplementation {
         fatalError("Generating test stubs is not yet supported for SwiftGRPC-NIO.")
         fatalError("Generating test stubs is not yet supported for SwiftGRPC-NIO.")
       }
       }
+      if options.generateTestStubs && !options.generateImplementations {
+        fatalError("Generating server test stubs without an implementation is not supported.")
+      }
       
       
       for service in file.services {
       for service in file.services {
         self.service = service
         self.service = service

+ 16 - 0
Sources/protoc-gen-swiftgrpc/options.swift

@@ -57,9 +57,11 @@ final class GeneratorOptions {
   private(set) var generateAsynchronous = true
   private(set) var generateAsynchronous = true
   private(set) var generateSynchronous = true
   private(set) var generateSynchronous = true
   private(set) var generateTestStubs = false
   private(set) var generateTestStubs = false
+  private(set) var generateImplementations = true 
   private(set) var generateNIOImplementation = false
   private(set) var generateNIOImplementation = false
   private(set) var protoToModuleMappings = ProtoFileToModuleMappings()
   private(set) var protoToModuleMappings = ProtoFileToModuleMappings()
   private(set) var fileNaming = FileNaming.FullPath
   private(set) var fileNaming = FileNaming.FullPath
+  private(set) var extraModuleImports: [String] = []
 
 
   init(parameter: String?) throws {
   init(parameter: String?) throws {
     for pair in GeneratorOptions.parseParameter(string: parameter) {
     for pair in GeneratorOptions.parseParameter(string: parameter) {
@@ -106,6 +108,13 @@ final class GeneratorOptions {
           throw GenerationError.invalidParameterValue(name: pair.key, value: pair.value)
           throw GenerationError.invalidParameterValue(name: pair.key, value: pair.value)
         }
         }
         
         
+      case "Implementations":
+        if let value = Bool(pair.value) {
+          generateImplementations = value
+        } else {
+          throw GenerationError.invalidParameterValue(name: pair.key, value: pair.value)
+        }
+
       case "NIO":
       case "NIO":
         if let value = Bool(pair.value) {
         if let value = Bool(pair.value) {
           generateNIOImplementation = value
           generateNIOImplementation = value
@@ -131,6 +140,13 @@ final class GeneratorOptions {
           throw GenerationError.invalidParameterValue(name: pair.key, value: pair.value)
           throw GenerationError.invalidParameterValue(name: pair.key, value: pair.value)
         }
         }
 
 
+      case "ExtraModuleImports":
+        if !pair.value.isEmpty {
+          extraModuleImports.append(pair.value)
+        } else {
+          throw GenerationError.invalidParameterValue(name: pair.key, value: pair.value)
+        }
+
       default:
       default:
         throw GenerationError.unknownParameter(name: pair.key)
         throw GenerationError.unknownParameter(name: pair.key)
       }
       }