/* * Copyright 2024, gRPC Authors All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import Foundation import SwiftProtobuf import SwiftProtobufPluginLibrary import struct GRPCCodeGen.CodeGenerationRequest /// Parses a ``FileDescriptor`` object into a ``CodeGenerationRequest`` object. internal struct ProtobufCodeGenParser { let input: FileDescriptor let namer: SwiftProtobufNamer internal init(input: FileDescriptor, protoFileModuleMappings: ProtoFileToModuleMappings) { self.input = input self.namer = SwiftProtobufNamer( currentFile: input, protoFileToModuleMappings: protoFileModuleMappings ) } internal func parse() throws -> CodeGenerationRequest { var header = self.input.header // Ensuring there is a blank line after the header. if !header.isEmpty && !header.hasSuffix("\n\n") { header.append("\n") } let leadingTrivia = """ // DO NOT EDIT. // swift-format-ignore-file // // Generated by the gRPC Swift generator plugin for the protocol buffer compiler. // Source: \(self.input.name) // // For information on using the generated types, please see the documentation: // https://github.com/grpc/grpc-swift """ var dependencies = self.input.dependencies.map { CodeGenerationRequest.Dependency(module: $0.name) } dependencies.append(CodeGenerationRequest.Dependency(module: "GRPCProtobuf")) let lookupSerializer: (String) -> String = { messageType in "ProtobufSerializer<\(messageType)>()" } let lookupDeserializer: (String) -> String = { messageType in "ProtobufDeserializer<\(messageType)>()" } let services = self.input.services.map { CodeGenerationRequest.ServiceDescriptor( descriptor: $0, package: input.package, protobufNamer: self.namer ) } return CodeGenerationRequest( fileName: self.input.name, leadingTrivia: header + leadingTrivia, dependencies: dependencies, services: services, lookupSerializer: lookupSerializer, lookupDeserializer: lookupDeserializer ) } } extension CodeGenerationRequest.ServiceDescriptor { fileprivate init( descriptor: ServiceDescriptor, package: String, protobufNamer: SwiftProtobufNamer ) { let methods = descriptor.methods.map { CodeGenerationRequest.ServiceDescriptor.MethodDescriptor( descriptor: $0, protobufNamer: protobufNamer ) } let name = CodeGenerationRequest.Name( base: descriptor.name, generatedUpperCase: NamingUtils.toUpperCamelCase(descriptor.name), generatedLowerCase: NamingUtils.toLowerCamelCase(descriptor.name) ) let namespace = CodeGenerationRequest.Name( base: package, generatedUpperCase: NamingUtils.toUpperCamelCase(package), generatedLowerCase: NamingUtils.toLowerCamelCase(package) ) let documentation = descriptor.protoSourceComments() self.init(documentation: documentation, name: name, namespace: namespace, methods: methods) } } extension CodeGenerationRequest.ServiceDescriptor.MethodDescriptor { fileprivate init(descriptor: MethodDescriptor, protobufNamer: SwiftProtobufNamer) { let name = CodeGenerationRequest.Name( base: descriptor.name, generatedUpperCase: NamingUtils.toUpperCamelCase(descriptor.name), generatedLowerCase: NamingUtils.toLowerCamelCase(descriptor.name) ) let documentation = descriptor.protoSourceComments() self.init( documentation: documentation, name: name, isInputStreaming: descriptor.clientStreaming, isOutputStreaming: descriptor.serverStreaming, inputType: protobufNamer.fullName(message: descriptor.inputType), outputType: protobufNamer.fullName(message: descriptor.outputType) ) } } extension FileDescriptor { fileprivate var header: String { var header = String() // Field number used to collect the syntax field which is usually the first // declaration in a.proto file. // See more here: // https://github.com/apple/swift-protobuf/blob/main/Protos/SwiftProtobuf/google/protobuf/descriptor.proto let syntaxPath = IndexPath(index: 12) if let syntaxLocation = self.sourceCodeInfoLocation(path: syntaxPath) { header = syntaxLocation.asSourceComment( commentPrefix: "///", leadingDetachedPrefix: "//" ) } return header } }