main.swift 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. /*
  2. * Copyright 2017, gRPC Authors All rights reserved.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. import Foundation
  17. import PathKit
  18. import Stencil
  19. import SwiftProtobuf
  20. import SwiftProtobufPluginLibrary
  21. func Log(_ message: String) {
  22. FileHandle.standardError.write((message + "\n").data(using: .utf8)!)
  23. }
  24. // Code templates use "//-" prefixes to comment-out template operators
  25. // to keep them from interfering with Swift code formatting tools.
  26. // Use this to remove them after templates have been expanded.
  27. func stripMarkers(_ code: String) -> String {
  28. let inputLines = code.components(separatedBy: "\n")
  29. var outputLines: [String] = []
  30. for line in inputLines {
  31. if line.contains("//-") {
  32. let removed = line.replacingOccurrences(of: "//-", with: "")
  33. if removed.trimmingCharacters(in: CharacterSet.whitespaces) != "" {
  34. outputLines.append(removed)
  35. }
  36. } else {
  37. outputLines.append(line)
  38. }
  39. }
  40. return outputLines.joined(separator: "\n")
  41. }
  42. // from apple/swift-protobuf/Sources/protoc-gen-swift/StringUtils.swift
  43. func splitPath(pathname: String) -> (dir: String, base: String, suffix: String) {
  44. var dir = ""
  45. var base = ""
  46. var suffix = ""
  47. #if swift(>=3.2)
  48. let pathnameChars = pathname
  49. #else
  50. let pathnameChars = pathname.characters
  51. #endif
  52. for c in pathnameChars {
  53. if c == "/" {
  54. dir += base + suffix + String(c)
  55. base = ""
  56. suffix = ""
  57. } else if c == "." {
  58. base += suffix
  59. suffix = String(c)
  60. } else {
  61. suffix += String(c)
  62. }
  63. }
  64. #if swift(>=3.2)
  65. let validSuffix = suffix.isEmpty || suffix.first == "."
  66. #else
  67. let validSuffix = suffix.isEmpty || suffix.characters.first == "."
  68. #endif
  69. if !validSuffix {
  70. base += suffix
  71. suffix = ""
  72. }
  73. return (dir: dir, base: base, suffix: suffix)
  74. }
  75. enum OutputNaming: String {
  76. case FullPath
  77. case PathToUnderscores
  78. case DropPath
  79. }
  80. var outputNamingOption: OutputNaming = .FullPath // temporarily hard-coded
  81. func outputFileName(component: String, fileDescriptor: FileDescriptor) -> String {
  82. let ext = "." + component + ".swift"
  83. let pathParts = splitPath(pathname: fileDescriptor.name)
  84. switch outputNamingOption {
  85. case .FullPath:
  86. return pathParts.dir + pathParts.base + ext
  87. case .PathToUnderscores:
  88. let dirWithUnderscores =
  89. pathParts.dir.replacingOccurrences(of: "/", with: "_")
  90. return dirWithUnderscores + pathParts.base + ext
  91. case .DropPath:
  92. return pathParts.base + ext
  93. }
  94. }
  95. var generatedFiles: [String: Int] = [:]
  96. func uniqueOutputFileName(component: String, fileDescriptor: FileDescriptor) -> String {
  97. let defaultName = outputFileName(component: component, fileDescriptor: fileDescriptor)
  98. if let count = generatedFiles[defaultName] {
  99. generatedFiles[defaultName] = count + 1
  100. return outputFileName(component: "\(count)." + component, fileDescriptor: fileDescriptor)
  101. } else {
  102. generatedFiles[defaultName] = 1
  103. return defaultName
  104. }
  105. }
  106. func main() throws {
  107. // initialize template engine and add custom filters
  108. let templateEnvironment = Environment(loader: InternalLoader(),
  109. extensions: [GRPCFilterExtension()])
  110. // initialize responses
  111. var response = Google_Protobuf_Compiler_CodeGeneratorResponse()
  112. // read plugin input
  113. let rawRequest = try Stdin.readall()
  114. let request = try Google_Protobuf_Compiler_CodeGeneratorRequest(serializedData: rawRequest)
  115. let options = try GeneratorOptions(parameter: request.parameter)
  116. // Build the SwiftProtobufPluginLibrary model of the plugin input
  117. let descriptorSet = DescriptorSet(protos: request.protoFile)
  118. // process each .proto file separately
  119. for fileDescriptor in descriptorSet.files {
  120. if fileDescriptor.services.count > 0 {
  121. // a package declaration is required for file containing service(s)
  122. let package = fileDescriptor.package
  123. // generate separate implementation files for client and server
  124. let context: [String: Any] = [
  125. "file": fileDescriptor,
  126. "client": true,
  127. "server": true,
  128. "access": options.visibility.sourceSnippet,
  129. "generateTestStubs": options.generateTestStubs
  130. ]
  131. do {
  132. let grpcFileName = uniqueOutputFileName(component: "grpc", fileDescriptor: fileDescriptor)
  133. let grpcCode = try templateEnvironment.renderTemplate(name: "main.swift", context: context)
  134. var grpcFile = Google_Protobuf_Compiler_CodeGeneratorResponse.File()
  135. grpcFile.name = grpcFileName
  136. grpcFile.content = stripMarkers(grpcCode)
  137. response.file.append(grpcFile)
  138. } catch (let error) {
  139. Log("ERROR \(error)")
  140. }
  141. }
  142. }
  143. // return everything to the caller
  144. let serializedResponse = try response.serializedData()
  145. Stdout.write(bytes: serializedResponse)
  146. }
  147. do {
  148. try main()
  149. } catch (let error) {
  150. Log("ERROR: \(error)")
  151. }