2
0

ProtobufCodeGenParser.swift 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. /*
  2. * Copyright 2024, 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 struct GRPCCodeGen.CodeGenerationRequest
  20. /// Parses a ``FileDescriptor`` object into a ``CodeGenerationRequest`` object.
  21. internal struct ProtobufCodeGenParser {
  22. let input: FileDescriptor
  23. let namer: SwiftProtobufNamer
  24. let extraModuleImports: [String]
  25. let protoToModuleMappings: ProtoFileToModuleMappings
  26. internal init(
  27. input: FileDescriptor,
  28. protoFileModuleMappings: ProtoFileToModuleMappings,
  29. extraModuleImports: [String]
  30. ) {
  31. self.input = input
  32. self.extraModuleImports = extraModuleImports
  33. self.protoToModuleMappings = protoFileModuleMappings
  34. self.namer = SwiftProtobufNamer(
  35. currentFile: input,
  36. protoFileToModuleMappings: protoFileModuleMappings
  37. )
  38. }
  39. internal func parse() throws -> CodeGenerationRequest {
  40. var header = self.input.header
  41. // Ensuring there is a blank line after the header.
  42. if !header.isEmpty && !header.hasSuffix("\n\n") {
  43. header.append("\n")
  44. }
  45. let leadingTrivia = """
  46. // DO NOT EDIT.
  47. // swift-format-ignore-file
  48. //
  49. // Generated by the gRPC Swift generator plugin for the protocol buffer compiler.
  50. // Source: \(self.input.name)
  51. //
  52. // For information on using the generated types, please see the documentation:
  53. // https://github.com/grpc/grpc-swift
  54. """
  55. let lookupSerializer: (String) -> String = { messageType in
  56. "ProtobufSerializer<\(messageType)>()"
  57. }
  58. let lookupDeserializer: (String) -> String = { messageType in
  59. "ProtobufDeserializer<\(messageType)>()"
  60. }
  61. let services = self.input.services.map {
  62. CodeGenerationRequest.ServiceDescriptor(
  63. descriptor: $0,
  64. package: input.package,
  65. protobufNamer: self.namer
  66. )
  67. }
  68. return CodeGenerationRequest(
  69. fileName: self.input.name,
  70. leadingTrivia: header + leadingTrivia,
  71. dependencies: self.codeDependencies,
  72. services: services,
  73. lookupSerializer: lookupSerializer,
  74. lookupDeserializer: lookupDeserializer
  75. )
  76. }
  77. }
  78. extension ProtobufCodeGenParser {
  79. fileprivate var codeDependencies: [CodeGenerationRequest.Dependency] {
  80. var codeDependencies: [CodeGenerationRequest.Dependency] = [.init(module: "GRPCProtobuf")]
  81. // Adding as dependencies the modules containing generated code or types for
  82. // '.proto' files imported in the '.proto' file we are parsing.
  83. codeDependencies.append(
  84. contentsOf: (self.protoToModuleMappings.neededModules(forFile: self.input) ?? []).map {
  85. CodeGenerationRequest.Dependency(module: $0)
  86. }
  87. )
  88. // Adding extra imports passed in as an option to the plugin.
  89. codeDependencies.append(
  90. contentsOf: self.extraModuleImports.sorted().map {
  91. CodeGenerationRequest.Dependency(module: $0)
  92. }
  93. )
  94. return codeDependencies
  95. }
  96. }
  97. extension CodeGenerationRequest.ServiceDescriptor {
  98. fileprivate init(
  99. descriptor: ServiceDescriptor,
  100. package: String,
  101. protobufNamer: SwiftProtobufNamer
  102. ) {
  103. let methods = descriptor.methods.map {
  104. CodeGenerationRequest.ServiceDescriptor.MethodDescriptor(
  105. descriptor: $0,
  106. protobufNamer: protobufNamer
  107. )
  108. }
  109. let name = CodeGenerationRequest.Name(
  110. base: descriptor.name,
  111. generatedUpperCase: NamingUtils.toUpperCamelCase(descriptor.name),
  112. generatedLowerCase: NamingUtils.toLowerCamelCase(descriptor.name)
  113. )
  114. let namespace = CodeGenerationRequest.Name(
  115. base: package,
  116. generatedUpperCase: NamingUtils.toUpperCamelCase(package),
  117. generatedLowerCase: NamingUtils.toLowerCamelCase(package)
  118. )
  119. let documentation = descriptor.protoSourceComments()
  120. self.init(documentation: documentation, name: name, namespace: namespace, methods: methods)
  121. }
  122. }
  123. extension CodeGenerationRequest.ServiceDescriptor.MethodDescriptor {
  124. fileprivate init(descriptor: MethodDescriptor, protobufNamer: SwiftProtobufNamer) {
  125. let name = CodeGenerationRequest.Name(
  126. base: descriptor.name,
  127. generatedUpperCase: NamingUtils.toUpperCamelCase(descriptor.name),
  128. generatedLowerCase: NamingUtils.toLowerCamelCase(descriptor.name)
  129. )
  130. let documentation = descriptor.protoSourceComments()
  131. self.init(
  132. documentation: documentation,
  133. name: name,
  134. isInputStreaming: descriptor.clientStreaming,
  135. isOutputStreaming: descriptor.serverStreaming,
  136. inputType: protobufNamer.fullName(message: descriptor.inputType),
  137. outputType: protobufNamer.fullName(message: descriptor.outputType)
  138. )
  139. }
  140. }
  141. extension FileDescriptor {
  142. fileprivate var header: String {
  143. var header = String()
  144. // Field number used to collect the syntax field which is usually the first
  145. // declaration in a.proto file.
  146. // See more here:
  147. // https://github.com/apple/swift-protobuf/blob/main/Protos/SwiftProtobuf/google/protobuf/descriptor.proto
  148. let syntaxPath = IndexPath(index: 12)
  149. if let syntaxLocation = self.sourceCodeInfoLocation(path: syntaxPath) {
  150. header = syntaxLocation.asSourceComment(
  151. commentPrefix: "///",
  152. leadingDetachedPrefix: "//"
  153. )
  154. }
  155. return header
  156. }
  157. }