main.swift 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  1. /*
  2. *
  3. * Copyright 2017, Google Inc.
  4. * All rights reserved.
  5. *
  6. * Redistribution and use in source and binary forms, with or without
  7. * modification, are permitted provided that the following conditions are
  8. * met:
  9. *
  10. * * Redistributions of source code must retain the above copyright
  11. * notice, this list of conditions and the following disclaimer.
  12. * * Redistributions in binary form must reproduce the above
  13. * copyright notice, this list of conditions and the following disclaimer
  14. * in the documentation and/or other materials provided with the
  15. * distribution.
  16. * * Neither the name of Google Inc. nor the names of its
  17. * contributors may be used to endorse or promote products derived from
  18. * this software without specific prior written permission.
  19. *
  20. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  21. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  22. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  23. * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  24. * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  25. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  26. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  27. * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  28. * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  29. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  30. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  31. *
  32. */
  33. import Foundation
  34. import SwiftProtobuf
  35. import PluginLibrary
  36. import Stencil
  37. import PathKit
  38. func protoMessageName(_ name :String?) -> String {
  39. guard let name = name else {
  40. return ""
  41. }
  42. let parts = name.components(separatedBy:".")
  43. if parts.count == 3 {
  44. return parts[1].capitalized + "_" + parts[2]
  45. } else {
  46. return name
  47. }
  48. }
  49. func pathName(_ arguments: [Any?]) throws -> String {
  50. if arguments.count != 3 {
  51. throw TemplateSyntaxError("path expects 3 arguments")
  52. }
  53. guard let protoFile = arguments[0] as? Google_Protobuf_FileDescriptorProto
  54. else {
  55. throw TemplateSyntaxError("tag must be called with a " +
  56. "Google_Protobuf_FileDescriptorProto" +
  57. " argument, received \(arguments[0])")
  58. }
  59. guard let service = arguments[1] as? Google_Protobuf_ServiceDescriptorProto
  60. else {
  61. throw TemplateSyntaxError("tag must be called with a " +
  62. "Google_Protobuf_ServiceDescriptorProto" +
  63. " argument, received \(arguments[1])")
  64. }
  65. guard let method = arguments[2] as? Google_Protobuf_MethodDescriptorProto
  66. else {
  67. throw TemplateSyntaxError("tag must be called with a " +
  68. "Google_Protobuf_MethodDescriptorProto" +
  69. " argument, received \(arguments[2])")
  70. }
  71. return "/" + protoFile.package! + "." + service.name! + "/" + method.name!
  72. }
  73. func packageServiceMethodName(_ arguments: [Any?]) throws -> String {
  74. if arguments.count != 3 {
  75. throw TemplateSyntaxError("tag expects 3 arguments")
  76. }
  77. guard let protoFile = arguments[0] as? Google_Protobuf_FileDescriptorProto
  78. else {
  79. throw TemplateSyntaxError("tag must be called with a " +
  80. "Google_Protobuf_FileDescriptorProto" +
  81. " argument, received \(arguments[0])")
  82. }
  83. guard let service = arguments[1] as? Google_Protobuf_ServiceDescriptorProto
  84. else {
  85. throw TemplateSyntaxError("tag must be called with a " +
  86. "Google_Protobuf_ServiceDescriptorProto" +
  87. " argument, received \(arguments[1])")
  88. }
  89. guard let method = arguments[2] as? Google_Protobuf_MethodDescriptorProto
  90. else {
  91. throw TemplateSyntaxError("tag must be called with a " +
  92. "Google_Protobuf_MethodDescriptorProto" +
  93. " argument, received \(arguments[2])")
  94. }
  95. return protoFile.package!.capitalized + "_" + service.name! + method.name!
  96. }
  97. func packageServiceName(_ arguments: [Any?]) throws -> String {
  98. if arguments.count != 2 {
  99. throw TemplateSyntaxError("tag expects 2 arguments")
  100. }
  101. guard let protoFile = arguments[0] as? Google_Protobuf_FileDescriptorProto
  102. else {
  103. throw TemplateSyntaxError("tag must be called with a " +
  104. "Google_Protobuf_FileDescriptorProto" +
  105. " argument, received \(arguments[0])")
  106. }
  107. guard let service = arguments[1] as? Google_Protobuf_ServiceDescriptorProto
  108. else {
  109. throw TemplateSyntaxError("tag must be called with a " +
  110. "Google_Protobuf_ServiceDescriptorProto" +
  111. " argument, received \(arguments[1])")
  112. }
  113. return protoFile.package!.capitalized + "_" + service.name!
  114. }
  115. // Code templates use "//-" prefixes to comment-out template operators
  116. // to keep them from interfering with Swift code formatting tools.
  117. // Use this to remove them after templates have been expanded.
  118. func stripMarkers(_ code:String) -> String {
  119. let inputLines = code.components(separatedBy:"\n")
  120. var outputLines : [String] = []
  121. for line in inputLines {
  122. if line.contains("//-") {
  123. let removed = line.replacingOccurrences(of:"//-", with:"")
  124. if (removed.trimmingCharacters(in:CharacterSet.whitespaces) != "") {
  125. outputLines.append(removed)
  126. }
  127. } else {
  128. outputLines.append(line)
  129. }
  130. }
  131. return outputLines.joined(separator:"\n")
  132. }
  133. func Log(_ message : String) {
  134. FileHandle.standardError.write((message + "\n").data(using:.utf8)!)
  135. }
  136. func main() throws {
  137. // initialize template engine and add custom filters
  138. let ext = Extension()
  139. ext.registerFilter("call") { (value: Any?, arguments: [Any?]) in
  140. return try packageServiceMethodName(arguments) + "Call"
  141. }
  142. ext.registerFilter("session") { (value: Any?, arguments: [Any?]) in
  143. return try packageServiceMethodName(arguments) + "Session"
  144. }
  145. ext.registerFilter("path") { (value: Any?, arguments: [Any?]) in
  146. return try pathName(arguments)
  147. }
  148. ext.registerFilter("provider") { (value: Any?, arguments: [Any?]) in
  149. return try packageServiceName(arguments) + "Provider"
  150. }
  151. ext.registerFilter("clienterror") { (value: Any?, arguments: [Any?]) in
  152. return try packageServiceName(arguments) + "ClientError"
  153. }
  154. ext.registerFilter("servererror") { (value: Any?, arguments: [Any?]) in
  155. return try packageServiceName(arguments) + "ServerError"
  156. }
  157. ext.registerFilter("server") { (value: Any?, arguments: [Any?]) in
  158. return try packageServiceName(arguments) + "Server"
  159. }
  160. ext.registerFilter("service") { (value: Any?, arguments: [Any?]) in
  161. return try packageServiceName(arguments)
  162. }
  163. ext.registerFilter("input") { (value: Any?) in
  164. if let value = value as? Google_Protobuf_MethodDescriptorProto {
  165. return protoMessageName(value.inputType)
  166. }
  167. throw TemplateSyntaxError("message: invalid argument \(value)")
  168. }
  169. ext.registerFilter("output") { (value: Any?) in
  170. if let value = value as? Google_Protobuf_MethodDescriptorProto {
  171. return protoMessageName(value.outputType)
  172. }
  173. throw TemplateSyntaxError("message: invalid argument \(value)")
  174. }
  175. let templateEnvironment = Environment(loader: InternalLoader(),
  176. extensions:[ext])
  177. // initialize responses
  178. var response = Google_Protobuf_Compiler_CodeGeneratorResponse()
  179. var log = ""
  180. // read plugin input
  181. let rawRequest = try Stdin.readall()
  182. let request = try Google_Protobuf_Compiler_CodeGeneratorRequest(protobuf: rawRequest)
  183. // process each .proto file separately
  184. for protoFile in request.protoFile {
  185. // a package declaration is required
  186. guard let package = protoFile.package else {
  187. print("ERROR: no package for \(protoFile.name)")
  188. continue
  189. }
  190. // log info about the service
  191. log += "File \(protoFile.name!)\n"
  192. for service in protoFile.service {
  193. log += "Service \(service.name!)\n"
  194. for method in service.method {
  195. log += " Method \(method.name!)\n"
  196. log += " input \(method.inputType!)\n"
  197. log += " output \(method.outputType!)\n"
  198. log += " client_streaming \(method.clientStreaming!)\n"
  199. log += " server_streaming \(method.serverStreaming!)\n"
  200. }
  201. log += " Options \(service.options)\n"
  202. }
  203. // generate separate implementation files for client and server
  204. let context = ["protoFile": protoFile]
  205. do {
  206. let clientcode = try templateEnvironment.renderTemplate(name:"client.pb.swift",
  207. context: context)
  208. var clientfile = Google_Protobuf_Compiler_CodeGeneratorResponse.File()
  209. clientfile.name = package + ".client.pb.swift"
  210. clientfile.content = stripMarkers(clientcode)
  211. response.file.append(clientfile)
  212. let servercode = try templateEnvironment.renderTemplate(name:"server.pb.swift",
  213. context: context)
  214. var serverfile = Google_Protobuf_Compiler_CodeGeneratorResponse.File()
  215. serverfile.name = package + ".server.pb.swift"
  216. serverfile.content = stripMarkers(servercode)
  217. response.file.append(serverfile)
  218. } catch (let error) {
  219. log += "ERROR: \(error)\n"
  220. }
  221. }
  222. log += "\(request)"
  223. // add the logfile to the code generation response
  224. var logfile = Google_Protobuf_Compiler_CodeGeneratorResponse.File()
  225. logfile.name = "swiftgrpc.log"
  226. logfile.content = log
  227. response.file.append(logfile)
  228. // return everything to the caller
  229. let serializedResponse = try response.serializeProtobuf()
  230. Stdout.write(bytes: serializedResponse)
  231. }
  232. try main()