-
Notifications
You must be signed in to change notification settings - Fork 435
[CodeGen Protobuf support] SwiftProtobuf parser to CodeGenerationRequest #1772
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
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.
| var documentation = String() | ||
| // Field number used to collect .proto file comments. | ||
| let documentationPath = IndexPath(index: 12) | ||
| if let syntaxLocation = input.sourceCodeInfoLocation(path: documentationPath) { | ||
| documentation = syntaxLocation.asSourceComment( | ||
| commentPrefix: "///", | ||
| leadingDetachedPrefix: "//" | ||
| ) | ||
| // If the was a leading or tailing comment it won't have a blank | ||
| // line, after it, so ensure there is one. | ||
| if !documentation.isEmpty && !documentation.hasSuffix("\n\n") { | ||
| documentation.append("\n") | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This part is taken from here https://github.com/apple/swift-protobuf/blob/ea9ac1c4831d14118372ce7e1dcebd142c74e7f3/Sources/protoc-gen-swift/FileGenerator.swift#L71. Apparently there are fixed path numbers for different things that appear in a proto file - 12 is for the header. Should I add to NOTICES that I've used this small piece of code from SwiftProtobuf or should I add some link in the comment above the line containing the index?
| @@ -0,0 +1,122 @@ | |||
| /* | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This shouldn't be in this module, this module is for protobuf components which are used at runtime (i.e. when running a client or server). This is used at code generation time so should be in its own module.
| import struct GRPCCodeGen.CodeGenerationRequest | ||
|
|
||
| /// Parses a ``FileDescriptor`` object into a ``CodeGenerationRequest`` object. | ||
| public struct InputParser { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we can use a more specific name here.
| var header = String() | ||
| // Field number used to collect the .proto file header. | ||
| let headerPath = IndexPath(index: 12) | ||
| if let headerLocation = input.sourceCodeInfoLocation(path: headerPath) { | ||
| header = headerLocation.asSourceComment( | ||
| commentPrefix: "///", | ||
| leadingDetachedPrefix: "//" | ||
| ) | ||
| // If the was a leading or tailing comment it won't have a blank | ||
| // line, after it, so ensure there is one. | ||
| if !header.isEmpty && !header.hasSuffix("\n\n") { | ||
| header.append("\n") | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this would make more sense as an extension on FileDescriptor
| // DO NOT EDIT. | ||
| // swift-format-ignore-file | ||
| // | ||
| // Generated by the Swift generator plugin for the protocol buffer compiler. | ||
| // Source: \(fileName) | ||
| // | ||
| // For information on using the generated types, please see the documentation: | ||
| // https://github.com/apple/swift-protobuf/\n\n |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is from Swift Protobuf and points to the protobuf docs which isn't right.
| let dependencies = input.dependencies.map { | ||
| CodeGenerationRequest.Dependency(module: $0.name) | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The dependencies should include GRPCProtobuf as well because that's where the serializer/deserializer are imported from.
| ) | ||
| } | ||
|
|
||
| fileprivate func createCodeGenerationServices( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: use make instead of create (https://www.swift.org/documentation/api-design-guidelines/#strive-for-fluent-usage), also this creates service descriptors, not services
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In general though, I think this is worth moving these to be conversion functions on the init of the descriptors, it makes them much easier to test in isolation:
extension CodeGenerationRequest.ServiceDescriptor {
init(descriptor: ServiceDescriptor, package: String) {
// ...
}
}
extension CodeGenerationRequest.ServiceDescriptor.MethodDescriptor {
init(descriptor: ServiceDescriptor.MethodDescriptor) {
// ...
}
}Then this function boils down to something like:
let descriptors = input.services.map { descriptor in
CoreGenerationRequest.ServiceDescriptor(descriptor: descriptor, package: package)
}| // Copyright 2015 gRPC authors.\n//\n// Licensed under the Apache License, \ | ||
| Version 2.0 (the \"License\");\n// you may not use this file except in \ | ||
| compliance with the License.\n// You may obtain a copy of the License \ | ||
| at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by \ | ||
| applicable law or agreed to in writing, software\n// distributed under \ | ||
| the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES \ | ||
| OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License \ | ||
| for the specific language governing permissions and\n// limitations under the License.\n\n |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this somewhat misses the point of multiline string literals..., you don't need to put in "\n" manually, you just write the text as you'd like it. The \ at the end of each line stops the newline being inserted.
| ) | ||
| } | ||
|
|
||
| var dummyFileDescriptor: FileDescriptor { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not really a dummy file descriptor, it's the hello world file descriptor
| XCTAssertEqual( | ||
| parsedCodeGenRequest.services[0].methods[0].documentation, | ||
| "/// Sends a greeting.\n" | ||
| ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we pull out the first service? I.e. guard let service = request.services.first else { return XCTFail() }
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also we should validate the number of services and methods is as expected
Package.swift
Outdated
| static let reflectionService: Self = .target(name: "GRPCReflectionService") | ||
| static let grpcCodeGen: Self = .target(name: "GRPCCodeGen") | ||
| static let grpcProtobuf: Self = .target(name: "GRPCProtobuf") | ||
| static let grpcProtobufParser: Self = .target(name: "GRPCProtobufParser") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we call this GRPCProtobufCodeGen -- it will do more than parsing
| // DO NOT EDIT. | ||
| // swift-format-ignore-file | ||
| // | ||
| // Generated by the Swift generator plugin for the protocol buffer compiler. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| // Generated by the Swift generator plugin for the protocol buffer compiler. | |
| // 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\n\n |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| // https://github.com/grpc/grpc-swift\n\n | |
| // https://github.com/grpc/grpc-swift | |
| ``` |
| } | ||
|
|
||
| extension FileDescriptor { | ||
| fileprivate func getHeader() -> String { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you have get in your name you probably want a computed property instead...
| // Ensuring there is a blank line after the header. | ||
| if !header.isEmpty && !header.hasSuffix("\n\n") { | ||
| header.append("\n") | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This logic is separate from getting the header, looks like it should be in parse
| // 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, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| // distributed under the License is distributed on an \"AS IS\" BASIS, | |
| // distributed under the License is distributed on an "AS IS" BASIS, |
|
|
||
| return CodeGenerationRequest( | ||
| fileName: input.name, | ||
| leadingTrivia: leadingTrivia + header, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be the other way around, any copyright header etc. should come first
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we want all this whitespace in the leading trivia.
| import struct GRPCCodeGen.CodeGenerationRequest | ||
|
|
||
| /// Parses a ``FileDescriptor`` object into a ``CodeGenerationRequest`` object. | ||
| public struct ProtobufToCodeGenParser { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Given we now have this extra module we can put more of the logic in it when constructing protoc-gen-grpc-swift, and have protoc-gen-grpc-swift basically do nothing apart from parsing the options and calling the generator we provide in this module. The upshot of this is that I don't think this type needs to be public.
| let services = input.services.map { | ||
| CodeGenerationRequest.ServiceDescriptor(descriptor: $0, package: input.package) | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This whole flow is much easier to follow!
…est (grpc#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 <gbarnett@apple.com>
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:
Result:
FileDescriptors can now be transformed into CodeGenerationRequest, which will
be useful in implementing the
protoc-gen-grpc-swiftv2.