ProtobufCodeGenParser.swift 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  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. internal init(input: FileDescriptor, protoFileModuleMappings: ProtoFileToModuleMappings) {
  25. self.input = input
  26. self.namer = SwiftProtobufNamer(
  27. currentFile: input,
  28. protoFileToModuleMappings: protoFileModuleMappings
  29. )
  30. }
  31. internal func parse() throws -> CodeGenerationRequest {
  32. var header = self.input.header
  33. // Ensuring there is a blank line after the header.
  34. if !header.isEmpty && !header.hasSuffix("\n\n") {
  35. header.append("\n")
  36. }
  37. let leadingTrivia = """
  38. // DO NOT EDIT.
  39. // swift-format-ignore-file
  40. //
  41. // Generated by the gRPC Swift generator plugin for the protocol buffer compiler.
  42. // Source: \(self.input.name)
  43. //
  44. // For information on using the generated types, please see the documentation:
  45. // https://github.com/grpc/grpc-swift
  46. """
  47. var dependencies = self.input.dependencies.map {
  48. CodeGenerationRequest.Dependency(module: $0.name)
  49. }
  50. dependencies.append(CodeGenerationRequest.Dependency(module: "GRPCProtobuf"))
  51. let lookupSerializer: (String) -> String = { messageType in
  52. "ProtobufSerializer<\(messageType)>()"
  53. }
  54. let lookupDeserializer: (String) -> String = { messageType in
  55. "ProtobufDeserializer<\(messageType)>()"
  56. }
  57. let services = self.input.services.map {
  58. CodeGenerationRequest.ServiceDescriptor(
  59. descriptor: $0,
  60. package: input.package,
  61. protobufNamer: self.namer
  62. )
  63. }
  64. return CodeGenerationRequest(
  65. fileName: self.input.name,
  66. leadingTrivia: header + leadingTrivia,
  67. dependencies: dependencies,
  68. services: services,
  69. lookupSerializer: lookupSerializer,
  70. lookupDeserializer: lookupDeserializer
  71. )
  72. }
  73. }
  74. extension CodeGenerationRequest.ServiceDescriptor {
  75. fileprivate init(
  76. descriptor: ServiceDescriptor,
  77. package: String,
  78. protobufNamer: SwiftProtobufNamer
  79. ) {
  80. let methods = descriptor.methods.map {
  81. CodeGenerationRequest.ServiceDescriptor.MethodDescriptor(
  82. descriptor: $0,
  83. protobufNamer: protobufNamer
  84. )
  85. }
  86. let name = CodeGenerationRequest.Name(
  87. base: descriptor.name,
  88. generatedUpperCase: NamingUtils.toUpperCamelCase(descriptor.name),
  89. generatedLowerCase: NamingUtils.toLowerCamelCase(descriptor.name)
  90. )
  91. let namespace = CodeGenerationRequest.Name(
  92. base: package,
  93. generatedUpperCase: NamingUtils.toUpperCamelCase(package),
  94. generatedLowerCase: NamingUtils.toLowerCamelCase(package)
  95. )
  96. let documentation = descriptor.protoSourceComments()
  97. self.init(documentation: documentation, name: name, namespace: namespace, methods: methods)
  98. }
  99. }
  100. extension CodeGenerationRequest.ServiceDescriptor.MethodDescriptor {
  101. fileprivate init(descriptor: MethodDescriptor, protobufNamer: SwiftProtobufNamer) {
  102. let name = CodeGenerationRequest.Name(
  103. base: descriptor.name,
  104. generatedUpperCase: NamingUtils.toUpperCamelCase(descriptor.name),
  105. generatedLowerCase: NamingUtils.toLowerCamelCase(descriptor.name)
  106. )
  107. let documentation = descriptor.protoSourceComments()
  108. self.init(
  109. documentation: documentation,
  110. name: name,
  111. isInputStreaming: descriptor.clientStreaming,
  112. isOutputStreaming: descriptor.serverStreaming,
  113. inputType: protobufNamer.fullName(message: descriptor.inputType),
  114. outputType: protobufNamer.fullName(message: descriptor.outputType)
  115. )
  116. }
  117. }
  118. extension FileDescriptor {
  119. fileprivate var header: String {
  120. var header = String()
  121. // Field number used to collect the syntax field which is usually the first
  122. // declaration in a.proto file.
  123. // See more here:
  124. // https://github.com/apple/swift-protobuf/blob/main/Protos/SwiftProtobuf/google/protobuf/descriptor.proto
  125. let syntaxPath = IndexPath(index: 12)
  126. if let syntaxLocation = self.sourceCodeInfoLocation(path: syntaxPath) {
  127. header = syntaxLocation.asSourceComment(
  128. commentPrefix: "///",
  129. leadingDetachedPrefix: "//"
  130. )
  131. }
  132. return header
  133. }
  134. }