Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 26 additions & 3 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ extension Target.Dependency {
static let reflectionService: Self = .target(name: "GRPCReflectionService")
static let grpcCodeGen: Self = .target(name: "GRPCCodeGen")
static let grpcProtobuf: Self = .target(name: "GRPCProtobuf")
static let grpcProtobufCodeGen: Self = .target(name: "GRPCProtobufCodeGen")

// Target dependencies; internal
static let grpcSampleData: Self = .target(name: "GRPCSampleData")
Expand Down Expand Up @@ -235,6 +236,7 @@ extension Target {
dependencies: [
.protobuf,
.protobufPluginLibrary,
.grpcCodeGen
],
exclude: [
"README.md",
Expand Down Expand Up @@ -341,12 +343,22 @@ extension Target {
static let grpcProtobufTests: Target = .testTarget(
name: "GRPCProtobufTests",
dependencies: [
.grpcCore,
.grpcProtobuf,
.grpcCore,
.protobuf
]
)

static let grpcProtobufCodeGenTests: Target = .testTarget(
name: "GRPCProtobufCodeGenTests",
dependencies: [
.grpcCodeGen,
.grpcProtobufCodeGen,
.protobuf,
.protobufPluginLibrary
]
)

static let interopTestModels: Target = .target(
name: "GRPCInteroperabilityTestModels",
dependencies: [
Expand Down Expand Up @@ -601,10 +613,19 @@ extension Target {
name: "GRPCProtobuf",
dependencies: [
.grpcCore,
.protobuf
.protobuf,
],
path: "Sources/GRPCProtobuf"
)
static let grpcProtobufCodeGen: Target = .target(
name: "GRPCProtobufCodeGen",
dependencies: [
.protobuf,
.protobufPluginLibrary,
.grpcCodeGen
],
path: "Sources/GRPCProtobufCodeGen"
)
}

// MARK: - Products
Expand Down Expand Up @@ -693,6 +714,7 @@ let package = Package(
.grpcHTTP2TransportNIOPosix,
.grpcHTTP2TransportNIOTransportServices,
.grpcProtobuf,
.grpcProtobufCodeGen,

// v2 tests
.grpcCoreTests,
Expand All @@ -702,7 +724,8 @@ let package = Package(
.grpcHTTP2CoreTests,
.grpcHTTP2TransportNIOPosixTests,
.grpcHTTP2TransportNIOTransportServicesTests,
.grpcProtobufTests
.grpcProtobufTests,
.grpcProtobufCodeGenTests
]
)

Expand Down
10 changes: 5 additions & 5 deletions Sources/GRPCCodeGen/CodeGenerationRequest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ public struct CodeGenerationRequest {
}

/// Represents an import: a module or a specific item from a module.
public struct Dependency {
public struct Dependency: Equatable {
/// If the dependency is an item, the property's value is the item representation.
/// If the dependency is a module, this property is nil.
public var item: Item? = nil
Expand Down Expand Up @@ -111,7 +111,7 @@ public struct CodeGenerationRequest {
}

/// Represents an item imported from a module.
public struct Item {
public struct Item: Equatable {
/// The keyword that specifies the item's kind (e.g. `func`, `struct`).
public var kind: Kind

Expand All @@ -124,7 +124,7 @@ public struct CodeGenerationRequest {
}

/// Represents the imported item's kind.
public struct Kind {
public struct Kind: Equatable {
/// Describes the keyword associated with the imported item.
internal enum Value: String {
case `typealias`
Expand Down Expand Up @@ -186,8 +186,8 @@ public struct CodeGenerationRequest {
}

/// Describes any requirement for the `@preconcurrency` attribute.
public struct PreconcurrencyRequirement {
internal enum Value {
public struct PreconcurrencyRequirement: Equatable {
internal enum Value: Equatable {
case required
case notRequired
case requiredOnOS([String])
Expand Down
123 changes: 123 additions & 0 deletions Sources/GRPCProtobufCodeGen/ProtobufCodeGenParser.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
/*
* 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
}
}
Loading