Options.swift 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  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 GRPCCodeGen
  17. import GRPCProtobufCodeGen
  18. import SwiftProtobufPluginLibrary
  19. enum GenerationError: Error, CustomStringConvertible {
  20. /// Raised when parsing the parameter string and found an unknown key
  21. case unknownParameter(name: String)
  22. /// Raised when a parameter was giving an invalid value
  23. case invalidParameterValue(name: String, value: String)
  24. /// Raised to wrap another error but provide a context message.
  25. case wrappedError(message: String, error: any Error)
  26. /// The parameter isn't supported.
  27. case unsupportedParameter(name: String, message: String)
  28. var description: String {
  29. switch self {
  30. case let .unknownParameter(name):
  31. return "Unknown generation parameter '\(name)'"
  32. case let .invalidParameterValue(name, value):
  33. return "Unknown value for generation parameter '\(name)': '\(value)'"
  34. case let .wrappedError(message, error):
  35. return "\(message): \(error)"
  36. case let .unsupportedParameter(name, message):
  37. return "Unsupported parameter '\(name)': \(message)"
  38. }
  39. }
  40. }
  41. enum FileNaming: String {
  42. case fullPath = "FullPath"
  43. case pathToUnderscores = "PathToUnderscores"
  44. case dropPath = "DropPath"
  45. }
  46. struct GeneratorOptions {
  47. private(set) var protoToModuleMappings = ProtoFileToModuleMappings()
  48. private(set) var fileNaming = FileNaming.fullPath
  49. private(set) var extraModuleImports: [String] = []
  50. private(set) var config: ProtobufCodeGenerator.Config = .defaults
  51. init(parameter: any CodeGeneratorParameter) throws {
  52. try self.init(pairs: parameter.parsedPairs)
  53. }
  54. init(pairs: [(key: String, value: String)]) throws {
  55. for pair in pairs {
  56. switch pair.key {
  57. case "Visibility":
  58. if let value = GRPCCodeGen.CodeGenerator.Config.AccessLevel(protocOption: pair.value) {
  59. self.config.accessLevel = value
  60. } else {
  61. throw GenerationError.invalidParameterValue(name: pair.key, value: pair.value)
  62. }
  63. case "Server":
  64. if let value = Bool(pair.value.lowercased()) {
  65. self.config.generateServer = value
  66. } else {
  67. throw GenerationError.invalidParameterValue(name: pair.key, value: pair.value)
  68. }
  69. case "Client":
  70. if let value = Bool(pair.value.lowercased()) {
  71. self.config.generateClient = value
  72. } else {
  73. throw GenerationError.invalidParameterValue(name: pair.key, value: pair.value)
  74. }
  75. case "ProtoPathModuleMappings":
  76. if !pair.value.isEmpty {
  77. do {
  78. self.protoToModuleMappings = try ProtoFileToModuleMappings(path: pair.value)
  79. } catch let e {
  80. throw GenerationError.wrappedError(
  81. message: "Parameter 'ProtoPathModuleMappings=\(pair.value)'",
  82. error: e
  83. )
  84. }
  85. }
  86. case "FileNaming":
  87. if let value = FileNaming(rawValue: pair.value) {
  88. self.fileNaming = value
  89. } else {
  90. throw GenerationError.invalidParameterValue(name: pair.key, value: pair.value)
  91. }
  92. case "ExtraModuleImports":
  93. if !pair.value.isEmpty {
  94. self.extraModuleImports.append(pair.value)
  95. } else {
  96. throw GenerationError.invalidParameterValue(name: pair.key, value: pair.value)
  97. }
  98. case "GRPCModuleName":
  99. if !pair.value.isEmpty {
  100. self.config.moduleNames.grpcCore = pair.value
  101. } else {
  102. throw GenerationError.invalidParameterValue(name: pair.key, value: pair.value)
  103. }
  104. case "GRPCProtobufModuleName":
  105. if !pair.value.isEmpty {
  106. self.config.moduleNames.grpcProtobuf = pair.value
  107. } else {
  108. throw GenerationError.invalidParameterValue(name: pair.key, value: pair.value)
  109. }
  110. case "SwiftProtobufModuleName":
  111. if !pair.value.isEmpty {
  112. self.config.moduleNames.swiftProtobuf = pair.value
  113. } else {
  114. throw GenerationError.invalidParameterValue(name: pair.key, value: pair.value)
  115. }
  116. case "ReflectionData":
  117. throw GenerationError.unsupportedParameter(
  118. name: pair.key,
  119. message: """
  120. The reflection service uses descriptor sets. Refer to the protoc docs and the \
  121. '--descriptor_set_out' option for more information.
  122. """
  123. )
  124. case "UseAccessLevelOnImports":
  125. if let value = Bool(pair.value.lowercased()) {
  126. self.config.accessLevelOnImports = value
  127. } else {
  128. throw GenerationError.invalidParameterValue(name: pair.key, value: pair.value)
  129. }
  130. default:
  131. throw GenerationError.unknownParameter(name: pair.key)
  132. }
  133. }
  134. }
  135. static func parseParameter(string: String?) -> [(key: String, value: String)] {
  136. guard let string = string, !string.isEmpty else {
  137. return []
  138. }
  139. let parts = string.split(separator: ",")
  140. // Partitions the string into the section before the = and after the =
  141. let result = parts.map { string -> (key: String, value: String) in
  142. // Finds the equal sign and exits early if none
  143. guard let index = string.firstIndex(of: "=") else {
  144. return (String(string), "")
  145. }
  146. // Creates key/value pair and trims whitespace
  147. let key = string[..<index]
  148. .trimmingWhitespaceAndNewlines()
  149. let value = string[string.index(after: index)...]
  150. .trimmingWhitespaceAndNewlines()
  151. return (key: key, value: value)
  152. }
  153. return result
  154. }
  155. }
  156. extension String.SubSequence {
  157. func trimmingWhitespaceAndNewlines() -> String {
  158. let trimmedSuffix = self.drop(while: { $0.isNewline || $0.isWhitespace })
  159. let trimmed = trimmedSuffix.trimmingPrefix(while: { $0.isNewline || $0.isWhitespace })
  160. return String(trimmed)
  161. }
  162. }
  163. extension GRPCCodeGen.CodeGenerator.Config.AccessLevel {
  164. fileprivate init?(protocOption value: String) {
  165. switch value {
  166. case "Internal":
  167. self = .internal
  168. case "Public":
  169. self = .public
  170. case "Package":
  171. self = .package
  172. default:
  173. return nil
  174. }
  175. }
  176. }