| /* |
| * 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 <map> |
| #include <sstream> |
| |
| #include "flatbuffers/util.h" |
| #include "src/compiler/schema_interface.h" |
| #include "src/compiler/swift_generator.h" |
| |
| namespace grpc_swift_generator { |
| |
| std::string WrapInNameSpace(const std::vector<std::string> &components, |
| const grpc::string &name) { |
| std::string qualified_name; |
| for (auto it = components.begin(); it != components.end(); ++it) |
| qualified_name += *it + "_"; |
| return qualified_name + name; |
| } |
| |
| grpc::string GenerateMessage(const std::vector<std::string> &components, |
| const grpc::string &name) { |
| return "Message<" + WrapInNameSpace(components, name) + ">"; |
| } |
| |
| // MARK: - Client |
| |
| void GenerateClientFuncName(const grpc_generator::Method *method, |
| grpc_generator::Printer *printer, |
| std::map<grpc::string, grpc::string> *dictonary) { |
| auto vars = *dictonary; |
| if (method->NoStreaming()) { |
| printer->Print(vars, |
| " $GenAccess$func $MethodName$(\n" |
| " _ request: $Input$\n" |
| " , callOptions: CallOptions?$isNil$\n" |
| " ) -> UnaryCall<$Input$, $Output$>"); |
| return; |
| } |
| |
| if (method->ServerStreaming()) { |
| printer->Print(vars, |
| " $GenAccess$func $MethodName$(\n" |
| " _ request: $Input$\n" |
| " , callOptions: CallOptions?$isNil$,\n" |
| " handler: @escaping ($Output$) -> Void\n" |
| " ) -> ServerStreamingCall<$Input$, $Output$>"); |
| return; |
| } |
| |
| if (method->ClientStreaming()) { |
| printer->Print(vars, |
| " $GenAccess$func $MethodName$(\n" |
| " callOptions: CallOptions?$isNil$\n" |
| " ) -> ClientStreamingCall<$Input$, $Output$>"); |
| return; |
| } |
| |
| printer->Print(vars, |
| " $GenAccess$func $MethodName$(\n" |
| " callOptions: CallOptions?$isNil$,\n" |
| " handler: @escaping ($Output$ ) -> Void\n" |
| " ) -> BidirectionalStreamingCall<$Input$, $Output$>"); |
| } |
| |
| void GenerateClientFuncBody(const grpc_generator::Method *method, |
| grpc_generator::Printer *printer, |
| std::map<grpc::string, grpc::string> *dictonary) { |
| auto vars = *dictonary; |
| vars["Interceptor"] = |
| "interceptors: self.interceptors?.make$MethodName$Interceptors() ?? []"; |
| if (method->NoStreaming()) { |
| printer->Print( |
| vars, |
| " return self.makeUnaryCall(\n" |
| " path: \"/$PATH$$ServiceName$/$MethodName$\",\n" |
| " request: request,\n" |
| " callOptions: callOptions ?? self.defaultCallOptions,\n" |
| " $Interceptor$\n" |
| " )\n"); |
| return; |
| } |
| |
| if (method->ServerStreaming()) { |
| printer->Print( |
| vars, |
| " return self.makeServerStreamingCall(\n" |
| " path: \"/$PATH$$ServiceName$/$MethodName$\",\n" |
| " request: request,\n" |
| " callOptions: callOptions ?? self.defaultCallOptions,\n" |
| " $Interceptor$,\n" |
| " handler: handler\n" |
| " )\n"); |
| return; |
| } |
| |
| if (method->ClientStreaming()) { |
| printer->Print( |
| vars, |
| " return self.makeClientStreamingCall(\n" |
| " path: \"/$PATH$$ServiceName$/$MethodName$\",\n" |
| " callOptions: callOptions ?? self.defaultCallOptions,\n" |
| " $Interceptor$\n" |
| " )\n"); |
| return; |
| } |
| printer->Print(vars, |
| " return self.makeBidirectionalStreamingCall(\n" |
| " path: \"/$PATH$$ServiceName$/$MethodName$\",\n" |
| " callOptions: callOptions ?? self.defaultCallOptions,\n" |
| " $Interceptor$,\n" |
| " handler: handler\n" |
| " )\n"); |
| } |
| |
| void GenerateClientProtocol(const grpc_generator::Service *service, |
| grpc_generator::Printer *printer, |
| std::map<grpc::string, grpc::string> *dictonary) { |
| auto vars = *dictonary; |
| printer->Print( |
| vars, |
| "$ACCESS$ protocol $ServiceQualifiedName$ClientProtocol: GRPCClient {"); |
| printer->Print("\n\n"); |
| printer->Print(" var serviceName: String { get }"); |
| printer->Print("\n\n"); |
| printer->Print( |
| vars, |
| " var interceptors: " |
| "$ServiceQualifiedName$ClientInterceptorFactoryProtocol? { get }"); |
| printer->Print("\n\n"); |
| |
| vars["GenAccess"] = ""; |
| for (auto it = 0; it < service->method_count(); it++) { |
| auto method = service->method(it); |
| vars["Input"] = GenerateMessage(method->get_input_namespace_parts(), |
| method->get_input_type_name()); |
| vars["Output"] = GenerateMessage(method->get_output_namespace_parts(), |
| method->get_output_type_name()); |
| vars["MethodName"] = method->name(); |
| vars["isNil"] = ""; |
| GenerateClientFuncName(method.get(), &*printer, &vars); |
| printer->Print("\n\n"); |
| } |
| printer->Print("}\n\n"); |
| |
| printer->Print(vars, "extension $ServiceQualifiedName$ClientProtocol {"); |
| printer->Print("\n\n"); |
| printer->Print(vars, |
| " $ACCESS$ var serviceName: String { " |
| "\"$PATH$$ServiceName$\" }\n"); |
| |
| vars["GenAccess"] = service->is_internal() ? "internal " : "public "; |
| for (auto it = 0; it < service->method_count(); it++) { |
| auto method = service->method(it); |
| vars["Input"] = GenerateMessage(method->get_input_namespace_parts(), |
| method->get_input_type_name()); |
| vars["Output"] = GenerateMessage(method->get_output_namespace_parts(), |
| method->get_output_type_name()); |
| vars["MethodName"] = method->name(); |
| vars["isNil"] = " = nil"; |
| printer->Print("\n"); |
| GenerateClientFuncName(method.get(), &*printer, &vars); |
| printer->Print(" {\n"); |
| GenerateClientFuncBody(method.get(), &*printer, &vars); |
| printer->Print(" }\n"); |
| } |
| printer->Print("}\n\n"); |
| |
| printer->Print(vars, |
| "$ACCESS$ protocol " |
| "$ServiceQualifiedName$ClientInterceptorFactoryProtocol {\n"); |
| |
| for (auto it = 0; it < service->method_count(); it++) { |
| auto method = service->method(it); |
| vars["Input"] = GenerateMessage(method->get_input_namespace_parts(), |
| method->get_input_type_name()); |
| vars["Output"] = GenerateMessage(method->get_output_namespace_parts(), |
| method->get_output_type_name()); |
| vars["MethodName"] = method->name(); |
| printer->Print( |
| vars, |
| " /// - Returns: Interceptors to use when invoking '$MethodName$'.\n"); |
| printer->Print(vars, |
| " func make$MethodName$Interceptors() -> " |
| "[ClientInterceptor<$Input$, $Output$>]\n\n"); |
| } |
| printer->Print("}\n\n"); |
| } |
| |
| void GenerateClientClass(grpc_generator::Printer *printer, |
| std::map<grpc::string, grpc::string> *dictonary) { |
| auto vars = *dictonary; |
| printer->Print(vars, |
| "$ACCESS$ final class $ServiceQualifiedName$ServiceClient: " |
| "$ServiceQualifiedName$ClientProtocol {\n"); |
| printer->Print(vars, " $ACCESS$ let channel: GRPCChannel\n"); |
| printer->Print(vars, " $ACCESS$ var defaultCallOptions: CallOptions\n"); |
| printer->Print(vars, |
| " $ACCESS$ var interceptors: " |
| "$ServiceQualifiedName$ClientInterceptorFactoryProtocol?\n"); |
| printer->Print("\n"); |
| printer->Print( |
| vars, |
| " $ACCESS$ init(\n" |
| " channel: GRPCChannel,\n" |
| " defaultCallOptions: CallOptions = CallOptions(),\n" |
| " interceptors: " |
| "$ServiceQualifiedName$ClientInterceptorFactoryProtocol? = nil\n" |
| " ) {\n"); |
| printer->Print(" self.channel = channel\n"); |
| printer->Print(" self.defaultCallOptions = defaultCallOptions\n"); |
| printer->Print(" self.interceptors = interceptors\n"); |
| printer->Print(" }"); |
| printer->Print("\n"); |
| printer->Print("}\n"); |
| } |
| |
| // MARK: - Server |
| |
| grpc::string GenerateServerFuncName(const grpc_generator::Method *method) { |
| if (method->NoStreaming()) { |
| return "func $MethodName$(request: $Input$" |
| ", context: StatusOnlyCallContext) -> EventLoopFuture<$Output$>"; |
| } |
| |
| if (method->ClientStreaming()) { |
| return "func $MethodName$(context: UnaryResponseCallContext<$Output$>) -> " |
| "EventLoopFuture<(StreamEvent<$Input$" |
| ">) -> Void>"; |
| } |
| |
| if (method->ServerStreaming()) { |
| return "func $MethodName$(request: $Input$" |
| ", context: StreamingResponseCallContext<$Output$>) -> " |
| "EventLoopFuture<GRPCStatus>"; |
| } |
| return "func $MethodName$(context: StreamingResponseCallContext<$Output$>) " |
| "-> EventLoopFuture<(StreamEvent<$Input$>) -> Void>"; |
| } |
| |
| grpc::string GenerateServerExtensionBody(const grpc_generator::Method *method) { |
| grpc::string start = " case \"$MethodName$\":\n "; |
| grpc::string interceptors = |
| " interceptors: self.interceptors?.make$MethodName$Interceptors() " |
| "?? [],\n"; |
| if (method->NoStreaming()) { |
| return start + |
| "return UnaryServerHandler(\n" |
| " context: context,\n" |
| " requestDeserializer: GRPCPayloadDeserializer<$Input$>(),\n" |
| " responseSerializer: GRPCPayloadSerializer<$Output$>(),\n" + |
| interceptors + |
| " userFunction: self.$MethodName$(request:context:))\n"; |
| } |
| if (method->ServerStreaming()) { |
| return start + |
| "return ServerStreamingServerHandler(\n" |
| " context: context,\n" |
| " requestDeserializer: GRPCPayloadDeserializer<$Input$>(),\n" |
| " responseSerializer: GRPCPayloadSerializer<$Output$>(),\n" + |
| interceptors + |
| " userFunction: self.$MethodName$(request:context:))\n"; |
| } |
| if (method->ClientStreaming()) { |
| return start + |
| "return ClientStreamingServerHandler(\n" |
| " context: context,\n" |
| " requestDeserializer: GRPCPayloadDeserializer<$Input$>(),\n" |
| " responseSerializer: GRPCPayloadSerializer<$Output$>(),\n" + |
| interceptors + |
| " observerFactory: self.$MethodName$(context:))\n"; |
| } |
| if (method->BidiStreaming()) { |
| return start + |
| "return BidirectionalStreamingServerHandler(\n" |
| " context: context,\n" |
| " requestDeserializer: GRPCPayloadDeserializer<$Input$>(),\n" |
| " responseSerializer: GRPCPayloadSerializer<$Output$>(),\n" + |
| interceptors + |
| " observerFactory: self.$MethodName$(context:))\n"; |
| } |
| return ""; |
| } |
| |
| void GenerateServerProtocol(const grpc_generator::Service *service, |
| grpc_generator::Printer *printer, |
| std::map<grpc::string, grpc::string> *dictonary) { |
| auto vars = *dictonary; |
| printer->Print(vars, |
| "$ACCESS$ protocol $ServiceQualifiedName$Provider: " |
| "CallHandlerProvider {\n"); |
| printer->Print( |
| vars, |
| " var interceptors: " |
| "$ServiceQualifiedName$ServerInterceptorFactoryProtocol? { get }\n"); |
| for (auto it = 0; it < service->method_count(); it++) { |
| auto method = service->method(it); |
| vars["Input"] = GenerateMessage(method->get_input_namespace_parts(), |
| method->get_input_type_name()); |
| vars["Output"] = GenerateMessage(method->get_output_namespace_parts(), |
| method->get_output_type_name()); |
| vars["MethodName"] = method->name(); |
| printer->Print(" "); |
| auto func = GenerateServerFuncName(method.get()); |
| printer->Print(vars, func.c_str()); |
| printer->Print("\n"); |
| } |
| printer->Print("}\n\n"); |
| |
| printer->Print(vars, "$ACCESS$ extension $ServiceQualifiedName$Provider {\n"); |
| printer->Print("\n"); |
| printer->Print(vars, |
| " var serviceName: Substring { return " |
| "\"$PATH$$ServiceName$\" }\n"); |
| printer->Print("\n"); |
| printer->Print( |
| " func handle(method name: Substring, context: " |
| "CallHandlerContext) -> GRPCServerHandlerProtocol? {\n"); |
| printer->Print(" switch name {\n"); |
| for (auto it = 0; it < service->method_count(); it++) { |
| auto method = service->method(it); |
| vars["Input"] = GenerateMessage(method->get_input_namespace_parts(), |
| method->get_input_type_name()); |
| vars["Output"] = GenerateMessage(method->get_output_namespace_parts(), |
| method->get_output_type_name()); |
| vars["MethodName"] = method->name(); |
| auto body = GenerateServerExtensionBody(method.get()); |
| printer->Print(vars, body.c_str()); |
| printer->Print("\n"); |
| } |
| printer->Print(" default: return nil;\n"); |
| printer->Print(" }\n"); |
| printer->Print(" }\n\n"); |
| printer->Print("}\n\n"); |
| |
| printer->Print(vars, |
| "$ACCESS$ protocol " |
| "$ServiceQualifiedName$ServerInterceptorFactoryProtocol {\n"); |
| for (auto it = 0; it < service->method_count(); it++) { |
| auto method = service->method(it); |
| vars["Input"] = GenerateMessage(method->get_input_namespace_parts(), |
| method->get_input_type_name()); |
| vars["Output"] = GenerateMessage(method->get_output_namespace_parts(), |
| method->get_output_type_name()); |
| vars["MethodName"] = method->name(); |
| printer->Print( |
| vars, |
| " /// - Returns: Interceptors to use when handling '$MethodName$'.\n" |
| " /// Defaults to calling `self.makeInterceptors()`.\n"); |
| printer->Print(vars, |
| " func make$MethodName$Interceptors() -> " |
| "[ServerInterceptor<$Input$, $Output$>]\n\n"); |
| } |
| printer->Print("}"); |
| } |
| |
| 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["ServiceQualifiedName"] = |
| WrapInNameSpace(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"); |
| GenerateClientProtocol(service, &*printer, &vars); |
| GenerateClientClass(&*printer, &vars); |
| printer->Print("\n"); |
| GenerateServerProtocol(service, &*printer, &vars); |
| 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 += "import Foundation\n"; |
| code += "import GRPC\n"; |
| code += "import NIO\n"; |
| code += "import NIOHTTP1\n"; |
| code += "import FlatBuffers\n"; |
| code += "\n"; |
| code += |
| "public protocol GRPCFlatBufPayload: GRPCPayload, FlatBufferGRPCMessage " |
| "{}\n"; |
| |
| code += "public extension GRPCFlatBufPayload {\n"; |
| code += " init(serializedByteBuffer: inout NIO.ByteBuffer) throws {\n"; |
| code += |
| " self.init(byteBuffer: FlatBuffers.ByteBuffer(contiguousBytes: " |
| "serializedByteBuffer.readableBytesView, count: " |
| "serializedByteBuffer.readableBytes))\n"; |
| code += " }\n"; |
| |
| code += " func serialize(into buffer: inout NIO.ByteBuffer) throws {\n"; |
| code += |
| " let buf = UnsafeRawBufferPointer(start: self.rawPointer, count: " |
| "Int(self.size))\n"; |
| code += " buffer.writeBytes(buf)\n"; |
| code += " }\n"; |
| code += "}\n"; |
| code += "extension Message: GRPCFlatBufPayload {}\n"; |
| return code; |
| } |
| } // namespace grpc_swift_generator |