IDLToStructuredSwiftTranslator.swift 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  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. struct IDLToStructuredSwiftTranslator: Translator {
  17. func translate(
  18. codeGenerationRequest: CodeGenerationRequest,
  19. client: Bool,
  20. server: Bool
  21. ) throws -> StructuredSwiftRepresentation {
  22. try self.validateInput(codeGenerationRequest)
  23. let typealiasTranslator = TypealiasTranslator(client: client, server: server)
  24. let topComment = Comment.doc(codeGenerationRequest.leadingTrivia)
  25. let imports = try codeGenerationRequest.dependencies.reduce(
  26. into: [ImportDescription(moduleName: "GRPCCore")]
  27. ) { partialResult, newDependency in
  28. try partialResult.append(translateImport(dependency: newDependency))
  29. }
  30. var codeBlocks: [CodeBlock] = []
  31. codeBlocks.append(
  32. contentsOf: try typealiasTranslator.translate(from: codeGenerationRequest)
  33. )
  34. if server {
  35. let serverCodeTranslator = ServerCodeTranslator()
  36. codeBlocks.append(
  37. contentsOf: try serverCodeTranslator.translate(from: codeGenerationRequest)
  38. )
  39. }
  40. if client {
  41. let clientCodeTranslator = ClientCodeTranslator()
  42. codeBlocks.append(
  43. contentsOf: try clientCodeTranslator.translate(from: codeGenerationRequest)
  44. )
  45. }
  46. let fileDescription = FileDescription(
  47. topComment: topComment,
  48. imports: imports,
  49. codeBlocks: codeBlocks
  50. )
  51. let fileName = String(codeGenerationRequest.fileName.split(separator: ".")[0])
  52. let file = NamedFileDescription(name: fileName, contents: fileDescription)
  53. return StructuredSwiftRepresentation(file: file)
  54. }
  55. }
  56. extension IDLToStructuredSwiftTranslator {
  57. private func translateImport(
  58. dependency: CodeGenerationRequest.Dependency
  59. ) throws -> ImportDescription {
  60. var importDescription = ImportDescription(moduleName: dependency.module)
  61. if let item = dependency.item {
  62. if let matchedKind = ImportDescription.Kind(rawValue: item.kind.value.rawValue) {
  63. importDescription.item = ImportDescription.Item(kind: matchedKind, name: item.name)
  64. } else {
  65. throw CodeGenError(
  66. code: .invalidKind,
  67. message: "Invalid kind name for import: \(item.kind.value.rawValue)"
  68. )
  69. }
  70. }
  71. if let spi = dependency.spi {
  72. importDescription.spi = spi
  73. }
  74. switch dependency.preconcurrency.value {
  75. case .required:
  76. importDescription.preconcurrency = .always
  77. case .notRequired:
  78. importDescription.preconcurrency = .never
  79. case .requiredOnOS(let OSs):
  80. importDescription.preconcurrency = .onOS(OSs)
  81. }
  82. return importDescription
  83. }
  84. private func validateInput(_ codeGenerationRequest: CodeGenerationRequest) throws {
  85. let servicesByNamespace = Dictionary(
  86. grouping: codeGenerationRequest.services,
  87. by: { $0.namespace }
  88. )
  89. try self.checkServiceNamesAreUnique(for: servicesByNamespace)
  90. for service in codeGenerationRequest.services {
  91. try self.checkMethodNamesAreUnique(in: service)
  92. }
  93. }
  94. // Verify service names are unique within each namespace and that services with no namespace
  95. // don't have the same names as any of the namespaces.
  96. private func checkServiceNamesAreUnique(
  97. for servicesByNamespace: [String: [CodeGenerationRequest.ServiceDescriptor]]
  98. ) throws {
  99. // Check that if there are services in an empty namespace, none have names which match other namespaces
  100. if let noNamespaceServices = servicesByNamespace[""] {
  101. let namespaces = servicesByNamespace.keys
  102. for service in noNamespaceServices {
  103. if namespaces.contains(service.name) {
  104. throw CodeGenError(
  105. code: .nonUniqueServiceName,
  106. message: """
  107. Services with no namespace must not have the same names as the namespaces. \
  108. \(service.name) is used as a name for a service with no namespace and a namespace.
  109. """
  110. )
  111. }
  112. }
  113. }
  114. // Check that service names are unique within each namespace.
  115. for (namespace, services) in servicesByNamespace {
  116. var serviceNames: Set<String> = []
  117. for service in services {
  118. if serviceNames.contains(service.name) {
  119. let errorMessage: String
  120. if namespace.isEmpty {
  121. errorMessage = """
  122. Services in an empty namespace must have unique names. \
  123. \(service.name) is used as a name for multiple services without namespaces.
  124. """
  125. } else {
  126. errorMessage = """
  127. Services within the same namespace must have unique names. \
  128. \(service.name) is used as a name for multiple services in the \(service.namespace) namespace.
  129. """
  130. }
  131. throw CodeGenError(
  132. code: .nonUniqueServiceName,
  133. message: errorMessage
  134. )
  135. }
  136. serviceNames.insert(service.name)
  137. }
  138. }
  139. }
  140. // Verify method names are unique for the service.
  141. private func checkMethodNamesAreUnique(
  142. in service: CodeGenerationRequest.ServiceDescriptor
  143. ) throws {
  144. let methodNames = service.methods.map { $0.name }
  145. var seenNames = Set<String>()
  146. for methodName in methodNames {
  147. if seenNames.contains(methodName) {
  148. throw CodeGenError(
  149. code: .nonUniqueMethodName,
  150. message: """
  151. Methods of a service must have unique names. \
  152. \(methodName) is used as a name for multiple methods of the \(service.name) service.
  153. """
  154. )
  155. }
  156. seenNames.insert(methodName)
  157. }
  158. }
  159. }
  160. extension CodeGenerationRequest.ServiceDescriptor {
  161. var namespacedTypealiasPrefix: String {
  162. if self.namespace.isEmpty {
  163. return self.name
  164. } else {
  165. return "\(self.namespace).\(self.name)"
  166. }
  167. }
  168. var namespacedPrefix: String {
  169. if self.namespace.isEmpty {
  170. return self.name
  171. } else {
  172. return "\(self.namespace)_\(self.name)"
  173. }
  174. }
  175. }