Skip to content

Commit 373d9f4

Browse files
[CodeGen Protobuf support] SwiftProtobuf parser to CodeGenerationRequest (#1772)
Motivation: We need the parser to transform a FileDescriptor object into a CodeGenerationRequest objet which can be used as input in the new code generation process. Modifications: - Created the parser struct - Created a test for the type Result: FileDescriptors can now be transformed into CodeGenerationRequest, which will be useful in implementing the `protoc-gen-grpc-swift` v2. --------- Co-authored-by: George Barnett <[email protected]>
1 parent a6802a8 commit 373d9f4

File tree

4 files changed

+347
-8
lines changed

4 files changed

+347
-8
lines changed

Package.swift

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ extension Target.Dependency {
9494
static let reflectionService: Self = .target(name: "GRPCReflectionService")
9595
static let grpcCodeGen: Self = .target(name: "GRPCCodeGen")
9696
static let grpcProtobuf: Self = .target(name: "GRPCProtobuf")
97+
static let grpcProtobufCodeGen: Self = .target(name: "GRPCProtobufCodeGen")
9798

9899
// Target dependencies; internal
99100
static let grpcSampleData: Self = .target(name: "GRPCSampleData")
@@ -235,6 +236,7 @@ extension Target {
235236
dependencies: [
236237
.protobuf,
237238
.protobufPluginLibrary,
239+
.grpcCodeGen
238240
],
239241
exclude: [
240242
"README.md",
@@ -341,12 +343,22 @@ extension Target {
341343
static let grpcProtobufTests: Target = .testTarget(
342344
name: "GRPCProtobufTests",
343345
dependencies: [
344-
.grpcCore,
345346
.grpcProtobuf,
347+
.grpcCore,
346348
.protobuf
347349
]
348350
)
349351

352+
static let grpcProtobufCodeGenTests: Target = .testTarget(
353+
name: "GRPCProtobufCodeGenTests",
354+
dependencies: [
355+
.grpcCodeGen,
356+
.grpcProtobufCodeGen,
357+
.protobuf,
358+
.protobufPluginLibrary
359+
]
360+
)
361+
350362
static let interopTestModels: Target = .target(
351363
name: "GRPCInteroperabilityTestModels",
352364
dependencies: [
@@ -601,10 +613,19 @@ extension Target {
601613
name: "GRPCProtobuf",
602614
dependencies: [
603615
.grpcCore,
604-
.protobuf
616+
.protobuf,
605617
],
606618
path: "Sources/GRPCProtobuf"
607619
)
620+
static let grpcProtobufCodeGen: Target = .target(
621+
name: "GRPCProtobufCodeGen",
622+
dependencies: [
623+
.protobuf,
624+
.protobufPluginLibrary,
625+
.grpcCodeGen
626+
],
627+
path: "Sources/GRPCProtobufCodeGen"
628+
)
608629
}
609630

610631
// MARK: - Products
@@ -693,6 +714,7 @@ let package = Package(
693714
.grpcHTTP2TransportNIOPosix,
694715
.grpcHTTP2TransportNIOTransportServices,
695716
.grpcProtobuf,
717+
.grpcProtobufCodeGen,
696718

697719
// v2 tests
698720
.grpcCoreTests,
@@ -702,7 +724,8 @@ let package = Package(
702724
.grpcHTTP2CoreTests,
703725
.grpcHTTP2TransportNIOPosixTests,
704726
.grpcHTTP2TransportNIOTransportServicesTests,
705-
.grpcProtobufTests
727+
.grpcProtobufTests,
728+
.grpcProtobufCodeGenTests
706729
]
707730
)
708731

Sources/GRPCCodeGen/CodeGenerationRequest.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ public struct CodeGenerationRequest {
8181
}
8282

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

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

@@ -124,7 +124,7 @@ public struct CodeGenerationRequest {
124124
}
125125

126126
/// Represents the imported item's kind.
127-
public struct Kind {
127+
public struct Kind: Equatable {
128128
/// Describes the keyword associated with the imported item.
129129
internal enum Value: String {
130130
case `typealias`
@@ -186,8 +186,8 @@ public struct CodeGenerationRequest {
186186
}
187187

188188
/// Describes any requirement for the `@preconcurrency` attribute.
189-
public struct PreconcurrencyRequirement {
190-
internal enum Value {
189+
public struct PreconcurrencyRequirement: Equatable {
190+
internal enum Value: Equatable {
191191
case required
192192
case notRequired
193193
case requiredOnOS([String])
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
/*
2+
* Copyright 2024, 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+
17+
import Foundation
18+
import SwiftProtobuf
19+
import SwiftProtobufPluginLibrary
20+
21+
import struct GRPCCodeGen.CodeGenerationRequest
22+
23+
/// Parses a ``FileDescriptor`` object into a ``CodeGenerationRequest`` object.
24+
internal struct ProtobufCodeGenParser {
25+
internal init() {}
26+
internal func parse(input: FileDescriptor) throws -> CodeGenerationRequest {
27+
var header = input.header
28+
// Ensuring there is a blank line after the header.
29+
if !header.isEmpty && !header.hasSuffix("\n\n") {
30+
header.append("\n")
31+
}
32+
let leadingTrivia = """
33+
// DO NOT EDIT.
34+
// swift-format-ignore-file
35+
//
36+
// Generated by the gRPC Swift generator plugin for the protocol buffer compiler.
37+
// Source: \(input.name)
38+
//
39+
// For information on using the generated types, please see the documentation:
40+
// https://github.com/grpc/grpc-swift
41+
42+
"""
43+
var dependencies = input.dependencies.map {
44+
CodeGenerationRequest.Dependency(module: $0.name)
45+
}
46+
dependencies.append(CodeGenerationRequest.Dependency(module: "GRPCProtobuf"))
47+
let lookupSerializer: (String) -> String = { messageType in
48+
"ProtobufSerializer<\(messageType)>()"
49+
}
50+
let lookupDeserializer: (String) -> String = { messageType in
51+
"ProtobufDeserializer<\(messageType)>()"
52+
}
53+
let services = input.services.map {
54+
CodeGenerationRequest.ServiceDescriptor(descriptor: $0, package: input.package)
55+
}
56+
57+
return CodeGenerationRequest(
58+
fileName: input.name,
59+
leadingTrivia: header + leadingTrivia,
60+
dependencies: dependencies,
61+
services: services,
62+
lookupSerializer: lookupSerializer,
63+
lookupDeserializer: lookupDeserializer
64+
)
65+
}
66+
}
67+
68+
extension CodeGenerationRequest.ServiceDescriptor {
69+
fileprivate init(descriptor: ServiceDescriptor, package: String) {
70+
let methods = descriptor.methods.map {
71+
CodeGenerationRequest.ServiceDescriptor.MethodDescriptor(descriptor: $0)
72+
}
73+
let name = CodeGenerationRequest.Name(
74+
base: descriptor.name,
75+
generatedUpperCase: NamingUtils.toUpperCamelCase(descriptor.name),
76+
generatedLowerCase: NamingUtils.toLowerCamelCase(descriptor.name)
77+
)
78+
let namespace = CodeGenerationRequest.Name(
79+
base: package,
80+
generatedUpperCase: NamingUtils.toUpperCamelCase(package),
81+
generatedLowerCase: NamingUtils.toLowerCamelCase(package)
82+
)
83+
let documentation = descriptor.protoSourceComments()
84+
self.init(documentation: documentation, name: name, namespace: namespace, methods: methods)
85+
}
86+
}
87+
88+
extension CodeGenerationRequest.ServiceDescriptor.MethodDescriptor {
89+
fileprivate init(descriptor: MethodDescriptor) {
90+
let name = CodeGenerationRequest.Name(
91+
base: descriptor.name,
92+
generatedUpperCase: NamingUtils.toUpperCamelCase(descriptor.name),
93+
generatedLowerCase: NamingUtils.toLowerCamelCase(descriptor.name)
94+
)
95+
let documentation = descriptor.protoSourceComments()
96+
self.init(
97+
documentation: documentation,
98+
name: name,
99+
isInputStreaming: descriptor.clientStreaming,
100+
isOutputStreaming: descriptor.serverStreaming,
101+
inputType: descriptor.inputType.name,
102+
outputType: descriptor.outputType.name
103+
)
104+
}
105+
}
106+
107+
extension FileDescriptor {
108+
fileprivate var header: String {
109+
var header = String()
110+
// Field number used to collect the syntax field which is usually the first
111+
// declaration in a.proto file.
112+
// See more here:
113+
// https://github.com/apple/swift-protobuf/blob/main/Protos/SwiftProtobuf/google/protobuf/descriptor.proto
114+
let syntaxPath = IndexPath(index: 12)
115+
if let syntaxLocation = self.sourceCodeInfoLocation(path: syntaxPath) {
116+
header = syntaxLocation.asSourceComment(
117+
commentPrefix: "///",
118+
leadingDetachedPrefix: "//"
119+
)
120+
}
121+
return header
122+
}
123+
}

0 commit comments

Comments
 (0)