IDLToStructuredSwiftTranslator.swift 9.1 KB

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