CodeGenerationRequest.swift 11 KB

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