CodeGenerationRequest.swift 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483
  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.makeSerializerCodeSnippet = { messageType in
  43. /// "ProtobufSerializer<\(messageType)>()"
  44. /// }
  45. /// ```
  46. public var makeSerializerCodeSnippet: (_ 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.makeDeserializerCodeSnippet = { messageType in
  56. /// "ProtobufDeserializer<\(messageType)>()"
  57. /// }
  58. /// ```
  59. public var makeDeserializerCodeSnippet: (_ messageType: String) -> String
  60. public init(
  61. fileName: String,
  62. leadingTrivia: String,
  63. dependencies: [Dependency],
  64. services: [ServiceDescriptor],
  65. makeSerializerCodeSnippet: @escaping (_ messageType: String) -> String,
  66. makeDeserializerCodeSnippet: @escaping (_ messageType: String) -> String
  67. ) {
  68. self.fileName = fileName
  69. self.leadingTrivia = leadingTrivia
  70. self.dependencies = dependencies
  71. self.services = services
  72. self.makeSerializerCodeSnippet = makeSerializerCodeSnippet
  73. self.makeDeserializerCodeSnippet = makeDeserializerCodeSnippet
  74. }
  75. }
  76. extension CodeGenerationRequest {
  77. @available(*, deprecated, renamed: "makeSerializerSnippet")
  78. public var lookupSerializer: (_ messageType: String) -> String {
  79. get { self.makeSerializerCodeSnippet }
  80. set { self.makeSerializerCodeSnippet = newValue }
  81. }
  82. @available(*, deprecated, renamed: "makeDeserializerSnippet")
  83. public var lookupDeserializer: (_ messageType: String) -> String {
  84. get { self.makeDeserializerCodeSnippet }
  85. set { self.makeDeserializerCodeSnippet = newValue }
  86. }
  87. @available(
  88. *,
  89. deprecated,
  90. renamed:
  91. "init(fileName:leadingTrivia:dependencies:services:lookupSerializer:lookupDeserializer:)"
  92. )
  93. public init(
  94. fileName: String,
  95. leadingTrivia: String,
  96. dependencies: [Dependency],
  97. services: [ServiceDescriptor],
  98. lookupSerializer: @escaping (String) -> String,
  99. lookupDeserializer: @escaping (String) -> String
  100. ) {
  101. self.init(
  102. fileName: fileName,
  103. leadingTrivia: leadingTrivia,
  104. dependencies: dependencies,
  105. services: services,
  106. makeSerializerCodeSnippet: lookupSerializer,
  107. makeDeserializerCodeSnippet: lookupDeserializer
  108. )
  109. }
  110. }
  111. /// Represents an import: a module or a specific item from a module.
  112. public struct Dependency: Equatable {
  113. /// If the dependency is an item, the property's value is the item representation.
  114. /// If the dependency is a module, this property is nil.
  115. public var item: Item?
  116. /// The access level to be included in imports of this dependency.
  117. public var accessLevel: CodeGenerator.Config.AccessLevel
  118. /// The name of the imported module or of the module an item is imported from.
  119. public var module: String
  120. /// The name of the private interface for an `@_spi` import.
  121. ///
  122. /// For example, if `spi` was "Secret" and the module name was "Foo" then the import
  123. /// would be `@_spi(Secret) import Foo`.
  124. public var spi: String?
  125. /// Requirements for the `@preconcurrency` attribute.
  126. public var preconcurrency: PreconcurrencyRequirement
  127. public init(
  128. item: Item? = nil,
  129. module: String,
  130. spi: String? = nil,
  131. preconcurrency: PreconcurrencyRequirement = .notRequired,
  132. accessLevel: CodeGenerator.Config.AccessLevel
  133. ) {
  134. self.item = item
  135. self.module = module
  136. self.spi = spi
  137. self.preconcurrency = preconcurrency
  138. self.accessLevel = accessLevel
  139. }
  140. /// Represents an item imported from a module.
  141. public struct Item: Equatable {
  142. /// The keyword that specifies the item's kind (e.g. `func`, `struct`).
  143. public var kind: Kind
  144. /// The name of the imported item.
  145. public var name: String
  146. public init(kind: Kind, name: String) {
  147. self.kind = kind
  148. self.name = name
  149. }
  150. /// Represents the imported item's kind.
  151. public struct Kind: Equatable {
  152. /// Describes the keyword associated with the imported item.
  153. internal enum Value: String {
  154. case `typealias`
  155. case `struct`
  156. case `class`
  157. case `enum`
  158. case `protocol`
  159. case `let`
  160. case `var`
  161. case `func`
  162. }
  163. internal var value: Value
  164. internal init(_ value: Value) {
  165. self.value = value
  166. }
  167. /// The imported item is a typealias.
  168. public static var `typealias`: Self {
  169. Self(.`typealias`)
  170. }
  171. /// The imported item is a struct.
  172. public static var `struct`: Self {
  173. Self(.`struct`)
  174. }
  175. /// The imported item is a class.
  176. public static var `class`: Self {
  177. Self(.`class`)
  178. }
  179. /// The imported item is an enum.
  180. public static var `enum`: Self {
  181. Self(.`enum`)
  182. }
  183. /// The imported item is a protocol.
  184. public static var `protocol`: Self {
  185. Self(.`protocol`)
  186. }
  187. /// The imported item is a let.
  188. public static var `let`: Self {
  189. Self(.`let`)
  190. }
  191. /// The imported item is a var.
  192. public static var `var`: Self {
  193. Self(.`var`)
  194. }
  195. /// The imported item is a function.
  196. public static var `func`: Self {
  197. Self(.`func`)
  198. }
  199. }
  200. }
  201. /// Describes any requirement for the `@preconcurrency` attribute.
  202. public struct PreconcurrencyRequirement: Equatable {
  203. internal enum Value: Equatable {
  204. case required
  205. case notRequired
  206. case requiredOnOS([String])
  207. }
  208. internal var value: Value
  209. internal init(_ value: Value) {
  210. self.value = value
  211. }
  212. /// The attribute is always required.
  213. public static var required: Self {
  214. Self(.required)
  215. }
  216. /// The attribute is not required.
  217. public static var notRequired: Self {
  218. Self(.notRequired)
  219. }
  220. /// The attribute is required only on the named operating systems.
  221. public static func requiredOnOS(_ OSs: [String]) -> PreconcurrencyRequirement {
  222. return Self(.requiredOnOS(OSs))
  223. }
  224. }
  225. }
  226. /// Represents a service described in an IDL file.
  227. public struct ServiceDescriptor: Hashable {
  228. /// Documentation from comments above the IDL service description.
  229. /// It is already formatted, meaning it contains "///" and new lines.
  230. public var documentation: String
  231. /// The name of the service.
  232. public var name: ServiceName
  233. /// A description of each method of a service.
  234. ///
  235. /// - SeeAlso: ``MethodDescriptor``.
  236. public var methods: [MethodDescriptor]
  237. public init(
  238. documentation: String,
  239. name: ServiceName,
  240. methods: [MethodDescriptor]
  241. ) {
  242. self.documentation = documentation
  243. self.name = name
  244. self.methods = methods
  245. }
  246. }
  247. extension ServiceDescriptor {
  248. @available(*, deprecated, renamed: "init(documentation:name:methods:)")
  249. public init(
  250. documentation: String,
  251. name: Name,
  252. namespace: Name,
  253. methods: [MethodDescriptor]
  254. ) {
  255. self.documentation = documentation
  256. self.methods = methods
  257. let identifier = namespace.base.isEmpty ? name.base : namespace.base + "." + name.base
  258. let typeName =
  259. namespace.generatedUpperCase.isEmpty
  260. ? name.generatedUpperCase
  261. : namespace.generatedUpperCase + "_" + name.generatedUpperCase
  262. let propertyName =
  263. namespace.generatedLowerCase.isEmpty
  264. ? name.generatedUpperCase
  265. : namespace.generatedLowerCase + "_" + name.generatedUpperCase
  266. self.name = ServiceName(
  267. identifyingName: identifier,
  268. typeName: typeName,
  269. propertyName: propertyName
  270. )
  271. }
  272. }
  273. /// Represents a method described in an IDL file.
  274. public struct MethodDescriptor: Hashable {
  275. /// Documentation from comments above the IDL method description.
  276. /// It is already formatted, meaning it contains "///" and new lines.
  277. public var documentation: String
  278. /// Method name in different formats.
  279. ///
  280. /// All properties of this object must be unique for each method
  281. /// from within a service.
  282. public var name: MethodName
  283. /// Identifies if the method is input streaming.
  284. public var isInputStreaming: Bool
  285. /// Identifies if the method is output streaming.
  286. public var isOutputStreaming: Bool
  287. /// The generated input type for the described method.
  288. public var inputType: String
  289. /// The generated output type for the described method.
  290. public var outputType: String
  291. public init(
  292. documentation: String,
  293. name: MethodName,
  294. isInputStreaming: Bool,
  295. isOutputStreaming: Bool,
  296. inputType: String,
  297. outputType: String
  298. ) {
  299. self.documentation = documentation
  300. self.name = name
  301. self.isInputStreaming = isInputStreaming
  302. self.isOutputStreaming = isOutputStreaming
  303. self.inputType = inputType
  304. self.outputType = outputType
  305. }
  306. }
  307. extension MethodDescriptor {
  308. @available(*, deprecated, message: "Use MethodName instead of Name")
  309. public init(
  310. documentation: String,
  311. name: Name,
  312. isInputStreaming: Bool,
  313. isOutputStreaming: Bool,
  314. inputType: String,
  315. outputType: String
  316. ) {
  317. self.documentation = documentation
  318. self.name = MethodName(
  319. identifyingName: name.base,
  320. typeName: name.generatedUpperCase,
  321. functionName: name.generatedLowerCase
  322. )
  323. self.isInputStreaming = isInputStreaming
  324. self.isOutputStreaming = isOutputStreaming
  325. self.inputType = inputType
  326. self.outputType = outputType
  327. }
  328. }
  329. public struct ServiceName: Hashable {
  330. /// The identifying name as used in the service/method descriptors including any namespace.
  331. ///
  332. /// This value is also used to identify the service to the remote peer, usually as part of the
  333. /// ":path" pseudoheader if doing gRPC over HTTP/2.
  334. ///
  335. /// If the service is declared in package "foo.bar" and the service is called "Baz" then this
  336. /// value should be "foo.bar.Baz".
  337. public var identifyingName: String
  338. /// The name as used on types including any namespace.
  339. ///
  340. /// This is used to generate a namespace for each service which contains a number of client and
  341. /// server protocols and concrete types.
  342. ///
  343. /// If the service is declared in package "foo.bar" and the service is called "Baz" then this
  344. /// value should be "Foo\_Bar\_Baz".
  345. public var typeName: String
  346. /// The name as used as a property.
  347. ///
  348. /// This is used to provide a convenience getter for a descriptor of the service.
  349. ///
  350. /// If the service is declared in package "foo.bar" and the service is called "Baz" then this
  351. /// value should be "foo\_bar\_Baz".
  352. public var propertyName: String
  353. public init(identifyingName: String, typeName: String, propertyName: String) {
  354. self.identifyingName = identifyingName
  355. self.typeName = typeName
  356. self.propertyName = propertyName
  357. }
  358. }
  359. public struct MethodName: Hashable {
  360. /// The identifying name as used in the service/method descriptors.
  361. ///
  362. /// This value is also used to identify the method to the remote peer, usually as part of the
  363. /// ":path" pseudoheader if doing gRPC over HTTP/2.
  364. ///
  365. /// This value typically starts with an uppercase character, for example "Get".
  366. public var identifyingName: String
  367. /// The name as used on types including any namespace.
  368. ///
  369. /// This is used to generate a namespace for each method which contains information about
  370. /// the method.
  371. ///
  372. /// This value typically starts with an uppercase character, for example "Get".
  373. public var typeName: String
  374. /// The name as used as a property.
  375. ///
  376. /// This value typically starts with an lowercase character, for example "get".
  377. public var functionName: String
  378. public init(identifyingName: String, typeName: String, functionName: String) {
  379. self.identifyingName = identifyingName
  380. self.typeName = typeName
  381. self.functionName = functionName
  382. }
  383. }
  384. /// Represents the name associated with a namespace, service or a method, in three different formats.
  385. @available(*, deprecated, message: "Use ServiceName/MethodName instead.")
  386. public struct Name: Hashable {
  387. /// The base name is the name used for the namespace/service/method in the IDL file, so it should follow
  388. /// the specific casing of the IDL.
  389. ///
  390. /// The base name is also used in the descriptors that identify a specific method or service :
  391. /// `<service_namespace_baseName>.<service_baseName>.<method_baseName>`.
  392. public var base: String
  393. /// The `generatedUpperCase` name is used in the generated code. It is expected
  394. /// to be the UpperCamelCase version of the base name
  395. ///
  396. /// For example, if `base` is "fooBar", then `generatedUpperCase` is "FooBar".
  397. public var generatedUpperCase: String
  398. /// The `generatedLowerCase` name is used in the generated code. It is expected
  399. /// to be the lowerCamelCase version of the base name
  400. ///
  401. /// For example, if `base` is "FooBar", then `generatedLowerCase` is "fooBar".
  402. public var generatedLowerCase: String
  403. public init(base: String, generatedUpperCase: String, generatedLowerCase: String) {
  404. self.base = base
  405. self.generatedUpperCase = generatedUpperCase
  406. self.generatedLowerCase = generatedLowerCase
  407. }
  408. }
  409. @available(*, deprecated, message: "Use ServiceName/MethodName instead.")
  410. extension Name {
  411. /// The base name replacing occurrences of "." with "_".
  412. ///
  413. /// For example, if `base` is "Foo.Bar", then `normalizedBase` is "Foo_Bar".
  414. public var normalizedBase: String {
  415. return self.base.replacing(".", with: "_")
  416. }
  417. }