CodeGenerationRequest.swift 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  1. /*
  2. * Copyright 2023, 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. /// Describes the services, dependencies and trivia from an IDL file,
  17. /// and the IDL itself through its specific serializer and deserializer.
  18. public struct CodeGenerationRequest {
  19. /// The name of the source file containing the IDL, including the extension if applicable.
  20. public var fileName: String
  21. /// Any comments at the top of the file such as documentation and copyright headers.
  22. /// They will be placed at the top of the generated file. They are already formatted,
  23. /// meaning they contain "///" and new lines.
  24. public var leadingTrivia: String
  25. /// The Swift imports that the generated file depends on. The gRPC specific imports aren't required
  26. /// as they will be added by default in the generated file.
  27. ///
  28. /// - SeeAlso: ``Dependency``.
  29. public var dependencies: [Dependency]
  30. /// A description of each service to generate.
  31. ///
  32. /// - SeeAlso: ``ServiceDescriptor``.
  33. public var services: [ServiceDescriptor]
  34. /// Closure that receives a message type as a `String` and returns a code snippet to
  35. /// initialise a `MessageSerializer` for that type as a `String`.
  36. ///
  37. /// The result is inserted in the generated code, where clients serialize RPC inputs and
  38. /// servers serialize RPC outputs.
  39. ///
  40. /// For example, to serialize Protobuf messages you could specify a serializer as:
  41. /// ```swift
  42. /// request.lookupSerializer = { messageType in
  43. /// "ProtobufSerializer<\(messageType)>()"
  44. /// }
  45. /// ```
  46. public var lookupSerializer: (_ messageType: String) -> String
  47. /// Closure that receives a message type as a `String` and returns a code snippet to
  48. /// initialize a `MessageDeserializer` for that type as a `String`.
  49. ///
  50. /// The result is inserted in the generated code, where clients deserialize RPC outputs and
  51. /// servers deserialize RPC inputs.
  52. ///
  53. /// For example, to serialize Protobuf messages you could specify a serializer as:
  54. /// ```swift
  55. /// request.lookupDeserializer = { messageType in
  56. /// "ProtobufDeserializer<\(messageType)>()"
  57. /// }
  58. /// ```
  59. public var lookupDeserializer: (_ messageType: String) -> String
  60. public init(
  61. fileName: String,
  62. leadingTrivia: String,
  63. dependencies: [Dependency],
  64. services: [ServiceDescriptor],
  65. lookupSerializer: @escaping (String) -> String,
  66. lookupDeserializer: @escaping (String) -> String
  67. ) {
  68. self.fileName = fileName
  69. self.leadingTrivia = leadingTrivia
  70. self.dependencies = dependencies
  71. self.services = services
  72. self.lookupSerializer = lookupSerializer
  73. self.lookupDeserializer = lookupDeserializer
  74. }
  75. }
  76. /// Represents an import: a module or a specific item from a module.
  77. public struct Dependency: Equatable {
  78. /// If the dependency is an item, the property's value is the item representation.
  79. /// If the dependency is a module, this property is nil.
  80. public var item: Item?
  81. /// The access level to be included in imports of this dependency.
  82. public var accessLevel: SourceGenerator.Config.AccessLevel
  83. /// The name of the imported module or of the module an item is imported from.
  84. public var module: String
  85. /// The name of the private interface for an `@_spi` import.
  86. ///
  87. /// For example, if `spi` was "Secret" and the module name was "Foo" then the import
  88. /// would be `@_spi(Secret) import Foo`.
  89. public var spi: String?
  90. /// Requirements for the `@preconcurrency` attribute.
  91. public var preconcurrency: PreconcurrencyRequirement
  92. public init(
  93. item: Item? = nil,
  94. module: String,
  95. spi: String? = nil,
  96. preconcurrency: PreconcurrencyRequirement = .notRequired,
  97. accessLevel: SourceGenerator.Config.AccessLevel
  98. ) {
  99. self.item = item
  100. self.module = module
  101. self.spi = spi
  102. self.preconcurrency = preconcurrency
  103. self.accessLevel = accessLevel
  104. }
  105. /// Represents an item imported from a module.
  106. public struct Item: Equatable {
  107. /// The keyword that specifies the item's kind (e.g. `func`, `struct`).
  108. public var kind: Kind
  109. /// The name of the imported item.
  110. public var name: String
  111. public init(kind: Kind, name: String) {
  112. self.kind = kind
  113. self.name = name
  114. }
  115. /// Represents the imported item's kind.
  116. public struct Kind: Equatable {
  117. /// Describes the keyword associated with the imported item.
  118. internal enum Value: String {
  119. case `typealias`
  120. case `struct`
  121. case `class`
  122. case `enum`
  123. case `protocol`
  124. case `let`
  125. case `var`
  126. case `func`
  127. }
  128. internal var value: Value
  129. internal init(_ value: Value) {
  130. self.value = value
  131. }
  132. /// The imported item is a typealias.
  133. public static var `typealias`: Self {
  134. Self(.`typealias`)
  135. }
  136. /// The imported item is a struct.
  137. public static var `struct`: Self {
  138. Self(.`struct`)
  139. }
  140. /// The imported item is a class.
  141. public static var `class`: Self {
  142. Self(.`class`)
  143. }
  144. /// The imported item is an enum.
  145. public static var `enum`: Self {
  146. Self(.`enum`)
  147. }
  148. /// The imported item is a protocol.
  149. public static var `protocol`: Self {
  150. Self(.`protocol`)
  151. }
  152. /// The imported item is a let.
  153. public static var `let`: Self {
  154. Self(.`let`)
  155. }
  156. /// The imported item is a var.
  157. public static var `var`: Self {
  158. Self(.`var`)
  159. }
  160. /// The imported item is a function.
  161. public static var `func`: Self {
  162. Self(.`func`)
  163. }
  164. }
  165. }
  166. /// Describes any requirement for the `@preconcurrency` attribute.
  167. public struct PreconcurrencyRequirement: Equatable {
  168. internal enum Value: Equatable {
  169. case required
  170. case notRequired
  171. case requiredOnOS([String])
  172. }
  173. internal var value: Value
  174. internal init(_ value: Value) {
  175. self.value = value
  176. }
  177. /// The attribute is always required.
  178. public static var required: Self {
  179. Self(.required)
  180. }
  181. /// The attribute is not required.
  182. public static var notRequired: Self {
  183. Self(.notRequired)
  184. }
  185. /// The attribute is required only on the named operating systems.
  186. public static func requiredOnOS(_ OSs: [String]) -> PreconcurrencyRequirement {
  187. return Self(.requiredOnOS(OSs))
  188. }
  189. }
  190. }
  191. /// Represents a service described in an IDL file.
  192. public struct ServiceDescriptor: Hashable {
  193. /// Documentation from comments above the IDL service description.
  194. /// It is already formatted, meaning it contains "///" and new lines.
  195. public var documentation: String
  196. /// The service name in different formats.
  197. ///
  198. /// All properties of this object must be unique for each service from within a namespace.
  199. public var name: Name
  200. /// The service namespace in different formats.
  201. ///
  202. /// All different services from within the same namespace must have
  203. /// the same ``Name`` object as this property.
  204. /// For `.proto` files the base name of this object is the package name.
  205. public var namespace: Name
  206. /// A description of each method of a service.
  207. ///
  208. /// - SeeAlso: ``MethodDescriptor``.
  209. public var methods: [MethodDescriptor]
  210. public init(
  211. documentation: String,
  212. name: Name,
  213. namespace: Name,
  214. methods: [MethodDescriptor]
  215. ) {
  216. self.documentation = documentation
  217. self.name = name
  218. self.namespace = namespace
  219. self.methods = methods
  220. }
  221. }
  222. /// Represents a method described in an IDL file.
  223. public struct MethodDescriptor: Hashable {
  224. /// Documentation from comments above the IDL method description.
  225. /// It is already formatted, meaning it contains "///" and new lines.
  226. public var documentation: String
  227. /// Method name in different formats.
  228. ///
  229. /// All properties of this object must be unique for each method
  230. /// from within a service.
  231. public var name: Name
  232. /// Identifies if the method is input streaming.
  233. public var isInputStreaming: Bool
  234. /// Identifies if the method is output streaming.
  235. public var isOutputStreaming: Bool
  236. /// The generated input type for the described method.
  237. public var inputType: String
  238. /// The generated output type for the described method.
  239. public var outputType: String
  240. public init(
  241. documentation: String,
  242. name: Name,
  243. isInputStreaming: Bool,
  244. isOutputStreaming: Bool,
  245. inputType: String,
  246. outputType: String
  247. ) {
  248. self.documentation = documentation
  249. self.name = name
  250. self.isInputStreaming = isInputStreaming
  251. self.isOutputStreaming = isOutputStreaming
  252. self.inputType = inputType
  253. self.outputType = outputType
  254. }
  255. }
  256. /// Represents the name associated with a namespace, service or a method, in three different formats.
  257. public struct Name: Hashable {
  258. /// The base name is the name used for the namespace/service/method in the IDL file, so it should follow
  259. /// the specific casing of the IDL.
  260. ///
  261. /// The base name is also used in the descriptors that identify a specific method or service :
  262. /// `<service_namespace_baseName>.<service_baseName>.<method_baseName>`.
  263. public var base: String
  264. /// The `generatedUpperCase` name is used in the generated code. It is expected
  265. /// to be the UpperCamelCase version of the base name
  266. ///
  267. /// For example, if `base` is "fooBar", then `generatedUpperCase` is "FooBar".
  268. public var generatedUpperCase: String
  269. /// The `generatedLowerCase` name is used in the generated code. It is expected
  270. /// to be the lowerCamelCase version of the base name
  271. ///
  272. /// For example, if `base` is "FooBar", then `generatedLowerCase` is "fooBar".
  273. public var generatedLowerCase: String
  274. public init(base: String, generatedUpperCase: String, generatedLowerCase: String) {
  275. self.base = base
  276. self.generatedUpperCase = generatedUpperCase
  277. self.generatedLowerCase = generatedLowerCase
  278. }
  279. }
  280. extension Name {
  281. /// The base name replacing occurrences of "." with "_".
  282. ///
  283. /// For example, if `base` is "Foo.Bar", then `normalizedBase` is "Foo_Bar".
  284. public var normalizedBase: String {
  285. return self.base.replacing(".", with: "_")
  286. }
  287. }
  288. extension ServiceDescriptor {
  289. var namespacedServicePropertyName: String {
  290. let prefix: String
  291. if self.namespace.normalizedBase.isEmpty {
  292. prefix = ""
  293. } else {
  294. prefix = self.namespace.normalizedBase + "_"
  295. }
  296. return prefix + self.name.normalizedBase
  297. }
  298. }