IDLToStructuredSwiftTranslator.swift 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289
  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. /// Creates a representation for the server and client code, as well as for the enums containing useful type aliases and properties.
  17. /// The representation is generated based on the ``CodeGenerationRequest`` object and user specifications,
  18. /// using types from ``StructuredSwiftRepresentation``.
  19. package struct IDLToStructuredSwiftTranslator {
  20. package init() {}
  21. func translate(
  22. codeGenerationRequest: CodeGenerationRequest,
  23. accessLevel: CodeGenerator.Config.AccessLevel,
  24. accessLevelOnImports: Bool,
  25. client: Bool,
  26. server: Bool,
  27. grpcCoreModuleName: String,
  28. availability: AvailabilityDescription
  29. ) throws -> StructuredSwiftRepresentation {
  30. try self.validateInput(codeGenerationRequest)
  31. let accessModifier = AccessModifier(accessLevel)
  32. var codeBlocks: [CodeBlock] = []
  33. let metadataTranslator = MetadataTranslator()
  34. let serverTranslator = ServerCodeTranslator()
  35. let clientTranslator = ClientCodeTranslator()
  36. let namer = Namer(grpcCore: grpcCoreModuleName)
  37. for service in codeGenerationRequest.services {
  38. codeBlocks.append(
  39. CodeBlock(comment: .mark("\(service.name.identifyingName)", sectionBreak: true))
  40. )
  41. let metadata = metadataTranslator.translate(
  42. accessModifier: accessModifier,
  43. service: service,
  44. availability: availability,
  45. namer: namer
  46. )
  47. codeBlocks.append(contentsOf: metadata)
  48. if server {
  49. codeBlocks.append(
  50. CodeBlock(comment: .mark("\(service.name.identifyingName) (server)", sectionBreak: false))
  51. )
  52. let blocks = serverTranslator.translate(
  53. accessModifier: accessModifier,
  54. service: service,
  55. availability: availability,
  56. namer: namer,
  57. serializer: codeGenerationRequest.makeSerializerCodeSnippet,
  58. deserializer: codeGenerationRequest.makeDeserializerCodeSnippet
  59. )
  60. codeBlocks.append(contentsOf: blocks)
  61. }
  62. if client {
  63. codeBlocks.append(
  64. CodeBlock(comment: .mark("\(service.name.identifyingName) (client)", sectionBreak: false))
  65. )
  66. let blocks = clientTranslator.translate(
  67. accessModifier: accessModifier,
  68. service: service,
  69. availability: availability,
  70. namer: namer,
  71. serializer: codeGenerationRequest.makeSerializerCodeSnippet,
  72. deserializer: codeGenerationRequest.makeDeserializerCodeSnippet
  73. )
  74. codeBlocks.append(contentsOf: blocks)
  75. }
  76. }
  77. let imports: [ImportDescription]?
  78. if codeGenerationRequest.services.isEmpty {
  79. imports = nil
  80. codeBlocks.append(
  81. CodeBlock(comment: .inline("This file contained no services."))
  82. )
  83. } else {
  84. imports = try self.makeImports(
  85. dependencies: codeGenerationRequest.dependencies,
  86. accessLevel: accessLevel,
  87. accessLevelOnImports: accessLevelOnImports,
  88. grpcCoreModuleName: grpcCoreModuleName
  89. )
  90. }
  91. let fileDescription = FileDescription(
  92. topComment: .preFormatted(codeGenerationRequest.leadingTrivia),
  93. imports: imports,
  94. codeBlocks: codeBlocks
  95. )
  96. let fileName = String(codeGenerationRequest.fileName.split(separator: ".")[0])
  97. let file = NamedFileDescription(name: fileName, contents: fileDescription)
  98. return StructuredSwiftRepresentation(file: file)
  99. }
  100. package func makeImports(
  101. dependencies: [Dependency],
  102. accessLevel: CodeGenerator.Config.AccessLevel,
  103. accessLevelOnImports: Bool,
  104. grpcCoreModuleName: String
  105. ) throws -> [ImportDescription] {
  106. var imports: [ImportDescription] = []
  107. imports.append(
  108. ImportDescription(
  109. accessLevel: accessLevelOnImports ? AccessModifier(accessLevel) : nil,
  110. moduleName: grpcCoreModuleName
  111. )
  112. )
  113. for dependency in dependencies {
  114. let importDescription = try self.translateImport(
  115. dependency: dependency,
  116. accessLevelOnImports: accessLevelOnImports
  117. )
  118. imports.append(importDescription)
  119. }
  120. return imports
  121. }
  122. }
  123. extension AccessModifier {
  124. init(_ accessLevel: CodeGenerator.Config.AccessLevel) {
  125. switch accessLevel.level {
  126. case .internal: self = .internal
  127. case .package: self = .package
  128. case .public: self = .public
  129. }
  130. }
  131. }
  132. extension IDLToStructuredSwiftTranslator {
  133. private func translateImport(
  134. dependency: Dependency,
  135. accessLevelOnImports: Bool
  136. ) throws -> ImportDescription {
  137. var importDescription = ImportDescription(
  138. accessLevel: accessLevelOnImports ? AccessModifier(dependency.accessLevel) : nil,
  139. moduleName: dependency.module
  140. )
  141. if let item = dependency.item {
  142. if let matchedKind = ImportDescription.Kind(rawValue: item.kind.value.rawValue) {
  143. importDescription.item = ImportDescription.Item(kind: matchedKind, name: item.name)
  144. } else {
  145. throw CodeGenError(
  146. code: .invalidKind,
  147. message: "Invalid kind name for import: \(item.kind.value.rawValue)"
  148. )
  149. }
  150. }
  151. if let spi = dependency.spi {
  152. importDescription.spi = spi
  153. }
  154. switch dependency.preconcurrency.value {
  155. case .required:
  156. importDescription.preconcurrency = .always
  157. case .notRequired:
  158. importDescription.preconcurrency = .never
  159. case .requiredOnOS(let OSs):
  160. importDescription.preconcurrency = .onOS(OSs)
  161. }
  162. return importDescription
  163. }
  164. private func validateInput(_ codeGenerationRequest: CodeGenerationRequest) throws {
  165. try self.checkServiceDescriptorsAreUnique(codeGenerationRequest.services)
  166. let servicesByGeneratedEnumName = Dictionary(
  167. grouping: codeGenerationRequest.services,
  168. by: { $0.name.typeName }
  169. )
  170. try self.checkServiceEnumNamesAreUnique(for: servicesByGeneratedEnumName)
  171. for service in codeGenerationRequest.services {
  172. try self.checkMethodNamesAreUnique(in: service)
  173. }
  174. }
  175. // Verify service enum names are unique.
  176. private func checkServiceEnumNamesAreUnique(
  177. for servicesByGeneratedEnumName: [String: [ServiceDescriptor]]
  178. ) throws {
  179. for (generatedEnumName, services) in servicesByGeneratedEnumName {
  180. if services.count > 1 {
  181. throw CodeGenError(
  182. code: .nonUniqueServiceName,
  183. message: """
  184. There must be a unique (namespace, service_name) pair for each service. \
  185. \(generatedEnumName) is used as a <namespace>_<service_name> construction for multiple services.
  186. """
  187. )
  188. }
  189. }
  190. }
  191. // Verify method names are unique within a service.
  192. private func checkMethodNamesAreUnique(
  193. in service: ServiceDescriptor
  194. ) throws {
  195. // Check that the method descriptors are unique, by checking that the base names
  196. // of the methods of a specific service are unique.
  197. let baseNames = service.methods.map { $0.name.identifyingName }
  198. if let duplicatedBase = baseNames.getFirstDuplicate() {
  199. throw CodeGenError(
  200. code: .nonUniqueMethodName,
  201. message: """
  202. Methods of a service must have unique base names. \
  203. \(duplicatedBase) is used as a base name for multiple methods of the \(service.name.identifyingName) service.
  204. """
  205. )
  206. }
  207. // Check that generated upper case names for methods are unique within a service, to ensure that
  208. // the enums containing type aliases for each method of a service.
  209. let upperCaseNames = service.methods.map { $0.name.typeName }
  210. if let duplicatedGeneratedUpperCase = upperCaseNames.getFirstDuplicate() {
  211. throw CodeGenError(
  212. code: .nonUniqueMethodName,
  213. message: """
  214. Methods of a service must have unique generated upper case names. \
  215. \(duplicatedGeneratedUpperCase) is used as a generated upper case name for multiple methods of the \(service.name.identifyingName) service.
  216. """
  217. )
  218. }
  219. // Check that generated lower case names for methods are unique within a service, to ensure that
  220. // the function declarations and definitions from the same protocols and extensions have unique names.
  221. let lowerCaseNames = service.methods.map { $0.name.functionName }
  222. if let duplicatedLowerCase = lowerCaseNames.getFirstDuplicate() {
  223. throw CodeGenError(
  224. code: .nonUniqueMethodName,
  225. message: """
  226. Methods of a service must have unique lower case names. \
  227. \(duplicatedLowerCase) is used as a signature name for multiple methods of the \(service.name.identifyingName) service.
  228. """
  229. )
  230. }
  231. }
  232. private func checkServiceDescriptorsAreUnique(
  233. _ services: [ServiceDescriptor]
  234. ) throws {
  235. var descriptors: Set<String> = []
  236. for service in services {
  237. let name = service.name.identifyingName
  238. let (inserted, _) = descriptors.insert(name)
  239. if !inserted {
  240. throw CodeGenError(
  241. code: .nonUniqueServiceName,
  242. message: """
  243. Services must have unique descriptors. \
  244. \(name) is the descriptor of at least two different services.
  245. """
  246. )
  247. }
  248. }
  249. }
  250. }
  251. extension [String] {
  252. internal func getFirstDuplicate() -> String? {
  253. var seen = Set<String>()
  254. for element in self {
  255. if seen.contains(element) {
  256. return element
  257. }
  258. seen.insert(element)
  259. }
  260. return nil
  261. }
  262. }