main.swift 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  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 SwiftProtobuf
  18. import SwiftProtobufPluginLibrary
  19. import Stencil
  20. import PathKit
  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. guard package != "" else {
  124. print("ERROR: no package for \(fileDescriptor.name)")
  125. continue
  126. }
  127. // generate separate implementation files for client and server
  128. let context : [String:Any] = [
  129. "file": fileDescriptor,
  130. "client": true,
  131. "server": true,
  132. "access": options.visibility.sourceSnippet]
  133. do {
  134. let grpcFileName = uniqueOutputFileName(component:"grpc", fileDescriptor:fileDescriptor)
  135. let grpcCode = try templateEnvironment.renderTemplate(name:"main.swift", context: context)
  136. var grpcFile = Google_Protobuf_Compiler_CodeGeneratorResponse.File()
  137. grpcFile.name = grpcFileName
  138. grpcFile.content = stripMarkers(grpcCode)
  139. response.file.append(grpcFile)
  140. } catch (let error) {
  141. Log("ERROR \(error)")
  142. }
  143. }
  144. }
  145. // return everything to the caller
  146. let serializedResponse = try response.serializedData()
  147. Stdout.write(bytes: serializedResponse)
  148. }
  149. do {
  150. try main()
  151. } catch (let error) {
  152. Log("ERROR: \(error)")
  153. }