Skip to content
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

Created the CodeGenerationRequest #1716

Merged
merged 5 commits into from
Nov 24, 2023
Merged
Show file tree
Hide file tree
Changes from 2 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
6 changes: 6 additions & 0 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ extension Target.Dependency {
static let grpc: Self = .target(name: grpcTargetName)
static let cgrpcZlib: Self = .target(name: cgrpcZlibTargetName)
static let protocGenGRPCSwift: Self = .target(name: "protoc-gen-grpc-swift")
static let grpcCodeGen: Self = .target(name: "GRPCCodeGen")

// Target dependencies; internal
static let grpcSampleData: Self = .target(name: "GRPCSampleData")
Expand Down Expand Up @@ -477,6 +478,11 @@ extension Target {
.copy("Generated")
]
)

static let grpcCodeGen: Target = .target(
name: "GRPCCodeGen",
path: "Sources/GRPCCodeGen"
)
}

// MARK: - Products
Expand Down
124 changes: 124 additions & 0 deletions Sources/GRPCCodeGen/CodeGenerationRequest.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
/*
* Copyright 2023, 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.
*/

/// Describes the services, dependencies and trivia from an IDL file,
/// and the IDL itself through its specific serializer and deserializer.
public struct CodeGenerationRequest {
glbrntt marked this conversation as resolved.
Show resolved Hide resolved
/// The name of the source file containing the IDL. It contains the file's extension.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not all files have extensions:

Suggested change
/// The name of the source file containing the IDL. It contains the file's extension.
/// The name of the source file containing the IDL, including the extension if applicable.

var fileName: String

/// The Swift imports that the generated file should depend on.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// The Swift imports that the generated file should depend on.
/// The Swift imports that the generated file depends on.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you clarify in the docs that gRPC imports aren't required because we'll add those?

///
/// - SeeAlso: ``Dependency``.
var dependencies: [Dependency] = []

/// Any comments at the top of the file including documentation and copyright headers.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They don't have to include these things, we're just stating examples of what they'll be.

Suggested change
/// Any comments at the top of the file including documentation and copyright headers.
/// Any comments at the top of the file such as documentation and copyright headers.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you also add a note here saying that we'll place these verbatim at the top of the generated file?

var leadingTrivia: String

/// An array of service descriptors.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't tell me anything more than [ServiceDescriptor] tells me (i.e. what the thing is). If you say "A description of each service to generate." then it tells the user what it's used for.

///
/// - SeeAlso: ``ServiceDescriptor``.
var services: [ServiceDescriptor] = []

/// Closure that receives the message type and returns a string representation of the
/// serializer call for that message type. The result is inserted in the string representing
/// the generated code, where clients or servers serialize their output.
///
/// For example: `lookupSerializer: {"ProtobufSerializer<\($0)>()"}`.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Docs should start with a single sentence summary and add more detail in other paragraphs. Tools like Xcode and DocC show the first paragraph more prominently so it's quite important for it to be concise.

We can also be more specific about the requirements here: the serializer type must conform to MessageSerializer.

Suggested change
/// Closure that receives the message type and returns a string representation of the
/// serializer call for that message type. The result is inserted in the string representing
/// the generated code, where clients or servers serialize their output.
///
/// For example: `lookupSerializer: {"ProtobufSerializer<\($0)>()"}`.
/// Closure that receives a message type as a `String` and returns a code snippet to
/// initialize a `MessageSerializer` for that type as a `String`.
///
/// The result is inserted in the generated code, where clients serialize RPC inputs and
/// servers serialize RPC outputs.
///
/// For example, to serialize Protobuf messages you could specify a serializer as:
/// ```swift
/// request.lookupSerializer = { messageType in
/// "ProtobufSerializer<\(messageType)>()"
/// }
/// ```

var lookupSerializer: (String) -> String
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See "Label tuple members and name closure parameters" https://www.swift.org/documentation/api-design-guidelines/#special-instructions:

Suggested change
var lookupSerializer: (String) -> String
var lookupSerializer: (_ messageType: String) -> String


/// Closure that receives the message type and returns a string representation of the
/// deserializer call for that message type. The result is inserted in the string representing
/// the generated code, where clients or servers deserialize their input.
///
/// For example: `lookupDeserializer: {"ProtobufDeserializer<\($0)>()"}`.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs a similar treatment to the one above, but the deserializer must conform to MessageDeserializer.

var lookupDeserializer: (String) -> String

/// Represents an import: a module or a specific item from a module.
public struct Dependency {
/// If the dependency is an item, the property's value is the item representation.
/// If the dependency is a module, this property is nil.
var item: Item? = nil

/// The name of the imported module or of the module an item is imported from.
var module: String

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

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// The keyword that specifies the item's kind (e.g. `func`, `struct`).
/// The keyword that specifies the item's kind (e.g. `func`, `struct`).

var kind: Kind

/// The imported item's symbol / name.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// The imported item's symbol / name.
/// The name of the imported item.

var name: String

/// Represents the imported item's kind.
public struct Kind {
/// One of the possible keywords associated to the imported item's kind.
var keyword: Keyword

public init(_ keyword: Keyword) {
self.keyword = keyword
}

internal enum Keyword {
case `typealias`
case `struct`
case `class`
case `enum`
case `protocol`
case `let`
case `var`
case `func`
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What want Kind to be is an enum, but we can't do that because adding a new case to an enum is an API breaking change. Instead we can have a struct backed by an enum; internally we can have access to the enum so we can switch over its values. However, publicly there needs to be API to create each enum case. The usual pattern is like this:

public struct Kind {
  internal enum Value {
    case foo
    case bar
  }

  internal var value: Value
  internal init(_ value: Value) { 
    self.value = value
  }

  public static var foo: Self { 
    Self(.foo) 
  }

  public static var bar: Self { 
    Self(.bar) 
  }
}

}
}

/// Represents a service described in an IDL file.
struct ServiceDescriptor {
/// Documentation from comments above the IDL service description.
var documentation: String

/// Service name.
glbrntt marked this conversation as resolved.
Show resolved Hide resolved
var name: String

/// Array of descriptors for the methods of a service.
///
/// - SeeAlso: ``MethodDescriptor``.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment as on services, this doesn't tell me anything more than [MethodDescriptor].

var methods: [MethodDescriptor] = []

/// Represents a method described in an IDL file.
struct MethodDescriptor {
/// Documentation from comments above the IDL method description.
var documentation: String

/// Method name.
var name: String

/// Identifies if the method is input streaming.
var isInputStreaming: Bool

/// Identifies if the method is output streaming.
var isOutputStreaming: Bool

/// The generated input type for the described method.
var inputType: String

/// The generated output type for the described method.
var ouputType: String
}
}
}