/* * 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 { internal init() {} internal func parse(input: FileDescriptor) throws -> CodeGenerationRequest { var header = 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: \(input.name) // // For information on using the generated types, please see the documentation: // https://github.com/grpc/grpc-swift """ var dependencies = 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 = input.services.map { CodeGenerationRequest.ServiceDescriptor(descriptor: $0, package: input.package) } return CodeGenerationRequest( fileName: input.name, leadingTrivia: header + leadingTrivia, dependencies: dependencies, services: services, lookupSerializer: lookupSerializer, lookupDeserializer: lookupDeserializer ) } } extension CodeGenerationRequest.ServiceDescriptor { fileprivate init(descriptor: ServiceDescriptor, package: String) { let methods = descriptor.methods.map { CodeGenerationRequest.ServiceDescriptor.MethodDescriptor(descriptor: $0) } 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) { 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: descriptor.inputType.name, outputType: descriptor.outputType.name ) } } 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 } }