forked from BigfootDev/flatbuffers
* Migrate to swift 6.0 & swift-gRPC 2.0 The following migrates to swift 6.0, and also migrate to swift-grpc 2.0 that uses swift-nio under the hood to provide nicer API and async await Adds sendable to enum & update @_implementationOnly imports to use internal imports * Address PR comments regarding misspelling & proper method naming.
986 lines
35 KiB
C++
986 lines
35 KiB
C++
/*
|
|
* Copyright 2020 Google Inc. 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.
|
|
*/
|
|
|
|
/*
|
|
* NOTE: The following implementation is a translation for the Swift-grpc
|
|
* generator since flatbuffers doesnt allow plugins for now. if an issue arises
|
|
* please open an issue in the flatbuffers repository. This file should always
|
|
* be maintained according to the Swift-grpc repository
|
|
*/
|
|
#include "src/compiler/swift_generator.h"
|
|
|
|
#include <map>
|
|
#include <sstream>
|
|
|
|
#include "flatbuffers/util.h"
|
|
#include "src/compiler/schema_interface.h"
|
|
|
|
namespace grpc_swift_generator {
|
|
namespace {
|
|
|
|
static std::string ServerResponse() { return "GRPCCore.ServerResponse"; }
|
|
|
|
static std::string ServerRequest() { return "GRPCCore.ServerRequest"; }
|
|
|
|
static std::string StreamingServerRequest() {
|
|
return "GRPCCore.StreamingServerRequest";
|
|
}
|
|
|
|
static std::string StreamingServerResponse() {
|
|
return "GRPCCore.StreamingServerResponse";
|
|
}
|
|
|
|
static std::string QualifiedName(const std::vector<std::string>& components,
|
|
const grpc::string& name,
|
|
const std::string& separator = "_") {
|
|
std::string qualified_name;
|
|
for (auto it = components.begin(); it != components.end(); ++it)
|
|
qualified_name += *it + separator;
|
|
return qualified_name + name;
|
|
}
|
|
|
|
static std::string GenerateGRPCMessage(const std::string& name) {
|
|
return "GRPCMessage<" + name + ">";
|
|
}
|
|
|
|
static std::string GenerateType(const std::string& name,
|
|
const std::string& wrapper) {
|
|
return wrapper + "<" + GenerateGRPCMessage(name) + ">";
|
|
}
|
|
|
|
static std::string GenerateInputMethodTypes(
|
|
const grpc_generator::Method* method) {
|
|
if (method->BidiStreaming()) {
|
|
return StreamingServerRequest();
|
|
} else if (method->ClientStreaming()) {
|
|
return StreamingServerRequest();
|
|
} else if (method->ServerStreaming()) {
|
|
return ServerRequest();
|
|
} else {
|
|
return ServerRequest();
|
|
}
|
|
}
|
|
|
|
static std::string GenerateOutputMethodTypes(
|
|
const grpc_generator::Method* method) {
|
|
if (method->BidiStreaming()) {
|
|
return StreamingServerResponse();
|
|
} else if (method->ClientStreaming()) {
|
|
return ServerResponse();
|
|
} else if (method->ServerStreaming()) {
|
|
return StreamingServerResponse();
|
|
} else {
|
|
return ServerResponse();
|
|
}
|
|
}
|
|
|
|
static std::string GenerateMethodTypes(const grpc_generator::Method* method) {
|
|
if (method->BidiStreaming()) {
|
|
return "bidirectionalStreaming";
|
|
} else if (method->ClientStreaming()) {
|
|
return "clientStreaming";
|
|
} else if (method->ServerStreaming()) {
|
|
return "serverStreaming";
|
|
} else {
|
|
return "unary";
|
|
}
|
|
}
|
|
|
|
void EnforceOSVersion(grpc_generator::Printer* printer) {
|
|
printer->Print(
|
|
"@available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, "
|
|
"*)\n");
|
|
}
|
|
|
|
void GenerateMethodDescriptor(grpc_generator::Printer* printer,
|
|
std::map<grpc::string, grpc::string>* dictionary) {
|
|
auto vars = *dictionary;
|
|
printer->Print(vars, "$ACCESS$ enum $MethodName$: Sendable {\n");
|
|
printer->Indent();
|
|
printer->Print(vars, "$ACCESS$ typealias Input = FlatBufferBuilder\n");
|
|
printer->Print(vars, "$ACCESS$ typealias Output = $Output$\n");
|
|
printer->Print(
|
|
vars, "$ACCESS$ static let descriptor = GRPCCore.MethodDescriptor(\n");
|
|
printer->Indent();
|
|
printer->Print(vars,
|
|
"service: GRPCCore.ServiceDescriptor(fullyQualifiedService: "
|
|
"\"$ServiceQualifiedName$\"),\n");
|
|
printer->Print(vars, "method: \"$MethodName$\"\n");
|
|
printer->Outdent();
|
|
printer->Print(")\n");
|
|
printer->Outdent();
|
|
printer->Print(vars, "}\n");
|
|
}
|
|
|
|
void GenerateCoders(grpc_generator::Printer* printer) {
|
|
EnforceOSVersion(printer);
|
|
printer->Print(
|
|
"extension FlatBuffersMessageSerializer: MessageSerializer {\n");
|
|
printer->Indent();
|
|
printer->Print(
|
|
"public func serialize<Bytes>(_ message: Message) throws -> Bytes where "
|
|
"Bytes : GRPCCore.GRPCContiguousBytes {\n");
|
|
printer->Indent();
|
|
printer->Print("do {\n");
|
|
printer->Indent();
|
|
printer->Print(
|
|
"return try self.serialize(message: message) { GRPCNIOTransportBytes($0) "
|
|
"} as! Bytes\n");
|
|
printer->Outdent();
|
|
printer->Print("} catch let error {\n");
|
|
printer->Indent();
|
|
printer->Print("throw RPCError(\n");
|
|
printer->Indent();
|
|
printer->Print("code: .invalidArgument,\n");
|
|
printer->Print("message: \"Can't serialize message\",\n");
|
|
printer->Print("cause: error\n");
|
|
printer->Outdent();
|
|
printer->Print(")\n");
|
|
printer->Outdent();
|
|
printer->Print("}\n");
|
|
printer->Outdent();
|
|
printer->Print("}\n");
|
|
printer->Outdent();
|
|
printer->Print("}\n\n");
|
|
|
|
EnforceOSVersion(printer);
|
|
printer->Print(
|
|
"extension FlatBuffersMessageDeserializer: MessageDeserializer {\n");
|
|
printer->Indent();
|
|
printer->Print(
|
|
"public func deserialize<Bytes>(_ serializedMessageBytes: Bytes) throws "
|
|
"-> Message where Bytes : GRPCCore.GRPCContiguousBytes {\n");
|
|
printer->Indent();
|
|
printer->Print("do {\n");
|
|
printer->Indent();
|
|
printer->Print("return try serializedMessageBytes.withUnsafeBytes {\n");
|
|
printer->Indent();
|
|
printer->Print("try self.deserialize(pointer: $0)\n");
|
|
printer->Outdent();
|
|
printer->Print("}\n");
|
|
printer->Outdent();
|
|
printer->Print("} catch let error {\n");
|
|
printer->Indent();
|
|
printer->Print("throw RPCError(\n");
|
|
printer->Indent();
|
|
printer->Print("code: .invalidArgument,\n");
|
|
printer->Print(
|
|
"message: \"Can't Decode message of type \\(Message.self)\",\n");
|
|
printer->Print("cause: error\n");
|
|
printer->Outdent();
|
|
printer->Print(")\n");
|
|
printer->Outdent();
|
|
printer->Print("}\n");
|
|
printer->Outdent();
|
|
printer->Print("}\n");
|
|
printer->Outdent();
|
|
printer->Print("}\n\n");
|
|
}
|
|
|
|
void GenerateSharedContent(const grpc_generator::Service* service,
|
|
grpc_generator::Printer* printer,
|
|
std::map<grpc::string, grpc::string>* dictionary) {
|
|
auto vars = *dictionary;
|
|
EnforceOSVersion(printer);
|
|
printer->Print(vars,
|
|
"$ACCESS$ enum $SwiftServiceQualifiedName$: Sendable {\n");
|
|
printer->Indent();
|
|
printer->Print(vars,
|
|
"$ACCESS$ static let descriptor = "
|
|
"GRPCCore.ServiceDescriptor(fullyQualifiedService: "
|
|
"\"$ServiceQualifiedName$\")\n");
|
|
printer->Print(vars, "$ACCESS$ enum Method: Sendable {\n");
|
|
printer->Indent();
|
|
|
|
std::vector<std::string> descriptors;
|
|
for (auto it = 0; it < service->method_count(); it++) {
|
|
auto method = service->method(it);
|
|
vars["Input"] = QualifiedName(method->get_input_namespace_parts(),
|
|
method->get_output_type_name());
|
|
vars["Output"] = QualifiedName(method->get_output_namespace_parts(),
|
|
method->get_output_type_name());
|
|
auto name = method->name();
|
|
vars["MethodName"] = name;
|
|
descriptors.push_back(name);
|
|
GenerateMethodDescriptor(printer, &vars);
|
|
}
|
|
|
|
printer->Print(
|
|
vars,
|
|
"$ACCESS$ static let descriptors: [GRPCCore.MethodDescriptor] = [\n");
|
|
printer->Indent();
|
|
for (auto it = descriptors.begin(); it < descriptors.end(); it++) {
|
|
vars["MethodName"] = *it;
|
|
printer->Print(vars, "$MethodName$.descriptor,\n");
|
|
}
|
|
printer->Outdent();
|
|
printer->Print("]\n");
|
|
printer->Outdent();
|
|
printer->Print("}\n");
|
|
|
|
printer->Outdent();
|
|
printer->Print("}\n\n");
|
|
|
|
EnforceOSVersion(printer);
|
|
printer->Print("extension GRPCCore.ServiceDescriptor {\n");
|
|
printer->Indent();
|
|
printer->Print(vars,
|
|
"$ACCESS$ static let $SwiftServiceQualifiedName$ = "
|
|
"GRPCCore.ServiceDescriptor(fullyQualifiedService: "
|
|
"\"$ServiceQualifiedName$\")\n");
|
|
printer->Outdent();
|
|
printer->Print("}\n");
|
|
}
|
|
|
|
// Service Generation
|
|
|
|
void GenerateFunction(grpc_generator::Printer* printer,
|
|
std::map<grpc::string, grpc::string>* dictionary) {
|
|
auto vars = *dictionary;
|
|
printer->Print(vars, "func $MethodName$(\n");
|
|
printer->Indent();
|
|
|
|
printer->Print(vars, "request: $Input$,\n");
|
|
printer->Print("context: GRPCCore.ServerContext\n");
|
|
|
|
printer->Outdent();
|
|
printer->Print(vars, ") async throws -> $Output$\n\n");
|
|
}
|
|
|
|
void GenerateServiceProtocols(const grpc_generator::Service* service,
|
|
grpc_generator::Printer* printer,
|
|
std::map<grpc::string, grpc::string>* dictionary) {
|
|
auto vars = *dictionary;
|
|
EnforceOSVersion(printer);
|
|
printer->Print(vars, "extension $SwiftServiceQualifiedName$ {\n");
|
|
printer->Indent();
|
|
// Base protocol
|
|
printer->Print(vars,
|
|
"$ACCESS$ protocol StreamingServiceProtocol: "
|
|
"GRPCCore.RegistrableRPCService {\n");
|
|
printer->Indent();
|
|
|
|
for (auto it = 0; it < service->method_count(); it++) {
|
|
auto method = service->method(it);
|
|
vars["Input"] =
|
|
GenerateType(QualifiedName(method->get_input_namespace_parts(),
|
|
method->get_output_type_name()),
|
|
StreamingServerRequest());
|
|
|
|
vars["Output"] =
|
|
GenerateType(QualifiedName(method->get_input_namespace_parts(),
|
|
method->get_output_type_name()),
|
|
StreamingServerResponse());
|
|
auto name = method->name();
|
|
vars["MethodName"] = name;
|
|
GenerateFunction(printer, &vars);
|
|
}
|
|
printer->Outdent();
|
|
printer->Print("}\n\n");
|
|
|
|
// Service
|
|
printer->Print(vars,
|
|
"$ACCESS$ protocol ServiceProtocol: "
|
|
"$SwiftServiceQualifiedName$.StreamingServiceProtocol {\n");
|
|
printer->Indent();
|
|
|
|
for (auto it = 0; it < service->method_count(); it++) {
|
|
auto method = service->method(it);
|
|
|
|
vars["Input"] =
|
|
GenerateType(QualifiedName(method->get_input_namespace_parts(),
|
|
method->get_output_type_name()),
|
|
GenerateInputMethodTypes(&*method));
|
|
|
|
vars["Output"] =
|
|
GenerateType(QualifiedName(method->get_input_namespace_parts(),
|
|
method->get_output_type_name()),
|
|
GenerateOutputMethodTypes(&*method));
|
|
auto name = method->name();
|
|
vars["MethodName"] = name;
|
|
GenerateFunction(printer, &vars);
|
|
}
|
|
|
|
printer->Outdent();
|
|
printer->Print("}\n\n");
|
|
|
|
// Simple service
|
|
printer->Print(vars,
|
|
"$ACCESS$ protocol SimpleServiceProtocol: "
|
|
"$SwiftServiceQualifiedName$.ServiceProtocol {\n");
|
|
printer->Indent();
|
|
|
|
for (auto it = 0; it < service->method_count(); it++) {
|
|
auto method = service->method(it);
|
|
|
|
vars["Input"] = GenerateGRPCMessage(QualifiedName(
|
|
method->get_input_namespace_parts(), method->get_output_type_name()));
|
|
|
|
vars["Output"] = GenerateGRPCMessage(QualifiedName(
|
|
method->get_input_namespace_parts(), method->get_output_type_name()));
|
|
auto name = method->name();
|
|
vars["MethodName"] = name;
|
|
|
|
printer->Print(vars, "func $MethodName$(\n");
|
|
printer->Indent();
|
|
|
|
if (method->ClientStreaming() || method->BidiStreaming()) {
|
|
printer->Print(
|
|
vars,
|
|
"request: GRPCCore.RPCAsyncSequence<$Input$, any Swift.Error>,\n");
|
|
} else {
|
|
printer->Print(vars, "request: $Input$,\n");
|
|
}
|
|
|
|
if (method->BidiStreaming() || method->ServerStreaming()) {
|
|
printer->Print(vars, "response: GRPCCore.RPCWriter<$Output$>,\n");
|
|
}
|
|
printer->Print("context: GRPCCore.ServerContext\n");
|
|
|
|
printer->Outdent();
|
|
|
|
if (!method->BidiStreaming() && !method->ServerStreaming()) {
|
|
printer->Print(vars, ") async throws -> $Output$\n\n");
|
|
} else {
|
|
printer->Print(") async throws\n\n");
|
|
}
|
|
}
|
|
|
|
printer->Outdent();
|
|
printer->Print("}\n");
|
|
|
|
printer->Outdent();
|
|
printer->Print("}\n\n");
|
|
|
|
EnforceOSVersion(printer);
|
|
printer->Print(
|
|
vars,
|
|
"extension $SwiftServiceQualifiedName$.StreamingServiceProtocol {\n");
|
|
printer->Indent();
|
|
printer->Print(
|
|
"public func registerMethods<Transport>(with router: inout "
|
|
"GRPCCore.RPCRouter<Transport>) where Transport: "
|
|
"GRPCCore.ServerTransport {\n");
|
|
printer->Indent();
|
|
|
|
for (auto it = 0; it < service->method_count(); it++) {
|
|
auto method = service->method(it);
|
|
vars["Input"] =
|
|
GenerateType(QualifiedName(method->get_input_namespace_parts(),
|
|
method->get_output_type_name()),
|
|
"FlatBuffersMessageSerializer");
|
|
|
|
vars["Output"] =
|
|
GenerateType(QualifiedName(method->get_input_namespace_parts(),
|
|
method->get_output_type_name()),
|
|
"FlatBuffersMessageDeserializer");
|
|
|
|
auto name = method->name();
|
|
vars["MethodName"] = name;
|
|
|
|
printer->Print("router.registerHandler(\n");
|
|
printer->Indent();
|
|
printer->Print(
|
|
vars,
|
|
"forMethod: "
|
|
"$SwiftServiceQualifiedName$.Method.$MethodName$.descriptor,\n");
|
|
printer->Print(vars, "deserializer: $Output$(),\n");
|
|
printer->Print(vars, "serializer: $Input$(),\n");
|
|
printer->Print("handler: { request, context in\n");
|
|
printer->Indent();
|
|
printer->Print(vars, "try await self.$MethodName$(\n");
|
|
printer->Indent();
|
|
printer->Print("request: request,\n");
|
|
printer->Print("context: context\n");
|
|
printer->Outdent();
|
|
printer->Print(")\n");
|
|
printer->Outdent();
|
|
printer->Print("}\n");
|
|
printer->Outdent();
|
|
printer->Print(")\n");
|
|
}
|
|
|
|
printer->Outdent();
|
|
printer->Print("}\n");
|
|
printer->Outdent();
|
|
printer->Print("}\n\n");
|
|
}
|
|
|
|
void CreateServiceProtocolFunctionsImplementations(
|
|
grpc_generator::Printer* printer,
|
|
std::map<grpc::string, grpc::string>* dictionary) {
|
|
auto vars = *dictionary;
|
|
printer->Print(vars, "$ACCESS$ func $MethodName$(\n");
|
|
printer->Indent();
|
|
printer->Print(vars, "request: $Input$,\n");
|
|
printer->Print("context: GRPCCore.ServerContext\n");
|
|
printer->Outdent();
|
|
printer->Print(vars, ") async throws -> $Output$ {\n");
|
|
printer->Indent();
|
|
printer->Print(vars, "let response = try await self.$MethodName$(\n");
|
|
printer->Indent();
|
|
printer->Print(vars, "request: $InputRequest$,\n");
|
|
printer->Print("context: context\n");
|
|
printer->Outdent();
|
|
printer->Print(")\n");
|
|
printer->Print(vars, "return $OutputResponse$\n");
|
|
printer->Outdent();
|
|
printer->Print("}\n");
|
|
}
|
|
|
|
std::string GenerateInputRequest(const grpc_generator::Method* method) {
|
|
if (method->NoStreaming() || method->ServerStreaming())
|
|
return ServerRequest() + "(stream: request)";
|
|
return "request";
|
|
}
|
|
|
|
std::string GenerateOutputResponse(const grpc_generator::Method* method) {
|
|
if (method->NoStreaming() || method->ClientStreaming())
|
|
return StreamingServerResponse() + "(single: response)";
|
|
return "response";
|
|
}
|
|
|
|
void GenerateServiceDefaultImplementation(
|
|
const grpc_generator::Service* service, grpc_generator::Printer* printer,
|
|
std::map<grpc::string, grpc::string>* dictionary) {
|
|
auto vars = *dictionary;
|
|
EnforceOSVersion(printer);
|
|
printer->Print(vars,
|
|
"extension $SwiftServiceQualifiedName$.ServiceProtocol {\n");
|
|
printer->Indent();
|
|
|
|
for (auto it = 0; it < service->method_count(); it++) {
|
|
auto method = service->method(it);
|
|
if (method->BidiStreaming()) {
|
|
continue;
|
|
}
|
|
|
|
vars["Input"] =
|
|
GenerateType(QualifiedName(method->get_input_namespace_parts(),
|
|
method->get_output_type_name()),
|
|
StreamingServerRequest());
|
|
|
|
vars["Output"] =
|
|
GenerateType(QualifiedName(method->get_input_namespace_parts(),
|
|
method->get_output_type_name()),
|
|
StreamingServerResponse());
|
|
auto name = method->name();
|
|
vars["MethodName"] = name;
|
|
|
|
vars["InputRequest"] = GenerateInputRequest(&*method);
|
|
vars["OutputResponse"] = GenerateOutputResponse(&*method);
|
|
|
|
CreateServiceProtocolFunctionsImplementations(printer, &vars);
|
|
}
|
|
printer->Outdent();
|
|
printer->Print("}\n\n");
|
|
}
|
|
|
|
void GenerateSimpleServiceImplemetation(
|
|
const grpc_generator::Service* service, grpc_generator::Printer* printer,
|
|
std::map<grpc::string, grpc::string>* dictionary) {
|
|
auto vars = *dictionary;
|
|
EnforceOSVersion(printer);
|
|
printer->Print(
|
|
vars, "extension $SwiftServiceQualifiedName$.SimpleServiceProtocol {\n");
|
|
printer->Indent();
|
|
|
|
for (auto it = 0; it < service->method_count(); it++) {
|
|
auto method = service->method(it);
|
|
|
|
vars["Input"] =
|
|
GenerateType(QualifiedName(method->get_input_namespace_parts(),
|
|
method->get_output_type_name()),
|
|
GenerateInputMethodTypes(&*method));
|
|
|
|
vars["Output"] =
|
|
GenerateType(QualifiedName(method->get_input_namespace_parts(),
|
|
method->get_output_type_name()),
|
|
GenerateOutputMethodTypes(&*method));
|
|
auto name = method->name();
|
|
vars["MethodName"] = name;
|
|
|
|
printer->Print(vars, "$ACCESS$ func $MethodName$(\n");
|
|
printer->Indent();
|
|
printer->Print(vars, "request: $Input$,\n");
|
|
printer->Print("context: GRPCCore.ServerContext\n");
|
|
printer->Outdent();
|
|
printer->Print(vars, ") async throws -> $Output$ {\n");
|
|
printer->Indent();
|
|
printer->Print(vars, "return $Output$(\n");
|
|
printer->Indent();
|
|
|
|
if (method->NoStreaming() || method->ClientStreaming()) {
|
|
vars["Message"] = method->ClientStreaming() ? "messages" : "message";
|
|
printer->Print(vars, "message: try await self.$MethodName$(\n");
|
|
printer->Indent();
|
|
printer->Print(vars, "request: request.$Message$,\n");
|
|
printer->Print("context: context\n");
|
|
printer->Outdent();
|
|
printer->Print(vars, "),\n");
|
|
printer->Print(vars, "metadata: [:]\n");
|
|
} else {
|
|
vars["Message"] = method->BidiStreaming() ? "messages" : "message";
|
|
printer->Print(vars, "metadata: [:],\n");
|
|
printer->Print(vars, "producer: { writer in\n");
|
|
printer->Indent();
|
|
printer->Print(vars, "try await self.$MethodName$(\n");
|
|
printer->Indent();
|
|
printer->Print(vars, "request: request.$Message$,\n");
|
|
printer->Print("response: writer,\n");
|
|
printer->Print("context: context\n");
|
|
printer->Outdent();
|
|
printer->Print(")\n");
|
|
printer->Print("return [:]\n");
|
|
printer->Outdent();
|
|
printer->Print(vars, "}\n");
|
|
}
|
|
|
|
printer->Outdent();
|
|
printer->Print(")\n");
|
|
printer->Outdent();
|
|
printer->Print("}\n");
|
|
}
|
|
printer->Outdent();
|
|
printer->Print("}\n\n");
|
|
}
|
|
|
|
void GenerateService(const grpc_generator::Service* service,
|
|
grpc_generator::Printer* printer,
|
|
std::map<grpc::string, grpc::string>* dictionary) {
|
|
GenerateServiceProtocols(service, printer, dictionary);
|
|
GenerateServiceDefaultImplementation(service, printer, dictionary);
|
|
GenerateSimpleServiceImplemetation(service, printer, dictionary);
|
|
}
|
|
|
|
// Client Generation
|
|
|
|
void GenerateClientProtocols(const grpc_generator::Service* service,
|
|
grpc_generator::Printer* printer,
|
|
std::map<grpc::string, grpc::string>* dictionary) {
|
|
auto vars = *dictionary;
|
|
EnforceOSVersion(printer);
|
|
printer->Print(vars, "extension $SwiftServiceQualifiedName$ {\n");
|
|
printer->Indent();
|
|
// Base protocol
|
|
printer->Print(vars, "$ACCESS$ protocol ClientProtocol: Sendable {\n");
|
|
printer->Indent();
|
|
|
|
for (auto it = 0; it < service->method_count(); it++) {
|
|
auto method = service->method(it);
|
|
auto name = method->name();
|
|
vars["MethodName"] = name;
|
|
|
|
vars["serializer"] =
|
|
GenerateType(QualifiedName(method->get_input_namespace_parts(),
|
|
method->get_output_type_name()),
|
|
"GRPCCore.MessageSerializer");
|
|
|
|
vars["deserializer"] =
|
|
GenerateType(QualifiedName(method->get_input_namespace_parts(),
|
|
method->get_output_type_name()),
|
|
"GRPCCore.MessageDeserializer");
|
|
|
|
if (method->ClientStreaming() || method->BidiStreaming()) {
|
|
vars["request"] =
|
|
GenerateType(QualifiedName(method->get_input_namespace_parts(),
|
|
method->get_output_type_name()),
|
|
"GRPCCore.StreamingClientRequest");
|
|
} else {
|
|
vars["request"] =
|
|
GenerateType(QualifiedName(method->get_input_namespace_parts(),
|
|
method->get_output_type_name()),
|
|
"GRPCCore.ClientRequest");
|
|
}
|
|
|
|
if (method->ServerStreaming() || method->BidiStreaming()) {
|
|
vars["response"] =
|
|
GenerateType(QualifiedName(method->get_input_namespace_parts(),
|
|
method->get_output_type_name()),
|
|
"GRPCCore.StreamingClientResponse");
|
|
} else {
|
|
vars["response"] =
|
|
GenerateType(QualifiedName(method->get_input_namespace_parts(),
|
|
method->get_output_type_name()),
|
|
"GRPCCore.ClientResponse");
|
|
}
|
|
|
|
printer->Print(vars, "func $MethodName$<Result>(\n");
|
|
printer->Indent();
|
|
printer->Print(vars, "request: $request$,\n");
|
|
printer->Print(vars, "serializer: some $serializer$,\n");
|
|
printer->Print(vars, "deserializer: some $deserializer$,\n");
|
|
printer->Print("options: GRPCCore.CallOptions,\n");
|
|
printer->Print(vars,
|
|
"onResponse handleResponse: @Sendable @escaping "
|
|
"($response$) async throws -> Result\n");
|
|
printer->Outdent();
|
|
printer->Print(") async throws -> Result where Result: Sendable\n\n");
|
|
}
|
|
|
|
printer->Outdent();
|
|
printer->Print("}\n");
|
|
printer->Outdent();
|
|
printer->Print("}\n\n");
|
|
}
|
|
|
|
void GenerateClientStruct(const grpc_generator::Service* service,
|
|
grpc_generator::Printer* printer,
|
|
std::map<grpc::string, grpc::string>* dictionary) {
|
|
auto vars = *dictionary;
|
|
EnforceOSVersion(printer);
|
|
printer->Print(vars, "extension $SwiftServiceQualifiedName$ {\n");
|
|
printer->Indent();
|
|
// Base protocol
|
|
printer->Print(vars,
|
|
"$ACCESS$ struct Client<Transport>: ClientProtocol where "
|
|
"Transport: GRPCCore.ClientTransport {\n");
|
|
printer->Indent();
|
|
printer->Print("private let client: GRPCCore.GRPCClient<Transport>\n\n");
|
|
printer->Print(
|
|
vars,
|
|
"$ACCESS$ init(wrapping client: GRPCCore.GRPCClient<Transport>) {\n");
|
|
printer->Indent();
|
|
printer->Print("self.client = client\n");
|
|
printer->Outdent();
|
|
printer->Print("}\n\n");
|
|
|
|
for (auto it = 0; it < service->method_count(); it++) {
|
|
auto method = service->method(it);
|
|
auto name = method->name();
|
|
vars["MethodType"] = GenerateMethodTypes(&*method);
|
|
vars["MethodName"] = name;
|
|
|
|
vars["serializer"] =
|
|
GenerateType(QualifiedName(method->get_input_namespace_parts(),
|
|
method->get_output_type_name()),
|
|
"GRPCCore.MessageSerializer");
|
|
|
|
vars["deserializer"] =
|
|
GenerateType(QualifiedName(method->get_input_namespace_parts(),
|
|
method->get_output_type_name()),
|
|
"GRPCCore.MessageDeserializer");
|
|
|
|
if (method->ClientStreaming() || method->BidiStreaming()) {
|
|
vars["request"] =
|
|
GenerateType(QualifiedName(method->get_input_namespace_parts(),
|
|
method->get_output_type_name()),
|
|
"GRPCCore.StreamingClientRequest");
|
|
} else {
|
|
vars["request"] =
|
|
GenerateType(QualifiedName(method->get_input_namespace_parts(),
|
|
method->get_output_type_name()),
|
|
"GRPCCore.ClientRequest");
|
|
}
|
|
|
|
if (method->ServerStreaming() || method->BidiStreaming()) {
|
|
vars["response"] =
|
|
GenerateType(QualifiedName(method->get_input_namespace_parts(),
|
|
method->get_output_type_name()),
|
|
"GRPCCore.StreamingClientResponse");
|
|
vars["CompletionBlock"] = "";
|
|
} else {
|
|
vars["response"] =
|
|
GenerateType(QualifiedName(method->get_input_namespace_parts(),
|
|
method->get_output_type_name()),
|
|
"GRPCCore.ClientResponse");
|
|
vars["CompletionBlock"] = " = { response in try response.message }";
|
|
}
|
|
|
|
printer->Print(vars, "$ACCESS$ func $MethodName$<Result>(\n");
|
|
printer->Indent();
|
|
printer->Print(vars, "request: $request$,\n");
|
|
printer->Print(vars, "serializer: some $serializer$,\n");
|
|
printer->Print(vars, "deserializer: some $deserializer$,\n");
|
|
printer->Print("options: GRPCCore.CallOptions = .defaults,\n");
|
|
printer->Print(vars,
|
|
"onResponse handleResponse: @Sendable @escaping "
|
|
"($response$) async throws -> Result$CompletionBlock$\n");
|
|
printer->Outdent();
|
|
printer->Print(") async throws -> Result where Result: Sendable {\n");
|
|
printer->Indent();
|
|
printer->Print(vars, "try await self.client.$MethodType$(\n");
|
|
printer->Indent();
|
|
printer->Print("request: request,\n");
|
|
printer->Print(
|
|
vars,
|
|
"descriptor: "
|
|
"$SwiftServiceQualifiedName$.Method.$MethodName$.descriptor,\n");
|
|
printer->Print("serializer: serializer,\n");
|
|
printer->Print("deserializer: deserializer,\n");
|
|
printer->Print("options: options,\n");
|
|
printer->Print("onResponse: handleResponse\n");
|
|
printer->Outdent();
|
|
printer->Print(")\n");
|
|
printer->Outdent();
|
|
printer->Print("}\n\n");
|
|
}
|
|
|
|
printer->Outdent();
|
|
printer->Print("}\n");
|
|
printer->Outdent();
|
|
printer->Print("}\n\n");
|
|
}
|
|
|
|
void GenerateClientDefaultImplementation(
|
|
const grpc_generator::Service* service, grpc_generator::Printer* printer,
|
|
std::map<grpc::string, grpc::string>* dictionary) {
|
|
auto vars = *dictionary;
|
|
EnforceOSVersion(printer);
|
|
printer->Print(vars,
|
|
"extension $SwiftServiceQualifiedName$.ClientProtocol {\n");
|
|
printer->Indent();
|
|
|
|
for (auto it = 0; it < service->method_count(); it++) {
|
|
auto method = service->method(it);
|
|
auto name = method->name();
|
|
vars["MethodName"] = name;
|
|
|
|
vars["serializer"] =
|
|
GenerateType(QualifiedName(method->get_input_namespace_parts(),
|
|
method->get_output_type_name()),
|
|
"FlatBuffersMessageSerializer");
|
|
|
|
vars["deserializer"] =
|
|
GenerateType(QualifiedName(method->get_input_namespace_parts(),
|
|
method->get_output_type_name()),
|
|
"FlatBuffersMessageDeserializer");
|
|
|
|
if (method->ClientStreaming() || method->BidiStreaming()) {
|
|
vars["request"] =
|
|
GenerateType(QualifiedName(method->get_input_namespace_parts(),
|
|
method->get_output_type_name()),
|
|
"GRPCCore.StreamingClientRequest");
|
|
} else {
|
|
vars["request"] =
|
|
GenerateType(QualifiedName(method->get_input_namespace_parts(),
|
|
method->get_output_type_name()),
|
|
"GRPCCore.ClientRequest");
|
|
}
|
|
|
|
if (method->ServerStreaming() || method->BidiStreaming()) {
|
|
vars["response"] =
|
|
GenerateType(QualifiedName(method->get_input_namespace_parts(),
|
|
method->get_output_type_name()),
|
|
"GRPCCore.StreamingClientResponse");
|
|
vars["CompletionBlock"] = "";
|
|
} else {
|
|
vars["response"] =
|
|
GenerateType(QualifiedName(method->get_input_namespace_parts(),
|
|
method->get_output_type_name()),
|
|
"GRPCCore.ClientResponse");
|
|
vars["CompletionBlock"] = " = { response in try response.message }";
|
|
}
|
|
|
|
printer->Print(vars, "$ACCESS$ func $MethodName$<Result>(\n");
|
|
printer->Indent();
|
|
printer->Print(vars, "request: $request$,\n");
|
|
printer->Print("options: GRPCCore.CallOptions = .defaults,\n");
|
|
printer->Print(vars,
|
|
"onResponse handleResponse: @Sendable @escaping "
|
|
"($response$) async throws -> Result$CompletionBlock$\n");
|
|
printer->Outdent();
|
|
printer->Print(") async throws -> Result where Result: Sendable {\n");
|
|
printer->Indent();
|
|
printer->Print(vars, "try await self.$MethodName$(\n");
|
|
printer->Indent();
|
|
printer->Print("request: request,\n");
|
|
printer->Print(vars, "serializer: $serializer$(),\n");
|
|
printer->Print(vars, "deserializer: $deserializer$(),\n");
|
|
printer->Print("options: options,\n");
|
|
printer->Print("onResponse: handleResponse\n");
|
|
printer->Outdent();
|
|
printer->Print(")\n");
|
|
printer->Outdent();
|
|
printer->Print("}\n\n");
|
|
}
|
|
|
|
printer->Outdent();
|
|
printer->Print("}\n\n");
|
|
}
|
|
|
|
void GenerateClientHelperMethods(
|
|
const grpc_generator::Service* service, grpc_generator::Printer* printer,
|
|
std::map<grpc::string, grpc::string>* dictionary) {
|
|
auto vars = *dictionary;
|
|
|
|
EnforceOSVersion(printer);
|
|
printer->Print(vars,
|
|
"extension $SwiftServiceQualifiedName$.ClientProtocol {\n");
|
|
printer->Indent();
|
|
|
|
for (auto it = 0; it < service->method_count(); it++) {
|
|
auto method = service->method(it);
|
|
auto name = method->name();
|
|
vars["MethodName"] = name;
|
|
|
|
vars["serializer"] =
|
|
GenerateType(QualifiedName(method->get_input_namespace_parts(),
|
|
method->get_output_type_name()),
|
|
"FlatBuffersMessageSerializer");
|
|
|
|
vars["deserializer"] =
|
|
GenerateType(QualifiedName(method->get_input_namespace_parts(),
|
|
method->get_output_type_name()),
|
|
"FlatBuffersMessageDeserializer");
|
|
|
|
printer->Print(vars, "$ACCESS$ func $MethodName$<Result>(\n");
|
|
printer->Indent();
|
|
|
|
if (method->NoStreaming() || method->ServerStreaming()) {
|
|
vars["request"] = GenerateGRPCMessage(QualifiedName(
|
|
method->get_input_namespace_parts(), method->get_output_type_name()));
|
|
printer->Print(vars, "_ message: $request$,\n");
|
|
}
|
|
|
|
printer->Print("metadata: GRPCCore.Metadata = [:],\n");
|
|
printer->Print("options: GRPCCore.CallOptions = .defaults,\n");
|
|
|
|
if (method->ClientStreaming() || method->BidiStreaming()) {
|
|
vars["request"] = GenerateGRPCMessage(QualifiedName(
|
|
method->get_input_namespace_parts(), method->get_output_type_name()));
|
|
printer->Print(vars,
|
|
"requestProducer producer: @Sendable @escaping "
|
|
"(GRPCCore.RPCWriter<$request$>) async throws -> Void,\n");
|
|
}
|
|
|
|
if (method->NoStreaming() || method->ClientStreaming()) {
|
|
vars["response"] = GenerateGRPCMessage(QualifiedName(
|
|
method->get_input_namespace_parts(), method->get_output_type_name()));
|
|
|
|
printer->Print(vars,
|
|
"onResponse handleResponse: @Sendable @escaping "
|
|
"(GRPCCore.ClientResponse<$response$>) async throws -> "
|
|
"Result = { try $0.message }\n");
|
|
} else {
|
|
vars["response"] = GenerateGRPCMessage(QualifiedName(
|
|
method->get_input_namespace_parts(), method->get_output_type_name()));
|
|
|
|
printer->Print(vars,
|
|
"onResponse handleResponse: @Sendable @escaping "
|
|
"(GRPCCore.StreamingClientResponse<$response$>) async "
|
|
"throws -> Result\n");
|
|
}
|
|
|
|
printer->Outdent();
|
|
printer->Print(") async throws -> Result where Result: Sendable {\n");
|
|
printer->Indent();
|
|
|
|
if (method->ClientStreaming() || method->BidiStreaming()) {
|
|
vars["request"] =
|
|
GenerateType(QualifiedName(method->get_input_namespace_parts(),
|
|
method->get_output_type_name()),
|
|
"GRPCCore.StreamingClientRequest");
|
|
} else {
|
|
vars["request"] =
|
|
GenerateType(QualifiedName(method->get_input_namespace_parts(),
|
|
method->get_output_type_name()),
|
|
"GRPCCore.ClientRequest");
|
|
}
|
|
|
|
printer->Print(vars, "let request = $request$(\n");
|
|
printer->Indent();
|
|
if (method->NoStreaming() || method->ServerStreaming()) {
|
|
printer->Print("message: message,\n");
|
|
printer->Print("metadata: metadata\n");
|
|
} else {
|
|
printer->Print("metadata: metadata,\n");
|
|
printer->Print("producer: producer\n");
|
|
}
|
|
|
|
printer->Outdent();
|
|
printer->Print(")\n");
|
|
|
|
printer->Print(vars, "return try await self.$MethodName$(\n");
|
|
printer->Indent();
|
|
printer->Print("request: request,\n");
|
|
printer->Print("options: options,\n");
|
|
printer->Print("onResponse: handleResponse\n");
|
|
printer->Outdent();
|
|
printer->Print(")\n");
|
|
printer->Outdent();
|
|
printer->Print("}\n\n");
|
|
}
|
|
|
|
printer->Outdent();
|
|
printer->Print("}\n\n");
|
|
}
|
|
|
|
void GenerateClient(const grpc_generator::Service* service,
|
|
grpc_generator::Printer* printer,
|
|
std::map<grpc::string, grpc::string>* dictionary) {
|
|
GenerateClientProtocols(service, printer, dictionary);
|
|
GenerateClientStruct(service, printer, dictionary);
|
|
GenerateClientDefaultImplementation(service, printer, dictionary);
|
|
GenerateClientHelperMethods(service, printer, dictionary);
|
|
}
|
|
|
|
} // namespace
|
|
|
|
grpc::string Generate(grpc_generator::File* file,
|
|
const grpc_generator::Service* service) {
|
|
grpc::string output;
|
|
std::map<grpc::string, grpc::string> vars;
|
|
vars["PATH"] = file->package();
|
|
if (!file->package().empty()) {
|
|
vars["PATH"].append(".");
|
|
}
|
|
vars["SwiftServiceQualifiedName"] =
|
|
QualifiedName(service->namespace_parts(), service->name());
|
|
vars["ServiceQualifiedName"] =
|
|
QualifiedName(service->namespace_parts(), service->name(), ".");
|
|
vars["ServiceName"] = service->name();
|
|
vars["ACCESS"] = service->is_internal() ? "internal" : "public";
|
|
auto printer = file->CreatePrinter(&output);
|
|
printer->Print(
|
|
vars,
|
|
"/// Usage: instantiate $ServiceQualifiedName$ServiceClient, then call "
|
|
"methods of this protocol to make API calls.\n");
|
|
GenerateCoders(&*printer);
|
|
GenerateSharedContent(service, &*printer, &vars);
|
|
printer->Print("\n");
|
|
printer->Print(vars, "// MARK: $ServiceQualifiedName$ Server\n\n");
|
|
GenerateService(service, &*printer, &vars);
|
|
printer->Print("\n");
|
|
printer->Print(vars, "// MARK: $ServiceQualifiedName$ Client\n\n");
|
|
|
|
GenerateClient(service, &*printer, &vars);
|
|
printer->Print("#endif\n");
|
|
return output;
|
|
}
|
|
|
|
grpc::string GenerateHeader() {
|
|
grpc::string code;
|
|
code +=
|
|
"/// The following code is generated by the Flatbuffers library which "
|
|
"might not be in sync with grpc-swift\n";
|
|
code +=
|
|
"/// in case of an issue please open github issue, though it would be "
|
|
"maintained\n";
|
|
code += "\n";
|
|
code += "// swiftlint:disable all\n";
|
|
code += "// swiftformat:disable all\n";
|
|
code += "\n";
|
|
code += "#if !os(Windows) && compiler(>=6.0)\n";
|
|
code += "import FlatBuffers\n";
|
|
code += "import Foundation\n";
|
|
code += "import GRPCCore\n";
|
|
code += "import GRPCNIOTransportCore\n";
|
|
code += "\n";
|
|
return code;
|
|
}
|
|
} // namespace grpc_swift_generator
|