diff options
author | mustiikhalil <mustii@mmk.one> | 2020-10-07 19:56:30 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-10-07 09:56:30 -0700 |
commit | 7b9e61fccffbaf9a673ae51f89c490508fd39670 (patch) | |
tree | e3a985a85a0cb8e10f06b026bc0d52d1f4dd1fa8 | |
parent | 3359e3042fd1dd947c4093b90ecce24d1839fbdd (diff) | |
download | flatbuffers-7b9e61fccffbaf9a673ae51f89c490508fd39670.tar.gz flatbuffers-7b9e61fccffbaf9a673ae51f89c490508fd39670.tar.bz2 flatbuffers-7b9e61fccffbaf9a673ae51f89c490508fd39670.zip |
[TS] GRPC Implementation (#6141)
* GRPC implementation for Typescript
* Fixes a couple of issues
* Finished implementing the typescript support for grpc
* Updated generated code
* Fixes CI
31 files changed, 1418 insertions, 163 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 456d0bee..6e2b32b0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -124,6 +124,8 @@ set(FlatBuffers_Compiler_SRCS grpc/src/compiler/python_generator.cc grpc/src/compiler/swift_generator.h grpc/src/compiler/swift_generator.cc + grpc/src/compiler/ts_generator.h + grpc/src/compiler/ts_generator.cc ) set(FlatHash_SRCS diff --git a/grpc/flatbuffers-js-grpc/package.json b/grpc/flatbuffers-js-grpc/package.json new file mode 100644 index 00000000..d8270493 --- /dev/null +++ b/grpc/flatbuffers-js-grpc/package.json @@ -0,0 +1,9 @@ +{ + "name": "flatbuffers-js-grpc", + "version": "1.0.0", + "author": "mustii@mmk.one", + "dependencies": { + "flatbuffers": "^1.12.0", + "grpc": "^1.24.3" + } +} diff --git a/grpc/flatbuffers-js-grpc/src/client.ts b/grpc/flatbuffers-js-grpc/src/client.ts new file mode 100644 index 00000000..00d10091 --- /dev/null +++ b/grpc/flatbuffers-js-grpc/src/client.ts @@ -0,0 +1,28 @@ +import grpc from 'grpc'; +import { HelloRequest } from './greeter_generated'; +import { GreeterClient } from './greeter_grpc'; +import { flatbuffers } from 'flatbuffers'; + +async function main() { + const _server = new GreeterClient('localhost:3000', grpc.credentials.createInsecure()); + const builder = new flatbuffers.Builder(); + const offset = builder.createString('mustii'); + const root = HelloRequest.createHelloRequest(builder, offset); + builder.finish(root); + const buffer = HelloRequest.getRootAsHelloRequest(new flatbuffers.ByteBuffer(builder.asUint8Array())); + + _server.SayHello(buffer, (err, response) => { + console.log(response.message()); + }); + + const data = _server.SayManyHellos(buffer, null); + + data.on('data', (data) => { + console.log(data.message()); + }); + data.on('end', (data) => { + console.log('end'); + }); +} + +main();
\ No newline at end of file diff --git a/grpc/flatbuffers-js-grpc/src/greeter.fbs b/grpc/flatbuffers-js-grpc/src/greeter.fbs new file mode 100644 index 00000000..b5108845 --- /dev/null +++ b/grpc/flatbuffers-js-grpc/src/greeter.fbs @@ -0,0 +1,12 @@ +table HelloReply { + message:string; +} + +table HelloRequest { + name:string; +} + +rpc_service Greeter { + SayHello(HelloRequest):HelloReply; + SayManyHellos(HelloRequest):HelloReply (streaming: "server"); +} diff --git a/grpc/flatbuffers-js-grpc/src/greeter_generated.ts b/grpc/flatbuffers-js-grpc/src/greeter_generated.ts new file mode 100644 index 00000000..87113f98 --- /dev/null +++ b/grpc/flatbuffers-js-grpc/src/greeter_generated.ts @@ -0,0 +1,174 @@ +// automatically generated by the FlatBuffers compiler, do not modify + +/** + * @constructor + */ +export class HelloReply { + bb: flatbuffers.ByteBuffer|null = null; + + bb_pos:number = 0; +/** + * @param number i + * @param flatbuffers.ByteBuffer bb + * @returns HelloReply + */ +__init(i:number, bb:flatbuffers.ByteBuffer):HelloReply { + this.bb_pos = i; + this.bb = bb; + return this; +}; + +/** + * @param flatbuffers.ByteBuffer bb + * @param HelloReply= obj + * @returns HelloReply + */ +static getRootAsHelloReply(bb:flatbuffers.ByteBuffer, obj?:HelloReply):HelloReply { + return (obj || new HelloReply()).__init(bb.readInt32(bb.position()) + bb.position(), bb); +}; + +/** + * @param flatbuffers.ByteBuffer bb + * @param HelloReply= obj + * @returns HelloReply + */ +static getSizePrefixedRootAsHelloReply(bb:flatbuffers.ByteBuffer, obj?:HelloReply):HelloReply { + bb.setPosition(bb.position() + flatbuffers.SIZE_PREFIX_LENGTH); + return (obj || new HelloReply()).__init(bb.readInt32(bb.position()) + bb.position(), bb); +}; + +/** + * @param flatbuffers.Encoding= optionalEncoding + * @returns string|Uint8Array|null + */ +message():string|null +message(optionalEncoding:flatbuffers.Encoding):string|Uint8Array|null +message(optionalEncoding?:any):string|Uint8Array|null { + var offset = this.bb!.__offset(this.bb_pos, 4); + return offset ? this.bb!.__string(this.bb_pos + offset, optionalEncoding) : null; +}; + +/** + * @param flatbuffers.Builder builder + */ +static startHelloReply(builder:flatbuffers.Builder) { + builder.startObject(1); +}; + +/** + * @param flatbuffers.Builder builder + * @param flatbuffers.Offset messageOffset + */ +static addMessage(builder:flatbuffers.Builder, messageOffset:flatbuffers.Offset) { + builder.addFieldOffset(0, messageOffset, 0); +}; + +/** + * @param flatbuffers.Builder builder + * @returns flatbuffers.Offset + */ +static endHelloReply(builder:flatbuffers.Builder):flatbuffers.Offset { + var offset = builder.endObject(); + return offset; +}; + +static createHelloReply(builder:flatbuffers.Builder, messageOffset:flatbuffers.Offset):flatbuffers.Offset { + HelloReply.startHelloReply(builder); + HelloReply.addMessage(builder, messageOffset); + return HelloReply.endHelloReply(builder); +} + +serialize():Uint8Array { + return this.bb!.bytes(); +} + +static deserialize(buffer: Uint8Array):HelloReply { + return HelloReply.getRootAsHelloReply(new flatbuffers.ByteBuffer(buffer)) +} +} +/** + * @constructor + */ +export class HelloRequest { + bb: flatbuffers.ByteBuffer|null = null; + + bb_pos:number = 0; +/** + * @param number i + * @param flatbuffers.ByteBuffer bb + * @returns HelloRequest + */ +__init(i:number, bb:flatbuffers.ByteBuffer):HelloRequest { + this.bb_pos = i; + this.bb = bb; + return this; +}; + +/** + * @param flatbuffers.ByteBuffer bb + * @param HelloRequest= obj + * @returns HelloRequest + */ +static getRootAsHelloRequest(bb:flatbuffers.ByteBuffer, obj?:HelloRequest):HelloRequest { + return (obj || new HelloRequest()).__init(bb.readInt32(bb.position()) + bb.position(), bb); +}; + +/** + * @param flatbuffers.ByteBuffer bb + * @param HelloRequest= obj + * @returns HelloRequest + */ +static getSizePrefixedRootAsHelloRequest(bb:flatbuffers.ByteBuffer, obj?:HelloRequest):HelloRequest { + bb.setPosition(bb.position() + flatbuffers.SIZE_PREFIX_LENGTH); + return (obj || new HelloRequest()).__init(bb.readInt32(bb.position()) + bb.position(), bb); +}; + +/** + * @param flatbuffers.Encoding= optionalEncoding + * @returns string|Uint8Array|null + */ +name():string|null +name(optionalEncoding:flatbuffers.Encoding):string|Uint8Array|null +name(optionalEncoding?:any):string|Uint8Array|null { + var offset = this.bb!.__offset(this.bb_pos, 4); + return offset ? this.bb!.__string(this.bb_pos + offset, optionalEncoding) : null; +}; + +/** + * @param flatbuffers.Builder builder + */ +static startHelloRequest(builder:flatbuffers.Builder) { + builder.startObject(1); +}; + +/** + * @param flatbuffers.Builder builder + * @param flatbuffers.Offset nameOffset + */ +static addName(builder:flatbuffers.Builder, nameOffset:flatbuffers.Offset) { + builder.addFieldOffset(0, nameOffset, 0); +}; + +/** + * @param flatbuffers.Builder builder + * @returns flatbuffers.Offset + */ +static endHelloRequest(builder:flatbuffers.Builder):flatbuffers.Offset { + var offset = builder.endObject(); + return offset; +}; + +static createHelloRequest(builder:flatbuffers.Builder, nameOffset:flatbuffers.Offset):flatbuffers.Offset { + HelloRequest.startHelloRequest(builder); + HelloRequest.addName(builder, nameOffset); + return HelloRequest.endHelloRequest(builder); +} + +serialize():Uint8Array { + return this.bb!.bytes(); +} + +static deserialize(buffer: Uint8Array):HelloRequest { + return HelloRequest.getRootAsHelloRequest(new flatbuffers.ByteBuffer(buffer)) +} +} diff --git a/grpc/flatbuffers-js-grpc/src/greeter_grpc.d.ts b/grpc/flatbuffers-js-grpc/src/greeter_grpc.d.ts new file mode 100644 index 00000000..acd4c2f3 --- /dev/null +++ b/grpc/flatbuffers-js-grpc/src/greeter_grpc.d.ts @@ -0,0 +1,54 @@ +// Generated GRPC code for FlatBuffers TS *** DO NOT EDIT *** +import { flatbuffers } from 'flatbuffers'; +import * as Greeter_fbs from './greeter_generated'; + +import * as grpc from 'grpc'; + +interface IGreeterService extends grpc.ServiceDefinition<grpc.UntypedServiceImplementation> { + SayHello: IGreeterService_ISayHello; + SayManyHellos: IGreeterService_ISayManyHellos; +} +interface IGreeterService_ISayHello extends grpc.MethodDefinition<Greeter_fbs.HelloRequest, Greeter_fbs.HelloReply> { + path: string; // /Greeter/SayHello + requestStream: boolean; // false + responseStream: boolean; // false + requestSerialize: grpc.serialize<Greeter_fbs.HelloRequest>; + requestDeserialize: grpc.deserialize<Greeter_fbs.HelloRequest>; + responseSerialize: grpc.serialize<Greeter_fbs.HelloReply>; + responseDeserialize: grpc.deserialize<Greeter_fbs.HelloReply>; +} + +interface IGreeterService_ISayManyHellos extends grpc.MethodDefinition<Greeter_fbs.HelloRequest, Greeter_fbs.HelloReply> { + path: string; // /Greeter/SayManyHellos + requestStream: boolean; // false + responseStream: boolean; // true + requestSerialize: grpc.serialize<Greeter_fbs.HelloRequest>; + requestDeserialize: grpc.deserialize<Greeter_fbs.HelloRequest>; + responseSerialize: grpc.serialize<Greeter_fbs.HelloReply>; + responseDeserialize: grpc.deserialize<Greeter_fbs.HelloReply>; +} + + +export const GreeterService: IGreeterService; + +export interface IGreeterServer { + SayHello: grpc.handleUnaryCall<Greeter_fbs.HelloRequest, Greeter_fbs.HelloReply>; + SayManyHellos: grpc.handleServerStreamingCall<Greeter_fbs.HelloRequest, Greeter_fbs.HelloReply>; +} + +export interface IGreeterClient { + SayHello(request: Greeter_fbs.HelloRequest, callback: (error: grpc.ServiceError | null, response: Greeter_fbs.HelloReply) => void): grpc.ClientUnaryCall; + SayHello(request: Greeter_fbs.HelloRequest, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: Greeter_fbs.HelloReply) => void): grpc.ClientUnaryCall; + SayHello(request: Greeter_fbs.HelloRequest, metadata: grpc.Metadata, options: Partial<grpc.CallOptions>, callback: (error: grpc.ServiceError | null, response: Greeter_fbs.HelloReply) => void): grpc.ClientUnaryCall; + SayManyHellos(request: Greeter_fbs.HelloRequest, metadata: grpc.Metadata): grpc.ClientReadableStream<Greeter_fbs.HelloReply>; + SayManyHellos(request: Greeter_fbs.HelloRequest, options: Partial<grpc.CallOptions>): grpc.ClientReadableStream<Greeter_fbs.HelloReply>; +} + +export class GreeterClient extends grpc.Client implements IGreeterClient { + constructor(address: string, credentials: grpc.ChannelCredentials, options?: object); public SayHello(request: Greeter_fbs.HelloRequest, callback: (error: grpc.ServiceError | null, response: Greeter_fbs.HelloReply) => void): grpc.ClientUnaryCall; + public SayHello(request: Greeter_fbs.HelloRequest, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: Greeter_fbs.HelloReply) => void): grpc.ClientUnaryCall; + public SayHello(request: Greeter_fbs.HelloRequest, metadata: grpc.Metadata, options: Partial<grpc.CallOptions>, callback: (error: grpc.ServiceError | null, response: Greeter_fbs.HelloReply) => void): grpc.ClientUnaryCall; + public SayManyHellos(request: Greeter_fbs.HelloRequest, metadata: grpc.Metadata): grpc.ClientReadableStream<Greeter_fbs.HelloReply>; + public SayManyHellos(request: Greeter_fbs.HelloRequest, options: Partial<grpc.CallOptions>): grpc.ClientReadableStream<Greeter_fbs.HelloReply>; +} + diff --git a/grpc/flatbuffers-js-grpc/src/greeter_grpc.js b/grpc/flatbuffers-js-grpc/src/greeter_grpc.js new file mode 100644 index 00000000..a184ba15 --- /dev/null +++ b/grpc/flatbuffers-js-grpc/src/greeter_grpc.js @@ -0,0 +1,55 @@ +// Generated GRPC code for FlatBuffers TS *** DO NOT EDIT *** +import { flatbuffers } from 'flatbuffers'; +import * as Greeter_fbs from './greeter_generated'; + +var grpc = require('grpc'); + +function serialize_HelloReply(buffer_args) { + if (!(buffer_args instanceof Greeter_fbs.HelloReply)) { + throw new Error('Expected argument of type Greeter_fbs.HelloReply'); + } + return buffer_args.serialize(); +} + +function deserialize_HelloReply(buffer) { + return Greeter_fbs.HelloReply.getRootAsHelloReply(new flatbuffers.ByteBuffer(buffer)) +} + + +function serialize_HelloRequest(buffer_args) { + if (!(buffer_args instanceof Greeter_fbs.HelloRequest)) { + throw new Error('Expected argument of type Greeter_fbs.HelloRequest'); + } + return buffer_args.serialize(); +} + +function deserialize_HelloRequest(buffer) { + return Greeter_fbs.HelloRequest.getRootAsHelloRequest(new flatbuffers.ByteBuffer(buffer)) +} + + +var GreeterService = exports.GreeterService = { + SayHello: { + path: '/Greeter/SayHello', + requestStream: false, + responseStream: false, + requestType: flatbuffers.ByteBuffer, + responseType: Greeter_fbs.HelloReply, + requestSerialize: serialize_HelloRequest, + requestDeserialize: deserialize_HelloRequest, + responseSerialize: serialize_HelloReply, + responseDeserialize: deserialize_HelloReply, + }, + SayManyHellos: { + path: '/Greeter/SayManyHellos', + requestStream: false, + responseStream: true, + requestType: flatbuffers.ByteBuffer, + responseType: Greeter_fbs.HelloReply, + requestSerialize: serialize_HelloRequest, + requestDeserialize: deserialize_HelloRequest, + responseSerialize: serialize_HelloReply, + responseDeserialize: deserialize_HelloReply, + }, +}; +exports.GreeterClient = grpc.makeGenericClientConstructor(GreeterService); diff --git a/grpc/flatbuffers-js-grpc/src/server.ts b/grpc/flatbuffers-js-grpc/src/server.ts new file mode 100644 index 00000000..47c2f899 --- /dev/null +++ b/grpc/flatbuffers-js-grpc/src/server.ts @@ -0,0 +1,40 @@ +import grpc from 'grpc'; +import { HelloReply, HelloRequest } from './greeter_generated'; +import { IGreeterServer, GreeterService } from './greeter_grpc'; +import { flatbuffers } from 'flatbuffers'; + +class GreeterServer implements IGreeterServer { + + SayHello(call: grpc.ServerUnaryCall<HelloRequest>, callback: grpc.sendUnaryData<HelloReply>): void { + console.log(`${call.request.name()}`); + const builder = new flatbuffers.Builder(); + const offset = builder.createString(`welcome ${call.request.name()}`); + const root = HelloReply.createHelloReply(builder, offset); + builder.finish(root); + callback(null, HelloReply.getRootAsHelloReply(new flatbuffers.ByteBuffer(builder.asUint8Array()))); + } + + async SayManyHellos(call: grpc.ServerWritableStream<HelloRequest>): Promise<void> { + const name = call.request.name(); + console.log(`${call.request.name()} saying hi in different langagues`); + ['Hi', 'Hallo', 'Ciao'].forEach(element => { + const builder = new flatbuffers.Builder(); + const offset = builder.createString(`${element} ${name}`); + const root = HelloReply.createHelloReply(builder, offset); + builder.finish(root); + call.write(HelloReply.getRootAsHelloReply(new flatbuffers.ByteBuffer(builder.asUint8Array()))) + }); + call.end(); + } +} + +function serve(): void { + const PORT = 3000; + const server = new grpc.Server(); + server.addService<IGreeterServer>(GreeterService, new GreeterServer()); + console.log(`Listening on ${PORT}`); + server.bind(`localhost:${PORT}`, grpc.ServerCredentials.createInsecure()); + server.start(); +} + +serve();
\ No newline at end of file diff --git a/grpc/flatbuffers-js-grpc/tsconfig.json b/grpc/flatbuffers-js-grpc/tsconfig.json new file mode 100644 index 00000000..70763787 --- /dev/null +++ b/grpc/flatbuffers-js-grpc/tsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "target": "es5", + "module": "commonjs", + "outDir": "./dist", + "allowJs": true, + "sourceMap": true, + "strict": true, + "noImplicitAny": false, + "strictNullChecks": false, + "esModuleInterop": true, + "baseUrl": "./", + "typeRoots": ["node_modules/@types"], + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true + } + }
\ No newline at end of file diff --git a/grpc/src/compiler/BUILD b/grpc/src/compiler/BUILD index f1599210..23fe5405 100644 --- a/grpc/src/compiler/BUILD +++ b/grpc/src/compiler/BUILD @@ -105,3 +105,19 @@ cc_library( "//:flatbuffers", ], ) + +cc_library( + name = "ts_generator", + srcs = [ + "ts_generator.cc", + ], + hdrs = [ + "ts_generator.h", + ":common_headers", + ], + include_prefix = "src/compiler", + strip_include_prefix = "/grpc/src/compiler", + deps = [ + "//:flatbuffers", + ], +)
\ No newline at end of file diff --git a/grpc/src/compiler/swift_generator.cc b/grpc/src/compiler/swift_generator.cc index a7979fe6..bb731d54 100644 --- a/grpc/src/compiler/swift_generator.cc +++ b/grpc/src/compiler/swift_generator.cc @@ -229,11 +229,13 @@ void GenerateServerProtocol(const grpc_generator::Service *service, printer->Print("}\n\n"); printer->Print(vars, "$ACCESS$ extension $ServiceQualifiedName$Provider {\n"); + printer->Print("\n"); printer->Print(vars, - "\tvar serviceName: String { return " + "\tvar serviceName: Substring { return " "\"$PATH$$ServiceName$\" }\n"); + printer->Print("\n"); printer->Print( - "\tfunc handleMethod(_ methodName: String, callHandlerContext: " + "\tfunc handleMethod(_ methodName: Substring, callHandlerContext: " "CallHandlerContext) -> GRPCCallHandler? {\n"); printer->Print("\t\tswitch methodName {\n"); for (auto it = 0; it < service->method_count(); it++) { diff --git a/grpc/src/compiler/ts_generator.cc b/grpc/src/compiler/ts_generator.cc new file mode 100644 index 00000000..fe9731d9 --- /dev/null +++ b/grpc/src/compiler/ts_generator.cc @@ -0,0 +1,455 @@ +/* + * 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/ts_generator.h" + +namespace grpc_ts_generator { + +// MARK: - Shared code + +void GenerateImports(grpc_generator::Printer *printer, + std::map<grpc::string, grpc::string> *dictonary, + const bool grpc_var_import) { + auto vars = *dictonary; + printer->Print( + "// Generated GRPC code for FlatBuffers TS *** DO NOT EDIT ***\n"); + printer->Print("import { flatbuffers } from 'flatbuffers';\n"); + printer->Print(vars, + "import * as $FBSFile$ from './$Filename$_generated';\n"); + printer->Print("\n"); + if (grpc_var_import) + printer->Print("var grpc = require('grpc');\n"); + else + printer->Print("import * as grpc from 'grpc';\n"); + printer->Print("\n"); +} + +// MARK: - Generate Main GRPC Code + +void GetStreamType(grpc_generator::Printer *printer, + const grpc_generator::Method *method, + std::map<grpc::string, grpc::string> *dictonary) { + auto vars = *dictonary; + auto client_streaming = method->ClientStreaming() || method->BidiStreaming(); + auto server_streaming = method->ServerStreaming() || method->BidiStreaming(); + vars["ClientStreaming"] = client_streaming ? "true" : "false"; + vars["ServerStreaming"] = server_streaming ? "true" : "false"; + printer->Print(vars, "requestStream: $ClientStreaming$,\n"); + printer->Print(vars, "responseStream: $ServerStreaming$,\n"); +} + +void GenerateSerializeMethod(grpc_generator::Printer *printer, + std::map<grpc::string, grpc::string> *dictonary) { + auto vars = *dictonary; + printer->Print(vars, "function serialize_$Type$(buffer_args) {\n"); + printer->Indent(); + printer->Print(vars, "if (!(buffer_args instanceof $FBSFile$.$Type$)) {\n"); + printer->Indent(); + printer->Print( + vars, "throw new Error('Expected argument of type $FBSFile$.$Type$');\n"); + printer->Outdent(); + printer->Print("}\n"); + printer->Print(vars, "return buffer_args.serialize();\n"); + printer->Outdent(); + printer->Print("}\n\n"); +} + +void GenerateDeserializeMethod( + grpc_generator::Printer *printer, + std::map<grpc::string, grpc::string> *dictonary) { + auto vars = *dictonary; + printer->Print(vars, "function deserialize_$Type$(buffer) {\n"); + printer->Indent(); + printer->Print(vars, + "return $FBSFile$.$Type$.getRootAs$Type$(new " + "flatbuffers.ByteBuffer(buffer))\n"); + printer->Outdent(); + printer->Print("}\n\n"); +} + +void GenerateMethods(const grpc_generator::Service *service, + grpc_generator::Printer *printer, + std::map<grpc::string, grpc::string> *dictonary) { + auto vars = *dictonary; + + std::set<grpc::string> generated_functions; + + for (auto it = 0; it < service->method_count(); it++) { + auto method = service->method(it); + auto output = method->get_output_type_name(); + auto input = method->get_input_type_name(); + + if (generated_functions.find(output) == generated_functions.end()) { + generated_functions.insert(output); + vars["Type"] = output; + GenerateSerializeMethod(printer, &vars); + GenerateDeserializeMethod(printer, &vars); + } + printer->Print("\n"); + if (generated_functions.find(input) == generated_functions.end()) { + generated_functions.insert(input); + vars["Type"] = input; + GenerateSerializeMethod(printer, &vars); + GenerateDeserializeMethod(printer, &vars); + } + } +} + +void GenerateService(const grpc_generator::Service *service, + grpc_generator::Printer *printer, + std::map<grpc::string, grpc::string> *dictonary) { + auto vars = *dictonary; + vars["NAME"] = service->name() + "Service"; + + printer->Print(vars, "var $NAME$ = exports.$NAME$ = {\n"); + printer->Indent(); + for (auto it = 0; it < service->method_count(); it++) { + auto method = service->method(it); + vars["MethodName"] = method->name(); + vars["Output"] = method->get_output_type_name(); + vars["Input"] = method->get_input_type_name(); + printer->Print(vars, "$MethodName$: {\n"); + printer->Indent(); + printer->Print(vars, "path: '/$PATH$$ServiceName$/$MethodName$',\n"); + GetStreamType(printer, &*method, &vars); + printer->Print(vars, "requestType: flatbuffers.ByteBuffer,\n"); + printer->Print(vars, "responseType: $FBSFile$.$Output$,\n"); + printer->Print(vars, "requestSerialize: serialize_$Input$,\n"); + printer->Print(vars, "requestDeserialize: deserialize_$Input$,\n"); + printer->Print(vars, "responseSerialize: serialize_$Output$,\n"); + printer->Print(vars, "responseDeserialize: deserialize_$Output$,\n"); + printer->Outdent(); + printer->Print("},\n"); + } + printer->Outdent(); + printer->Print("};\n"); + printer->Print(vars, + "exports.$ServiceName$Client = " + "grpc.makeGenericClientConstructor($NAME$);"); +} + +grpc::string Generate(grpc_generator::File *file, + const grpc_generator::Service *service, + const grpc::string &filename) { + grpc::string output; + std::map<grpc::string, grpc::string> vars; + + vars["PATH"] = file->package(); + + if (!file->package().empty()) { vars["PATH"].append("."); } + + vars["ServiceName"] = service->name(); + vars["FBSFile"] = service->name() + "_fbs"; + vars["Filename"] = filename; + auto printer = file->CreatePrinter(&output); + + GenerateImports(&*printer, &vars, true); + GenerateMethods(service, &*printer, &vars); + GenerateService(service, &*printer, &vars); + return output; +} + +// MARK: - Generate Interface + +void FillInterface(grpc_generator::Printer *printer, + std::map<grpc::string, grpc::string> *dictonary) { + auto vars = *dictonary; + printer->Print( + vars, + "interface I$ServiceName$Service_I$MethodName$ extends " + "grpc.MethodDefinition<$FBSFile$.$INPUT$, $FBSFile$.$OUTPUT$> {\n"); + printer->Indent(); + printer->Print(vars, "path: string; // /$PATH$$ServiceName$/$MethodName$\n"); + printer->Print(vars, "requestStream: boolean; // $ClientStreaming$\n"); + printer->Print(vars, "responseStream: boolean; // $ServerStreaming$\n"); + printer->Print(vars, + "requestSerialize: grpc.serialize<$FBSFile$.$INPUT$>;\n"); + printer->Print(vars, + "requestDeserialize: grpc.deserialize<$FBSFile$.$INPUT$>;\n"); + printer->Print(vars, + "responseSerialize: grpc.serialize<$FBSFile$.$OUTPUT$>;\n"); + printer->Print( + vars, "responseDeserialize: grpc.deserialize<$FBSFile$.$OUTPUT$>;\n"); + printer->Outdent(); + printer->Print("}\n"); +} + +void GenerateInterfaces(const grpc_generator::Service *service, + grpc_generator::Printer *printer, + std::map<grpc::string, grpc::string> *dictonary) { + auto vars = *dictonary; + for (auto it = 0; it < service->method_count(); it++) { + auto method = service->method(it); + auto client_streaming = + method->ClientStreaming() || method->BidiStreaming(); + auto server_streaming = + method->ServerStreaming() || method->BidiStreaming(); + vars["ClientStreaming"] = client_streaming ? "true" : "false"; + vars["ServerStreaming"] = server_streaming ? "true" : "false"; + vars["MethodName"] = method->name(); + vars["INPUT"] = method->get_input_type_name(); + vars["OUTPUT"] = method->get_output_type_name(); + FillInterface(printer, &vars); + printer->Print("\n"); + } +} + +void GenerateExportedInterface( + const grpc_generator::Service *service, grpc_generator::Printer *printer, + std::map<grpc::string, grpc::string> *dictonary) { + auto vars = *dictonary; + printer->Print(vars, "export interface I$ServiceName$Server {\n"); + printer->Indent(); + for (auto it = 0; it < service->method_count(); it++) { + auto method = service->method(it); + vars["Name"] = method->name(); + vars["INPUT"] = method->get_input_type_name(); + vars["OUTPUT"] = method->get_output_type_name(); + if (method->BidiStreaming()) { + printer->Print(vars, + "$Name$: grpc.handleBidiStreamingCall<$FBSFile$.$INPUT$, " + "$FBSFile$.$OUTPUT$>;\n"); + continue; + } + if (method->NoStreaming()) { + printer->Print(vars, + "$Name$: grpc.handleUnaryCall<$FBSFile$.$INPUT$, " + "$FBSFile$.$OUTPUT$>;\n"); + continue; + } + if (method->ClientStreaming()) { + printer->Print( + vars, + "$Name$: grpc.handleClientStreamingCall<$FBSFile$.$INPUT$, " + "$FBSFile$.$OUTPUT$>;\n"); + continue; + } + if (method->ServerStreaming()) { + printer->Print( + vars, + "$Name$: grpc.handleServerStreamingCall<$FBSFile$.$INPUT$, " + "$FBSFile$.$OUTPUT$>;\n"); + continue; + } + } + printer->Outdent(); + printer->Print("}\n"); +} + +void GenerateMainInterface(const grpc_generator::Service *service, + grpc_generator::Printer *printer, + std::map<grpc::string, grpc::string> *dictonary) { + auto vars = *dictonary; + printer->Print( + vars, + "interface I$ServiceName$Service extends " + "grpc.ServiceDefinition<grpc.UntypedServiceImplementation> {\n"); + printer->Indent(); + for (auto it = 0; it < service->method_count(); it++) { + auto method = service->method(it); + vars["MethodName"] = method->name(); + printer->Print(vars, + "$MethodName$: I$ServiceName$Service_I$MethodName$;\n"); + } + printer->Outdent(); + printer->Print("}\n"); + GenerateInterfaces(service, printer, &vars); + printer->Print("\n"); + printer->Print(vars, + "export const $ServiceName$Service: I$ServiceName$Service;\n"); + printer->Print("\n"); + GenerateExportedInterface(service, printer, &vars); +} + +grpc::string GenerateMetaData() { return "metadata: grpc.Metadata"; } + +grpc::string GenerateOptions() { return "options: Partial<grpc.CallOptions>"; } + +void GenerateUnaryClientInterface( + grpc_generator::Printer *printer, + std::map<grpc::string, grpc::string> *dictonary) { + auto vars = *dictonary; + grpc::string main = "$ISPUBLIC$$MethodName$(request: $FBSFile$.$INPUT$, "; + grpc::string callback = + "callback: (error: grpc.ServiceError | null, response: " + "$FBSFile$.$OUTPUT$) => void): grpc.ClientUnaryCall;\n"; + auto meta_data = GenerateMetaData() + ", "; + auto options = GenerateOptions() + ", "; + printer->Print(vars, (main + callback).c_str()); + printer->Print(vars, (main + meta_data + callback).c_str()); + printer->Print(vars, (main + meta_data + options + callback).c_str()); +} + +void GenerateClientWriteStreamInterface( + grpc_generator::Printer *printer, + std::map<grpc::string, grpc::string> *dictonary) { + auto vars = *dictonary; + grpc::string main = "$ISPUBLIC$$MethodName$("; + grpc::string callback = + "callback: (error: grpc.ServiceError | null, response: " + "$FBSFile$.$INPUT$) => void): " + "grpc.ClientWritableStream<$FBSFile$.$OUTPUT$>;\n"; + auto meta_data = GenerateMetaData() + ", "; + auto options = GenerateOptions() + ", "; + printer->Print(vars, (main + callback).c_str()); + printer->Print(vars, (main + meta_data + callback).c_str()); + printer->Print(vars, (main + options + callback).c_str()); + printer->Print(vars, (main + meta_data + options + callback).c_str()); +} + +void GenerateClientReadableStreamInterface( + grpc_generator::Printer *printer, + std::map<grpc::string, grpc::string> *dictonary) { + auto vars = *dictonary; + grpc::string main = "$ISPUBLIC$$MethodName$(request: $FBSFile$.$INPUT$, "; + grpc::string end_function = + "): grpc.ClientReadableStream<$FBSFile$.$OUTPUT$>;\n"; + auto meta_data = GenerateMetaData(); + auto options = GenerateOptions(); + printer->Print(vars, (main + meta_data + end_function).c_str()); + printer->Print(vars, (main + options + end_function).c_str()); +} + +void GenerateDepluxStreamInterface( + grpc_generator::Printer *printer, + std::map<grpc::string, grpc::string> *dictonary) { + auto vars = *dictonary; + grpc::string main = "$ISPUBLIC$$MethodName$("; + grpc::string end_function = + "): grpc.ClientDuplexStream<$FBSFile$.$INPUT$, $FBSFile$.$OUTPUT$>;\n"; + auto meta_data = GenerateMetaData(); + auto options = GenerateOptions(); + printer->Print(vars, (main + end_function).c_str()); + printer->Print(vars, (main + options + end_function).c_str()); + printer->Print(vars, (main + meta_data + + ", options?: Partial<grpc.CallOptions>" + end_function) + .c_str()); +} + +void GenerateClientInterface(const grpc_generator::Service *service, + grpc_generator::Printer *printer, + std::map<grpc::string, grpc::string> *dictonary) { + auto vars = *dictonary; + printer->Print(vars, "export interface I$ServiceName$Client {\n"); + printer->Indent(); + for (auto it = 0; it < service->method_count(); it++) { + auto method = service->method(it); + vars["MethodName"] = method->name(); + vars["INPUT"] = method->get_input_type_name(); + vars["OUTPUT"] = method->get_output_type_name(); + vars["ISPUBLIC"] = ""; + + if (method->NoStreaming()) { + GenerateUnaryClientInterface(printer, &vars); + continue; + } + if (method->BidiStreaming()) { + GenerateDepluxStreamInterface(printer, &vars); + continue; + } + + if (method->ClientStreaming()) { + GenerateClientWriteStreamInterface(printer, &vars); + continue; + } + + if (method->ServerStreaming()) { + GenerateClientReadableStreamInterface(printer, &vars); + continue; + } + } + printer->Outdent(); + printer->Print("}\n"); +} + +void GenerateClientClassInterface( + const grpc_generator::Service *service, grpc_generator::Printer *printer, + std::map<grpc::string, grpc::string> *dictonary) { + auto vars = *dictonary; + printer->Print(vars, + "export class $ServiceName$Client extends grpc.Client " + "implements I$ServiceName$Client {\n"); + printer->Indent(); + printer->Print( + "constructor(address: string, credentials: grpc.ChannelCredentials, " + "options?: object);"); + for (auto it = 0; it < service->method_count(); it++) { + auto method = service->method(it); + vars["MethodName"] = method->name(); + vars["INPUT"] = method->get_input_type_name(); + vars["OUTPUT"] = method->get_output_type_name(); + vars["ISPUBLIC"] = "public "; + if (method->NoStreaming()) { + GenerateUnaryClientInterface(printer, &vars); + continue; + } + if (method->BidiStreaming()) { + GenerateDepluxStreamInterface(printer, &vars); + continue; + } + + if (method->ClientStreaming()) { + GenerateClientWriteStreamInterface(printer, &vars); + continue; + } + + if (method->ServerStreaming()) { + GenerateClientReadableStreamInterface(printer, &vars); + continue; + } + } + printer->Outdent(); + printer->Print("}\n"); +} + +grpc::string GenerateInterface(grpc_generator::File *file, + const grpc_generator::Service *service, + const grpc::string &filename) { + grpc::string output; + + std::set<grpc::string> generated_functions; + std::map<grpc::string, grpc::string> vars; + + vars["PATH"] = file->package(); + + if (!file->package().empty()) { vars["PATH"].append("."); } + + vars["ServiceName"] = service->name(); + vars["FBSFile"] = service->name() + "_fbs"; + vars["Filename"] = filename; + auto printer = file->CreatePrinter(&output); + + GenerateImports(&*printer, &vars, false); + GenerateMainInterface(service, &*printer, &vars); + printer->Print("\n"); + GenerateClientInterface(service, &*printer, &vars); + printer->Print("\n"); + GenerateClientClassInterface(service, &*printer, &vars); + return output; +} +} // namespace grpc_ts_generator diff --git a/grpc/src/compiler/ts_generator.h b/grpc/src/compiler/ts_generator.h new file mode 100644 index 00000000..a33bb3c5 --- /dev/null +++ b/grpc/src/compiler/ts_generator.h @@ -0,0 +1,61 @@ +/* + * + * Copyright 2020, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include <memory> +#include <vector> +#include <set> + +#include "src/compiler/config.h" +#include "src/compiler/schema_interface.h" + +#ifndef GRPC_CUSTOM_STRING +# include <string> +# define GRPC_CUSTOM_STRING std::string +#endif + +namespace grpc { + +typedef GRPC_CUSTOM_STRING string; + +} // namespace grpc + +namespace grpc_ts_generator { +grpc::string Generate(grpc_generator::File *file, + const grpc_generator::Service *service, + const grpc::string &filename); + +grpc::string GenerateInterface(grpc_generator::File *file, + const grpc_generator::Service *service, + const grpc::string &filename); +} // namespace grpc_ts_generator + diff --git a/include/flatbuffers/idl.h b/include/flatbuffers/idl.h index a3c31883..af7eb9b7 100644 --- a/include/flatbuffers/idl.h +++ b/include/flatbuffers/idl.h @@ -1146,6 +1146,8 @@ bool GeneratePythonGRPC(const Parser &parser, const std::string &path, extern bool GenerateSwiftGRPC(const Parser &parser, const std::string &path, const std::string &file_name); +extern bool GenerateTSGRPC(const Parser &parser, const std::string &path, + const std::string &file_name); } // namespace flatbuffers #endif // FLATBUFFERS_IDL_H_ @@ -71,5 +71,6 @@ cc_library( "//grpc/src/compiler:java_generator", "//grpc/src/compiler:python_generator", "//grpc/src/compiler:swift_generator", + "//grpc/src/compiler:ts_generator", ], ) diff --git a/src/flatc_main.cpp b/src/flatc_main.cpp index 0ad26448..c942bdac 100644 --- a/src/flatc_main.cpp +++ b/src/flatc_main.cpp @@ -78,8 +78,8 @@ int main(int argc, const char *argv[]) { { flatbuffers::GenerateDart, "-d", "--dart", "Dart", true, nullptr, flatbuffers::IDLOptions::kDart, "Generate Dart classes for tables/structs", flatbuffers::DartMakeRule }, - { flatbuffers::GenerateJSTS, "-T", "--ts", "TypeScript", true, nullptr, - flatbuffers::IDLOptions::kTs, + { flatbuffers::GenerateJSTS, "-T", "--ts", "TypeScript", true, + flatbuffers::GenerateTSGRPC, flatbuffers::IDLOptions::kTs, "Generate TypeScript code for tables/structs", flatbuffers::JSTSMakeRule }, { flatbuffers::GenerateCSharp, "-n", "--csharp", "C#", true, nullptr, diff --git a/src/idl_gen_cpp.cpp b/src/idl_gen_cpp.cpp index 9cb01696..f6535f0c 100644 --- a/src/idl_gen_cpp.cpp +++ b/src/idl_gen_cpp.cpp @@ -1714,9 +1714,7 @@ class CppGenerator : public BaseGenerator { auto native_default = field.attributes.Lookup("native_default"); // Scalar types get parsed defaults, raw pointers get nullptrs. if (IsScalar(field.value.type.base_type)) { - if (!initializer_list.empty()) { - initializer_list += ",\n "; - } + if (!initializer_list.empty()) { initializer_list += ",\n "; } initializer_list += Name(field); initializer_list += "(" + @@ -1992,8 +1990,7 @@ class CppGenerator : public BaseGenerator { code_ += " {{FIELD_TYPE}}{{FIELD_NAME}}() const {"; code_ += " return {{FIELD_VALUE}};"; code_ += " }"; - } - else { + } else { auto wire_type = GenTypeBasic(type, false); auto face_type = GenTypeBasic(type, true); auto opt_value = "GetOptional<" + wire_type + ", " + face_type + ">(" + @@ -2011,7 +2008,7 @@ class CppGenerator : public BaseGenerator { const auto &type = field.value.type; const bool is_scalar = IsScalar(type.base_type); if (is_scalar && IsUnion(type)) - return; // changing of a union's type is forbidden + return; // changing of a union's type is forbidden auto offset_str = GenFieldOffsetName(field); if (is_scalar) { @@ -2109,9 +2106,7 @@ class CppGenerator : public BaseGenerator { code_.SetValue("FIELD_NAME", Name(field)); GenTableFieldGetter(field); - if (opts_.mutable_buffer) { - GenTableFieldSetter(field); - } + if (opts_.mutable_buffer) { GenTableFieldSetter(field); } auto nested = field.attributes.Lookup("nested_flatbuffer"); if (nested) { @@ -2252,8 +2247,7 @@ class CppGenerator : public BaseGenerator { for (auto it = struct_def.fields.vec.begin(); it != struct_def.fields.vec.end(); ++it) { const auto &field = **it; - if (field.deprecated) - continue; + if (field.deprecated) continue; const bool is_scalar = IsScalar(field.value.type.base_type); const bool is_default_scalar = is_scalar && !field.IsScalarOptional(); const bool is_string = field.value.type.base_type == BASE_TYPE_STRING; @@ -2813,15 +2807,17 @@ class CppGenerator : public BaseGenerator { code_ += "inline " + TableUnPackSignature(struct_def, false, opts_) + " {"; - if(opts_.g_cpp_std == cpp::CPP_STD_X0) { + if (opts_.g_cpp_std == cpp::CPP_STD_X0) { auto native_name = NativeName(WrapInNameSpace(struct_def), &struct_def, parser_.opts); code_.SetValue("POINTER_TYPE", GenTypeNativePtr(native_name, nullptr, false)); code_ += " {{POINTER_TYPE}} _o = {{POINTER_TYPE}}(new {{NATIVE_NAME}}());"; - } else if(opts_.g_cpp_std == cpp::CPP_STD_11) { - code_ += " auto _o = std::unique_ptr<{{NATIVE_NAME}}>(new {{NATIVE_NAME}}());"; + } else if (opts_.g_cpp_std == cpp::CPP_STD_11) { + code_ += + " auto _o = std::unique_ptr<{{NATIVE_NAME}}>(new " + "{{NATIVE_NAME}}());"; } else { code_ += " auto _o = std::make_unique<{{NATIVE_NAME}}>();"; } @@ -2958,8 +2954,7 @@ class CppGenerator : public BaseGenerator { int padding_initializer_id = 0; int padding_body_id = 0; for (auto it = struct_def.fields.vec.begin(); - it != struct_def.fields.vec.end(); - ++it) { + it != struct_def.fields.vec.end(); ++it) { const auto field = *it; const auto field_name = field->name + "_"; diff --git a/src/idl_gen_dart.cpp b/src/idl_gen_dart.cpp index e4dde75f..80fd5834 100644 --- a/src/idl_gen_dart.cpp +++ b/src/idl_gen_dart.cpp @@ -486,8 +486,7 @@ class DartGenerator : public BaseGenerator { auto &part = *it; for (size_t i = 0; i < part.length(); i++) { - if (i && !isdigit(part[i]) && - part[i] == CharToUpper(part[i])) { + if (i && !isdigit(part[i]) && part[i] == CharToUpper(part[i])) { ns += "_"; ns += CharToLower(part[i]); } else { diff --git a/src/idl_gen_grpc.cpp b/src/idl_gen_grpc.cpp index 86a2c593..394ebe32 100644 --- a/src/idl_gen_grpc.cpp +++ b/src/idl_gen_grpc.cpp @@ -26,6 +26,7 @@ #include "src/compiler/python_generator.h" #include "src/compiler/python_private_generator.h" #include "src/compiler/swift_generator.h" +#include "src/compiler/ts_generator.h" #if defined(_MSC_VER) # pragma warning(push) @@ -206,7 +207,8 @@ class FlatBufFile : public grpc_generator::File { kLanguageCpp, kLanguageJava, kLanguagePython, - kLanguageSwift + kLanguageSwift, + kLanguageTS }; FlatBufFile(const Parser &parser, const std::string &file_name, @@ -258,6 +260,9 @@ class FlatBufFile : public grpc_generator::File { case kLanguageSwift: { return ""; } + case kLanguageTS: { + return ""; + } } return ""; } @@ -467,6 +472,54 @@ bool GenerateSwiftGRPC(const Parser &parser, const std::string &path, return SwiftGRPCGenerator(parser, path, file_name).generate(); } +class TSGRPCGenerator : public flatbuffers::BaseGenerator { + private: + CodeWriter code_; + + public: + TSGRPCGenerator(const Parser &parser, const std::string &path, + const std::string &filename) + : BaseGenerator(parser, path, filename, "", "" /*Unused*/, "ts") {} + + bool generate() { + code_.Clear(); + FlatBufFile file(parser_, file_name_, FlatBufFile::kLanguageTS); + + for (int i = 0; i < file.service_count(); i++) { + auto service = file.service(i); + code_ += grpc_ts_generator::Generate(&file, service.get(), file_name_); + const auto ts_name = GeneratedFileName(path_, file_name_); + if (!SaveFile(ts_name.c_str(), code_.ToString(), false)) return false; + + code_.Clear(); + code_ += grpc_ts_generator::GenerateInterface(&file, service.get(), + file_name_); + const auto ts_interface_name = GeneratedFileName(path_, file_name_, true); + if (!SaveFile(ts_interface_name.c_str(), code_.ToString(), false)) + return false; + } + return true; + } + + static std::string GeneratedFileName(const std::string &path, + const std::string &file_name, + const bool is_interface = false) { + if (is_interface) return path + file_name + "_grpc.d.ts"; + return path + file_name + "_grpc.js"; + } +}; + +bool GenerateTSGRPC(const Parser &parser, const std::string &path, + const std::string &file_name) { + int nservices = 0; + for (auto it = parser.services_.vec.begin(); it != parser.services_.vec.end(); + ++it) { + if (!(*it)->generated) nservices++; + } + if (!nservices) return true; + return TSGRPCGenerator(parser, path, file_name).generate(); +} + } // namespace flatbuffers #if defined(_MSC_VER) diff --git a/src/idl_gen_js_ts.cpp b/src/idl_gen_js_ts.cpp index 02b85bd2..71b68b85 100644 --- a/src/idl_gen_js_ts.cpp +++ b/src/idl_gen_js_ts.cpp @@ -141,7 +141,8 @@ class JsTsGenerator : public BaseGenerator { const auto &file = *it; const auto basename = flatbuffers::StripPath(flatbuffers::StripExtension(file.first)); - if (basename != file_name_ && imported.find(file.second.symbol) == imported.end()) { + if (basename != file_name_ && + imported.find(file.second.symbol) == imported.end()) { if (imported_files.find(file.first) == imported_files.end()) { code += GenPrefixedImport(file.first, basename); imported_files.emplace(file.first); @@ -225,9 +226,7 @@ class JsTsGenerator : public BaseGenerator { for (auto it = sorted_namespaces.begin(); it != sorted_namespaces.end(); ++it) { if (lang_.language == IDLOptions::kTs) { - if (it->find('.') == std::string::npos) { - break; - } + if (it->find('.') == std::string::npos) { break; } } else { code += "/**\n * @const\n * @namespace\n */\n"; if (it->find('.') == std::string::npos) { @@ -992,8 +991,8 @@ class JsTsGenerator : public BaseGenerator { std::string constructor_func = "constructor("; constructor_func += (struct_def.fields.vec.empty() ? "" : "\n"); - - const auto has_create = struct_def.fixed || CanCreateFactoryMethod(struct_def); + const auto has_create = + struct_def.fixed || CanCreateFactoryMethod(struct_def); std::string pack_func_prototype = "/**\n * " + @@ -1004,14 +1003,13 @@ class JsTsGenerator : public BaseGenerator { std::string pack_func_offset_decl; std::string pack_func_create_call; - const auto struct_name = GenPrefixedTypeName(WrapInNameSpace(struct_def), struct_def.file); + const auto struct_name = + GenPrefixedTypeName(WrapInNameSpace(struct_def), struct_def.file); if (has_create) { - pack_func_create_call = - " return " + - struct_name + - ".create" + Verbose(struct_def) + "(builder" + - (struct_def.fields.vec.empty() ? "" : ",\n "); + pack_func_create_call = " return " + struct_name + ".create" + + Verbose(struct_def) + "(builder" + + (struct_def.fields.vec.empty() ? "" : ",\n "); } else { pack_func_create_call = " " + struct_name + ".start(builder);\n"; } @@ -1231,7 +1229,9 @@ class JsTsGenerator : public BaseGenerator { if (has_create) { pack_func_create_call += field_offset_val; } else { - pack_func_create_call += " " + struct_name + ".add" + MakeCamel(field.name) + "(builder, " + field_offset_val + ");\n"; + pack_func_create_call += " " + struct_name + ".add" + + MakeCamel(field.name) + "(builder, " + + field_offset_val + ");\n"; } } @@ -1239,7 +1239,9 @@ class JsTsGenerator : public BaseGenerator { constructor_annotation += "\n"; constructor_func += ",\n"; - if (!struct_def.fixed && has_create) { pack_func_create_call += ",\n "; } + if (!struct_def.fixed && has_create) { + pack_func_create_call += ",\n "; + } unpack_func += ",\n"; unpack_to_func += "\n"; @@ -1698,11 +1700,12 @@ class JsTsGenerator : public BaseGenerator { if (struct_def.fixed) { code += " " + GenBBAccess() + ".write" + - MakeCamel(GenType(field.value.type)) + - "(this.bb_pos + " + NumToString(field.value.offset) + ", "; + MakeCamel(GenType(field.value.type)) + "(this.bb_pos + " + + NumToString(field.value.offset) + ", "; } else { - code += " var offset = " + GenBBAccess() + ".__offset(this.bb_pos, " + - NumToString(field.value.offset) + ");\n\n"; + code += " var offset = " + GenBBAccess() + + ".__offset(this.bb_pos, " + NumToString(field.value.offset) + + ");\n\n"; code += " if (offset === 0) {\n"; code += " return false;\n"; code += " }\n\n"; @@ -2063,6 +2066,21 @@ class JsTsGenerator : public BaseGenerator { } } + if (!struct_def.fixed && parser_.services_.vec.size() != 0 && + lang_.language == IDLOptions::kTs) { + auto name = Verbose(struct_def, ""); + code += "\n"; + code += "serialize():Uint8Array {\n"; + code += " return this.bb!.bytes();\n"; + code += "}\n"; + + code += "\n"; + code += "static deserialize(buffer: Uint8Array):"+ name +" {\n"; + code += " return " + name + ".getRootAs" + name + + "(new flatbuffers.ByteBuffer(buffer))\n"; + code += "}\n"; + } + if (lang_.language == IDLOptions::kTs) { if (parser_.opts.generate_object_based_api) { std::string obj_api_class; diff --git a/src/idl_gen_kotlin.cpp b/src/idl_gen_kotlin.cpp index 79913f54..80416d5e 100644 --- a/src/idl_gen_kotlin.cpp +++ b/src/idl_gen_kotlin.cpp @@ -303,12 +303,15 @@ class KotlinGenerator : public BaseGenerator { } writer += ")"; }); - GenerateFunOneLine(writer, "name", "e: Int", "String", [&]() { - writer += "names[e\\"; - if (enum_def.MinValue()->IsNonZero()) - writer += " - " + enum_def.MinValue()->name + ".toInt()\\"; - writer += "]"; - }, parser_.opts.gen_jvmstatic); + GenerateFunOneLine( + writer, "name", "e: Int", "String", + [&]() { + writer += "names[e\\"; + if (enum_def.MinValue()->IsNonZero()) + writer += " - " + enum_def.MinValue()->name + ".toInt()\\"; + writer += "]"; + }, + parser_.opts.gen_jvmstatic); } }); writer.DecrementIdentLevel(); @@ -449,7 +452,8 @@ class KotlinGenerator : public BaseGenerator { return key_offset; } - void GenStruct(StructDef &struct_def, CodeWriter &writer, IDLOptions options) const { + void GenStruct(StructDef &struct_def, CodeWriter &writer, + IDLOptions options) const { if (struct_def.generated) return; GenerateComment(struct_def.doc_comment, writer, &comment_config); @@ -488,9 +492,10 @@ class KotlinGenerator : public BaseGenerator { // Generate verson check method. // Force compile time error if not using the same version // runtime. - GenerateFunOneLine(writer, "validateVersion", "", "", [&]() { - writer += "Constants.FLATBUFFERS_1_12_0()"; - }, options.gen_jvmstatic); + GenerateFunOneLine( + writer, "validateVersion", "", "", + [&]() { writer += "Constants.FLATBUFFERS_1_12_0()"; }, + options.gen_jvmstatic); GenerateGetRootAsAccessors(Esc(struct_def.name), writer, options); GenerateBufferHasIdentifier(struct_def, writer, options); @@ -520,8 +525,10 @@ class KotlinGenerator : public BaseGenerator { GenerateEndStructMethod(struct_def, writer, options); auto file_identifier = parser_.file_identifier_; if (parser_.root_struct_def_ == &struct_def) { - GenerateFinishStructBuffer(struct_def, file_identifier, writer, options); - GenerateFinishSizePrefixed(struct_def, file_identifier, writer, options); + GenerateFinishStructBuffer(struct_def, file_identifier, writer, + options); + GenerateFinishSizePrefixed(struct_def, file_identifier, writer, + options); } if (struct_def.has_key) { @@ -606,9 +613,10 @@ class KotlinGenerator : public BaseGenerator { auto id = identifier.length() > 0 ? ", \"" + identifier + "\"" : ""; auto params = "builder: FlatBufferBuilder, offset: Int"; auto method_name = "finishSizePrefixed" + Esc(struct_def.name) + "Buffer"; - GenerateFunOneLine(writer, method_name, params, "", [&]() { - writer += "builder.finishSizePrefixed(offset" + id + ")"; - }, options.gen_jvmstatic); + GenerateFunOneLine( + writer, method_name, params, "", + [&]() { writer += "builder.finishSizePrefixed(offset" + id + ")"; }, + options.gen_jvmstatic); } void GenerateFinishStructBuffer(StructDef &struct_def, const std::string &identifier, @@ -617,9 +625,10 @@ class KotlinGenerator : public BaseGenerator { auto id = identifier.length() > 0 ? ", \"" + identifier + "\"" : ""; auto params = "builder: FlatBufferBuilder, offset: Int"; auto method_name = "finish" + Esc(struct_def.name) + "Buffer"; - GenerateFunOneLine(writer, method_name, params, "", - [&]() { writer += "builder.finish(offset" + id + ")"; }, - options.gen_jvmstatic); + GenerateFunOneLine( + writer, method_name, params, "", + [&]() { writer += "builder.finish(offset" + id + ")"; }, + options.gen_jvmstatic); } void GenerateEndStructMethod(StructDef &struct_def, CodeWriter &writer, @@ -630,18 +639,21 @@ class KotlinGenerator : public BaseGenerator { auto returns = "Int"; auto field_vec = struct_def.fields.vec; - GenerateFun(writer, name, params, returns, [&]() { - writer += "val o = builder.endTable()"; - writer.IncrementIdentLevel(); - for (auto it = field_vec.begin(); it != field_vec.end(); ++it) { - auto &field = **it; - if (field.deprecated || !field.required) { continue; } - writer.SetValue("offset", NumToString(field.value.offset)); - writer += "builder.required(o, {{offset}})"; - } - writer.DecrementIdentLevel(); - writer += "return o"; - }, options.gen_jvmstatic); + GenerateFun( + writer, name, params, returns, + [&]() { + writer += "val o = builder.endTable()"; + writer.IncrementIdentLevel(); + for (auto it = field_vec.begin(); it != field_vec.end(); ++it) { + auto &field = **it; + if (field.deprecated || !field.required) { continue; } + writer.SetValue("offset", NumToString(field.value.offset)); + writer += "builder.required(o, {{offset}})"; + } + writer.DecrementIdentLevel(); + writer += "return o"; + }, + options.gen_jvmstatic); } // Generate a method to create a vector from a Kotlin array. @@ -656,15 +668,18 @@ class KotlinGenerator : public BaseGenerator { writer.SetValue("root", GenMethod(vector_type)); writer.SetValue("cast", CastToSigned(vector_type)); - GenerateFun(writer, method_name, params, "Int", [&]() { - writer += "builder.startVector({{size}}, data.size, {{align}})"; - writer += "for (i in data.size - 1 downTo 0) {"; - writer.IncrementIdentLevel(); - writer += "builder.add{{root}}(data[i]{{cast}})"; - writer.DecrementIdentLevel(); - writer += "}"; - writer += "return builder.endVector()"; - }, options.gen_jvmstatic); + GenerateFun( + writer, method_name, params, "Int", + [&]() { + writer += "builder.startVector({{size}}, data.size, {{align}})"; + writer += "for (i in data.size - 1 downTo 0) {"; + writer.IncrementIdentLevel(); + writer += "builder.add{{root}}(data[i]{{cast}})"; + writer.DecrementIdentLevel(); + writer += "}"; + writer += "return builder.endVector()"; + }, + options.gen_jvmstatic); } void GenerateStartVectorField(FieldDef &field, CodeWriter &writer, @@ -678,9 +693,11 @@ class KotlinGenerator : public BaseGenerator { GenerateFunOneLine( writer, "start" + MakeCamel(Esc(field.name) + "Vector", true), params, - "", [&]() { + "", + [&]() { writer += "builder.startVector({{size}}, numElems, {{align}})"; - }, options.gen_jvmstatic); + }, + options.gen_jvmstatic); } void GenerateAddField(std::string field_pos, FieldDef &field, @@ -745,12 +762,13 @@ class KotlinGenerator : public BaseGenerator { // fun startMonster(builder: FlatBufferBuilder) = builder.startTable(11) void GenerateStartStructMethod(StructDef &struct_def, CodeWriter &code, const IDLOptions options) const { - GenerateFunOneLine(code, "start" + Esc(struct_def.name), - "builder: FlatBufferBuilder", "", [&]() { - code += "builder.startTable(" + - NumToString(struct_def.fields.vec.size()) + - ")"; - }, options.gen_jvmstatic); + GenerateFunOneLine( + code, "start" + Esc(struct_def.name), "builder: FlatBufferBuilder", "", + [&]() { + code += "builder.startTable(" + + NumToString(struct_def.fields.vec.size()) + ")"; + }, + options.gen_jvmstatic); } void GenerateTableCreator(StructDef &struct_def, CodeWriter &writer, @@ -793,51 +811,59 @@ class KotlinGenerator : public BaseGenerator { params << GenTypeBasic(field.value.type.base_type) << optional; } - GenerateFun(writer, name, params.str(), "Int", [&]() { - writer.SetValue("vec_size", NumToString(fields_vec.size())); - - writer += "builder.startTable({{vec_size}})"; - - auto sortbysize = struct_def.sortbysize; - auto largest = sortbysize ? sizeof(largest_scalar_t) : 1; - for (size_t size = largest; size; size /= 2) { - for (auto it = fields_vec.rbegin(); it != fields_vec.rend(); ++it) { - auto &field = **it; - auto base_type_size = SizeOf(field.value.type.base_type); - if (!field.deprecated && (!sortbysize || size == base_type_size)) { - writer.SetValue("camel_field_name", - MakeCamel(Esc(field.name), true)); - writer.SetValue("field_name", MakeCamel(Esc(field.name), false)); - - // we wrap on null check for scalar optionals - writer += - field.IsScalarOptional() ? "{{field_name}}?.run { \\" : "\\"; - - writer += "add{{camel_field_name}}(builder, {{field_name}}\\"; - if (!IsScalar(field.value.type.base_type)) { - writer += "Offset\\"; + GenerateFun( + writer, name, params.str(), "Int", + [&]() { + writer.SetValue("vec_size", NumToString(fields_vec.size())); + + writer += "builder.startTable({{vec_size}})"; + + auto sortbysize = struct_def.sortbysize; + auto largest = sortbysize ? sizeof(largest_scalar_t) : 1; + for (size_t size = largest; size; size /= 2) { + for (auto it = fields_vec.rbegin(); it != fields_vec.rend(); + ++it) { + auto &field = **it; + auto base_type_size = SizeOf(field.value.type.base_type); + if (!field.deprecated && + (!sortbysize || size == base_type_size)) { + writer.SetValue("camel_field_name", + MakeCamel(Esc(field.name), true)); + writer.SetValue("field_name", + MakeCamel(Esc(field.name), false)); + + // we wrap on null check for scalar optionals + writer += field.IsScalarOptional() + ? "{{field_name}}?.run { \\" + : "\\"; + + writer += "add{{camel_field_name}}(builder, {{field_name}}\\"; + if (!IsScalar(field.value.type.base_type)) { + writer += "Offset\\"; + } + // we wrap on null check for scalar optionals + writer += field.IsScalarOptional() ? ") }" : ")"; + } } - // we wrap on null check for scalar optionals - writer += field.IsScalarOptional() ? ") }" : ")"; } - } - } - writer += "return end{{struct_name}}(builder)"; - }, options.gen_jvmstatic); + writer += "return end{{struct_name}}(builder)"; + }, + options.gen_jvmstatic); } } - void GenerateBufferHasIdentifier(StructDef &struct_def, - CodeWriter &writer, IDLOptions options) const { + void GenerateBufferHasIdentifier(StructDef &struct_def, CodeWriter &writer, + IDLOptions options) const { auto file_identifier = parser_.file_identifier_; // Check if a buffer has the identifier. if (parser_.root_struct_def_ != &struct_def || !file_identifier.length()) return; auto name = MakeCamel(Esc(struct_def.name), false); - GenerateFunOneLine(writer, name + "BufferHasIdentifier", "_bb: ByteBuffer", - "Boolean", [&]() { - writer += "__has_identifier(_bb, \"" + - file_identifier + "\")"; - }, options.gen_jvmstatic); + GenerateFunOneLine( + writer, name + "BufferHasIdentifier", "_bb: ByteBuffer", "Boolean", + [&]() { + writer += "__has_identifier(_bb, \"" + file_identifier + "\")"; + }, + options.gen_jvmstatic); } void GenerateStructGetters(StructDef &struct_def, CodeWriter &writer) const { @@ -996,12 +1022,11 @@ class KotlinGenerator : public BaseGenerator { break; } case BASE_TYPE_UNION: - GenerateFun(writer, field_name, "obj: " + field_type, return_type, - [&]() { - writer += OffsetWrapperOneLine( - offset_val, bbgetter + "(obj, o + bb_pos)", - "null"); - }); + GenerateFun( + writer, field_name, "obj: " + field_type, return_type, [&]() { + writer += OffsetWrapperOneLine( + offset_val, bbgetter + "(obj, o + bb_pos)", "null"); + }); break; default: FLATBUFFERS_ASSERT(0); } @@ -1293,10 +1318,13 @@ class KotlinGenerator : public BaseGenerator { const IDLOptions options) { // create a struct constructor function auto params = StructConstructorParams(struct_def); - GenerateFun(code, "create" + Esc(struct_def.name), params, "Int", [&]() { - GenStructBody(struct_def, code, ""); - code += "return builder.offset()"; - }, options.gen_jvmstatic); + GenerateFun( + code, "create" + Esc(struct_def.name), params, "Int", + [&]() { + GenStructBody(struct_def, code, ""); + code += "return builder.offset()"; + }, + options.gen_jvmstatic); } static std::string StructConstructorParams(const StructDef &struct_def, @@ -1482,9 +1510,7 @@ class KotlinGenerator : public BaseGenerator { // Prepend @JvmStatic to methods in companion object. static void GenerateJvmStaticAnnotation(CodeWriter &code, bool gen_jvmstatic) { - if (gen_jvmstatic) { - code += "@JvmStatic"; - } + if (gen_jvmstatic) { code += "@JvmStatic"; } } // This tracks the current namespace used to determine if a type need to be diff --git a/src/idl_gen_rust.cpp b/src/idl_gen_rust.cpp index 1e7502ae..16c8dfa1 100644 --- a/src/idl_gen_rust.cpp +++ b/src/idl_gen_rust.cpp @@ -47,9 +47,7 @@ std::string MakeSnakeCase(const std::string &in) { // Convert a string to all uppercase. std::string MakeUpper(const std::string &in) { std::string s; - for (size_t i = 0; i < in.length(); i++) { - s += CharToUpper(in[i]); - } + for (size_t i = 0; i < in.length(); i++) { s += CharToUpper(in[i]); } return s; } @@ -668,8 +666,8 @@ class RustGenerator : public BaseGenerator { return field.optional ? "None" : field.value.constant; } case ftBool: { - return field.optional ? "None" : - field.value.constant == "0" ? "false" : "true"; + return field.optional ? "None" + : field.value.constant == "0" ? "false" : "true"; } case ftUnionKey: case ftEnumKey: { @@ -868,9 +866,9 @@ class RustGenerator : public BaseGenerator { case ftBool: case ftFloat: { const auto typname = GetTypeBasic(field.value.type); - return (field.optional ? - "self.fbb_.push_slot_always::<" : - "self.fbb_.push_slot::<") + typname + ">"; + return (field.optional ? "self.fbb_.push_slot_always::<" + : "self.fbb_.push_slot::<") + + typname + ">"; } case ftEnumKey: case ftUnionKey: { @@ -1005,7 +1003,7 @@ class RustGenerator : public BaseGenerator { const auto default_value = GetDefaultScalarValue(field); return "self._tab.get::<" + typname + ">(" + offset_name + ", Some(" + default_value + ")).unwrap()"; - } + } } case ftStruct: { const auto typname = WrapInNameSpace(*type.struct_def); diff --git a/src/idl_parser.cpp b/src/idl_parser.cpp index 566482d2..d2c729a7 100644 --- a/src/idl_parser.cpp +++ b/src/idl_parser.cpp @@ -757,7 +757,8 @@ CheckedError Parser::ParseField(StructDef &struct_def) { type.enum_def->name + "'."); } if (field->attributes.Lookup("key")) { - return Error("only a non-optional scalar field can be used as a 'key' field"); + return Error( + "only a non-optional scalar field can be used as a 'key' field"); } if (!SupportsOptionalScalars()) { return Error( @@ -862,7 +863,7 @@ CheckedError Parser::ParseField(StructDef &struct_def) { if (field->required && (struct_def.fixed || IsScalar(type.base_type))) return Error("only non-scalar fields in tables may be 'required'"); - if(!IsScalar(type.base_type)) { + if (!IsScalar(type.base_type)) { // For nonscalars, only required fields are non-optional. // At least until https://github.com/google/flatbuffers/issues/6053 field->optional = !field->required; @@ -1230,7 +1231,7 @@ CheckedError Parser::ParseTable(const StructDef &struct_def, std::string *value, if (!struct_def.sortbysize || size == SizeOf(field_value.type.base_type)) { switch (field_value.type.base_type) { - // clang-format off +// clang-format off #define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, ...) \ case BASE_TYPE_ ## ENUM: \ builder_.Pad(field->padding); \ @@ -1369,7 +1370,7 @@ CheckedError Parser::ParseVector(const Type &type, uoffset_t *ovalue, // start at the back, since we're building the data backwards. auto &val = field_stack_.back().first; switch (val.type.base_type) { - // clang-format off +// clang-format off #define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE,...) \ case BASE_TYPE_ ## ENUM: \ if (IsStruct(val.type)) SerializeStruct(*val.type.struct_def, val); \ @@ -2279,7 +2280,7 @@ CheckedError Parser::CheckClash(std::vector<FieldDef *> &fields, return NoError(); } -bool Parser::SupportsOptionalScalars(const flatbuffers::IDLOptions &opts){ +bool Parser::SupportsOptionalScalars(const flatbuffers::IDLOptions &opts) { static FLATBUFFERS_CONSTEXPR unsigned long supported_langs = IDLOptions::kRust | IDLOptions::kSwift | IDLOptions::kLobster | IDLOptions::kKotlin | IDLOptions::kCpp; diff --git a/src/reflection.cpp b/src/reflection.cpp index cb941a02..2dedcb4f 100644 --- a/src/reflection.cpp +++ b/src/reflection.cpp @@ -23,7 +23,7 @@ namespace flatbuffers { int64_t GetAnyValueI(reflection::BaseType type, const uint8_t *data) { - // clang-format off +// clang-format off #define FLATBUFFERS_GET(T) static_cast<int64_t>(ReadScalar<T>(data)) switch (type) { case reflection::UType: @@ -121,7 +121,7 @@ std::string GetAnyValueS(reflection::BaseType type, const uint8_t *data, } void SetAnyValueI(reflection::BaseType type, uint8_t *data, int64_t val) { - // clang-format off +// clang-format off #define FLATBUFFERS_SET(T) WriteScalar(data, static_cast<T>(val)) switch (type) { case reflection::UType: diff --git a/tests/FlatBuffers.GRPC.Swift/Sources/Model/greeter.grpc.swift b/tests/FlatBuffers.GRPC.Swift/Sources/Model/greeter.grpc.swift index 43b81574..f3c4b675 100644 --- a/tests/FlatBuffers.GRPC.Swift/Sources/Model/greeter.grpc.swift +++ b/tests/FlatBuffers.GRPC.Swift/Sources/Model/greeter.grpc.swift @@ -49,8 +49,10 @@ public protocol GreeterProvider: CallHandlerProvider { } public extension GreeterProvider { - var serviceName: String { return "Greeter" } - func handleMethod(_ methodName: String, callHandlerContext: CallHandlerContext) -> GRPCCallHandler? { + + var serviceName: Substring { return "Greeter" } + + func handleMethod(_ methodName: Substring, callHandlerContext: CallHandlerContext) -> GRPCCallHandler? { switch methodName { case "SayHello": return CallHandlerFactory.makeUnary(callHandlerContext: callHandlerContext) { context in diff --git a/tests/FlatBuffers.GRPC.Swift/Sources/client/main.swift b/tests/FlatBuffers.GRPC.Swift/Sources/client/main.swift index 45915dd9..74729e7e 100644 --- a/tests/FlatBuffers.GRPC.Swift/Sources/client/main.swift +++ b/tests/FlatBuffers.GRPC.Swift/Sources/client/main.swift @@ -36,6 +36,7 @@ func greet(name: String, client greeter: GreeterServiceClient) { // Make the RPC call to the server. let sayHello = greeter.SayHello(Message<HelloRequest>(builder: &builder)) + // wait() on the response to stop the program from exiting before the response is received. do { let response = try sayHello.response.wait() @@ -43,15 +44,15 @@ func greet(name: String, client greeter: GreeterServiceClient) { } catch { print("Greeter failed: \(error)") } - + let surname = builder.create(string: "Name") let manyRoot = ManyHellosRequest.createManyHellosRequest(&builder, offsetOfName: surname, numGreetings: 2) builder.finish(offset: manyRoot) - + let call = greeter.SayManyHellos(Message(builder: &builder)) { message in print(message.object.message) } - + let status = try! call.status.recover { _ in .processingError }.wait() if status.code != .ok { print("RPC failed: \(status)") @@ -83,10 +84,15 @@ func main(args: [String]) { // Configure the channel, we're not using TLS so the connection is `insecure`. let channel = ClientConnection.insecure(group: group) .connect(host: "localhost", port: port) - + + // Close the connection when we're done with it. + defer { + try! channel.close().wait() + } + // Provide the connection to the generated client. let greeter = GreeterServiceClient(channel: channel) - + // Do the greeting. greet(name: name ?? "Hello FlatBuffers!", client: greeter) } diff --git a/tests/FlatBuffers.GRPC.Swift/Sources/server/main.swift b/tests/FlatBuffers.GRPC.Swift/Sources/server/main.swift index b361b256..396c151e 100644 --- a/tests/FlatBuffers.GRPC.Swift/Sources/server/main.swift +++ b/tests/FlatBuffers.GRPC.Swift/Sources/server/main.swift @@ -40,7 +40,7 @@ class Greeter: GreeterProvider { context: StatusOnlyCallContext ) -> EventLoopFuture<Message<HelloReply>> { let recipient = request.object.name ?? "Stranger" - + var builder = FlatBufferBuilder() let off = builder.create(string: recipient) let root = HelloReply.createHelloReply(&builder, offsetOfMessage: off) diff --git a/tests/FlatBuffers.Test.Swift/Tests/FlatBuffers.Test.SwiftTests/monster_test.grpc.swift b/tests/FlatBuffers.Test.Swift/Tests/FlatBuffers.Test.SwiftTests/monster_test.grpc.swift index 66bac373..b2f3ff27 100644 --- a/tests/FlatBuffers.Test.Swift/Tests/FlatBuffers.Test.SwiftTests/monster_test.grpc.swift +++ b/tests/FlatBuffers.Test.Swift/Tests/FlatBuffers.Test.SwiftTests/monster_test.grpc.swift @@ -61,8 +61,10 @@ public protocol MyGame_Example_MonsterStorageProvider: CallHandlerProvider { } public extension MyGame_Example_MonsterStorageProvider { - var serviceName: String { return "MyGame.Example.MonsterStorage" } - func handleMethod(_ methodName: String, callHandlerContext: CallHandlerContext) -> GRPCCallHandler? { + + var serviceName: Substring { return "MyGame.Example.MonsterStorage" } + + func handleMethod(_ methodName: Substring, callHandlerContext: CallHandlerContext) -> GRPCCallHandler? { switch methodName { case "Store": return CallHandlerFactory.makeUnary(callHandlerContext: callHandlerContext) { context in diff --git a/tests/monster_test_generated.ts b/tests/monster_test_generated.ts index 9ba72541..bc4b084b 100644 --- a/tests/monster_test_generated.ts +++ b/tests/monster_test_generated.ts @@ -210,6 +210,14 @@ static createInParentNamespace(builder:flatbuffers.Builder):flatbuffers.Offset { return InParentNamespace.endInParentNamespace(builder); } +serialize():Uint8Array { + return this.bb!.bytes(); +} + +static deserialize(buffer: Uint8Array):InParentNamespace { + return InParentNamespace.getRootAsInParentNamespace(new flatbuffers.ByteBuffer(buffer)) +} + /** * @returns InParentNamespaceT */ @@ -297,6 +305,14 @@ static createMonster(builder:flatbuffers.Builder):flatbuffers.Offset { return Monster.endMonster(builder); } +serialize():Uint8Array { + return this.bb!.bytes(); +} + +static deserialize(buffer: Uint8Array):Monster { + return Monster.getRootAsMonster(new flatbuffers.ByteBuffer(buffer)) +} + /** * @returns MonsterT */ @@ -531,6 +547,14 @@ static createTestSimpleTableWithEnum(builder:flatbuffers.Builder, color:MyGame.E return TestSimpleTableWithEnum.endTestSimpleTableWithEnum(builder); } +serialize():Uint8Array { + return this.bb!.bytes(); +} + +static deserialize(buffer: Uint8Array):TestSimpleTableWithEnum { + return TestSimpleTableWithEnum.getRootAsTestSimpleTableWithEnum(new flatbuffers.ByteBuffer(buffer)) +} + /** * @returns TestSimpleTableWithEnumT */ @@ -1031,6 +1055,14 @@ static createStat(builder:flatbuffers.Builder, idOffset:flatbuffers.Offset, val: return Stat.endStat(builder); } +serialize():Uint8Array { + return this.bb!.bytes(); +} + +static deserialize(buffer: Uint8Array):Stat { + return Stat.getRootAsStat(new flatbuffers.ByteBuffer(buffer)) +} + /** * @returns StatT */ @@ -1171,6 +1203,14 @@ static createReferrable(builder:flatbuffers.Builder, id:flatbuffers.Long):flatbu return Referrable.endReferrable(builder); } +serialize():Uint8Array { + return this.bb!.bytes(); +} + +static deserialize(buffer: Uint8Array):Referrable { + return Referrable.getRootAsReferrable(new flatbuffers.ByteBuffer(buffer)) +} + /** * @returns ReferrableT */ @@ -2922,6 +2962,14 @@ static finishSizePrefixedMonsterBuffer(builder:flatbuffers.Builder, offset:flatb }; +serialize():Uint8Array { + return this.bb!.bytes(); +} + +static deserialize(buffer: Uint8Array):Monster { + return Monster.getRootAsMonster(new flatbuffers.ByteBuffer(buffer)) +} + /** * @returns MonsterT */ @@ -3741,6 +3789,14 @@ static createTypeAliases(builder:flatbuffers.Builder, i8:number, u8:number, i16: return TypeAliases.endTypeAliases(builder); } +serialize():Uint8Array { + return this.bb!.bytes(); +} + +static deserialize(buffer: Uint8Array):TypeAliases { + return TypeAliases.getRootAsTypeAliases(new flatbuffers.ByteBuffer(buffer)) +} + /** * @returns TypeAliasesT */ diff --git a/tests/monster_test_grpc.d.ts b/tests/monster_test_grpc.d.ts new file mode 100644 index 00000000..128c6022 --- /dev/null +++ b/tests/monster_test_grpc.d.ts @@ -0,0 +1,92 @@ +// Generated GRPC code for FlatBuffers TS *** DO NOT EDIT *** +import { flatbuffers } from 'flatbuffers'; +import * as MonsterStorage_fbs from './monster_test_generated'; + +import * as grpc from 'grpc'; + +interface IMonsterStorageService extends grpc.ServiceDefinition<grpc.UntypedServiceImplementation> { + Store: IMonsterStorageService_IStore; + Retrieve: IMonsterStorageService_IRetrieve; + GetMaxHitPoint: IMonsterStorageService_IGetMaxHitPoint; + GetMinMaxHitPoints: IMonsterStorageService_IGetMinMaxHitPoints; +} +interface IMonsterStorageService_IStore extends grpc.MethodDefinition<MonsterStorage_fbs.Monster, MonsterStorage_fbs.Stat> { + path: string; // /MyGame.Example.MonsterStorage/Store + requestStream: boolean; // false + responseStream: boolean; // false + requestSerialize: grpc.serialize<MonsterStorage_fbs.Monster>; + requestDeserialize: grpc.deserialize<MonsterStorage_fbs.Monster>; + responseSerialize: grpc.serialize<MonsterStorage_fbs.Stat>; + responseDeserialize: grpc.deserialize<MonsterStorage_fbs.Stat>; +} + +interface IMonsterStorageService_IRetrieve extends grpc.MethodDefinition<MonsterStorage_fbs.Stat, MonsterStorage_fbs.Monster> { + path: string; // /MyGame.Example.MonsterStorage/Retrieve + requestStream: boolean; // false + responseStream: boolean; // true + requestSerialize: grpc.serialize<MonsterStorage_fbs.Stat>; + requestDeserialize: grpc.deserialize<MonsterStorage_fbs.Stat>; + responseSerialize: grpc.serialize<MonsterStorage_fbs.Monster>; + responseDeserialize: grpc.deserialize<MonsterStorage_fbs.Monster>; +} + +interface IMonsterStorageService_IGetMaxHitPoint extends grpc.MethodDefinition<MonsterStorage_fbs.Monster, MonsterStorage_fbs.Stat> { + path: string; // /MyGame.Example.MonsterStorage/GetMaxHitPoint + requestStream: boolean; // true + responseStream: boolean; // false + requestSerialize: grpc.serialize<MonsterStorage_fbs.Monster>; + requestDeserialize: grpc.deserialize<MonsterStorage_fbs.Monster>; + responseSerialize: grpc.serialize<MonsterStorage_fbs.Stat>; + responseDeserialize: grpc.deserialize<MonsterStorage_fbs.Stat>; +} + +interface IMonsterStorageService_IGetMinMaxHitPoints extends grpc.MethodDefinition<MonsterStorage_fbs.Monster, MonsterStorage_fbs.Stat> { + path: string; // /MyGame.Example.MonsterStorage/GetMinMaxHitPoints + requestStream: boolean; // true + responseStream: boolean; // true + requestSerialize: grpc.serialize<MonsterStorage_fbs.Monster>; + requestDeserialize: grpc.deserialize<MonsterStorage_fbs.Monster>; + responseSerialize: grpc.serialize<MonsterStorage_fbs.Stat>; + responseDeserialize: grpc.deserialize<MonsterStorage_fbs.Stat>; +} + + +export const MonsterStorageService: IMonsterStorageService; + +export interface IMonsterStorageServer { + Store: grpc.handleUnaryCall<MonsterStorage_fbs.Monster, MonsterStorage_fbs.Stat>; + Retrieve: grpc.handleServerStreamingCall<MonsterStorage_fbs.Stat, MonsterStorage_fbs.Monster>; + GetMaxHitPoint: grpc.handleClientStreamingCall<MonsterStorage_fbs.Monster, MonsterStorage_fbs.Stat>; + GetMinMaxHitPoints: grpc.handleBidiStreamingCall<MonsterStorage_fbs.Monster, MonsterStorage_fbs.Stat>; +} + +export interface IMonsterStorageClient { + Store(request: MonsterStorage_fbs.Monster, callback: (error: grpc.ServiceError | null, response: MonsterStorage_fbs.Stat) => void): grpc.ClientUnaryCall; + Store(request: MonsterStorage_fbs.Monster, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: MonsterStorage_fbs.Stat) => void): grpc.ClientUnaryCall; + Store(request: MonsterStorage_fbs.Monster, metadata: grpc.Metadata, options: Partial<grpc.CallOptions>, callback: (error: grpc.ServiceError | null, response: MonsterStorage_fbs.Stat) => void): grpc.ClientUnaryCall; + Retrieve(request: MonsterStorage_fbs.Stat, metadata: grpc.Metadata): grpc.ClientReadableStream<MonsterStorage_fbs.Monster>; + Retrieve(request: MonsterStorage_fbs.Stat, options: Partial<grpc.CallOptions>): grpc.ClientReadableStream<MonsterStorage_fbs.Monster>; + GetMaxHitPoint(callback: (error: grpc.ServiceError | null, response: MonsterStorage_fbs.Monster) => void): grpc.ClientWritableStream<MonsterStorage_fbs.Stat>; + GetMaxHitPoint(metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: MonsterStorage_fbs.Monster) => void): grpc.ClientWritableStream<MonsterStorage_fbs.Stat>; + GetMaxHitPoint(options: Partial<grpc.CallOptions>, callback: (error: grpc.ServiceError | null, response: MonsterStorage_fbs.Monster) => void): grpc.ClientWritableStream<MonsterStorage_fbs.Stat>; + GetMaxHitPoint(metadata: grpc.Metadata, options: Partial<grpc.CallOptions>, callback: (error: grpc.ServiceError | null, response: MonsterStorage_fbs.Monster) => void): grpc.ClientWritableStream<MonsterStorage_fbs.Stat>; + GetMinMaxHitPoints(): grpc.ClientDuplexStream<MonsterStorage_fbs.Monster, MonsterStorage_fbs.Stat>; + GetMinMaxHitPoints(options: Partial<grpc.CallOptions>): grpc.ClientDuplexStream<MonsterStorage_fbs.Monster, MonsterStorage_fbs.Stat>; + GetMinMaxHitPoints(metadata: grpc.Metadata, options?: Partial<grpc.CallOptions>): grpc.ClientDuplexStream<MonsterStorage_fbs.Monster, MonsterStorage_fbs.Stat>; +} + +export class MonsterStorageClient extends grpc.Client implements IMonsterStorageClient { + constructor(address: string, credentials: grpc.ChannelCredentials, options?: object); public Store(request: MonsterStorage_fbs.Monster, callback: (error: grpc.ServiceError | null, response: MonsterStorage_fbs.Stat) => void): grpc.ClientUnaryCall; + public Store(request: MonsterStorage_fbs.Monster, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: MonsterStorage_fbs.Stat) => void): grpc.ClientUnaryCall; + public Store(request: MonsterStorage_fbs.Monster, metadata: grpc.Metadata, options: Partial<grpc.CallOptions>, callback: (error: grpc.ServiceError | null, response: MonsterStorage_fbs.Stat) => void): grpc.ClientUnaryCall; + public Retrieve(request: MonsterStorage_fbs.Stat, metadata: grpc.Metadata): grpc.ClientReadableStream<MonsterStorage_fbs.Monster>; + public Retrieve(request: MonsterStorage_fbs.Stat, options: Partial<grpc.CallOptions>): grpc.ClientReadableStream<MonsterStorage_fbs.Monster>; + public GetMaxHitPoint(callback: (error: grpc.ServiceError | null, response: MonsterStorage_fbs.Monster) => void): grpc.ClientWritableStream<MonsterStorage_fbs.Stat>; + public GetMaxHitPoint(metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: MonsterStorage_fbs.Monster) => void): grpc.ClientWritableStream<MonsterStorage_fbs.Stat>; + public GetMaxHitPoint(options: Partial<grpc.CallOptions>, callback: (error: grpc.ServiceError | null, response: MonsterStorage_fbs.Monster) => void): grpc.ClientWritableStream<MonsterStorage_fbs.Stat>; + public GetMaxHitPoint(metadata: grpc.Metadata, options: Partial<grpc.CallOptions>, callback: (error: grpc.ServiceError | null, response: MonsterStorage_fbs.Monster) => void): grpc.ClientWritableStream<MonsterStorage_fbs.Stat>; + public GetMinMaxHitPoints(): grpc.ClientDuplexStream<MonsterStorage_fbs.Monster, MonsterStorage_fbs.Stat>; + public GetMinMaxHitPoints(options: Partial<grpc.CallOptions>): grpc.ClientDuplexStream<MonsterStorage_fbs.Monster, MonsterStorage_fbs.Stat>; + public GetMinMaxHitPoints(metadata: grpc.Metadata, options?: Partial<grpc.CallOptions>): grpc.ClientDuplexStream<MonsterStorage_fbs.Monster, MonsterStorage_fbs.Stat>; +} + diff --git a/tests/monster_test_grpc.js b/tests/monster_test_grpc.js new file mode 100644 index 00000000..c12d7895 --- /dev/null +++ b/tests/monster_test_grpc.js @@ -0,0 +1,79 @@ +// Generated GRPC code for FlatBuffers TS *** DO NOT EDIT *** +import { flatbuffers } from 'flatbuffers'; +import * as MonsterStorage_fbs from './monster_test_generated'; + +var grpc = require('grpc'); + +function serialize_Stat(buffer_args) { + if (!(buffer_args instanceof MonsterStorage_fbs.Stat)) { + throw new Error('Expected argument of type MonsterStorage_fbs.Stat'); + } + return buffer_args.serialize(); +} + +function deserialize_Stat(buffer) { + return MonsterStorage_fbs.Stat.getRootAsStat(new flatbuffers.ByteBuffer(buffer)) +} + + +function serialize_Monster(buffer_args) { + if (!(buffer_args instanceof MonsterStorage_fbs.Monster)) { + throw new Error('Expected argument of type MonsterStorage_fbs.Monster'); + } + return buffer_args.serialize(); +} + +function deserialize_Monster(buffer) { + return MonsterStorage_fbs.Monster.getRootAsMonster(new flatbuffers.ByteBuffer(buffer)) +} + + + + +var MonsterStorageService = exports.MonsterStorageService = { + Store: { + path: '/MyGame.Example.MonsterStorage/Store', + requestStream: false, + responseStream: false, + requestType: flatbuffers.ByteBuffer, + responseType: MonsterStorage_fbs.Stat, + requestSerialize: serialize_Monster, + requestDeserialize: deserialize_Monster, + responseSerialize: serialize_Stat, + responseDeserialize: deserialize_Stat, + }, + Retrieve: { + path: '/MyGame.Example.MonsterStorage/Retrieve', + requestStream: false, + responseStream: true, + requestType: flatbuffers.ByteBuffer, + responseType: MonsterStorage_fbs.Monster, + requestSerialize: serialize_Stat, + requestDeserialize: deserialize_Stat, + responseSerialize: serialize_Monster, + responseDeserialize: deserialize_Monster, + }, + GetMaxHitPoint: { + path: '/MyGame.Example.MonsterStorage/GetMaxHitPoint', + requestStream: true, + responseStream: false, + requestType: flatbuffers.ByteBuffer, + responseType: MonsterStorage_fbs.Stat, + requestSerialize: serialize_Monster, + requestDeserialize: deserialize_Monster, + responseSerialize: serialize_Stat, + responseDeserialize: deserialize_Stat, + }, + GetMinMaxHitPoints: { + path: '/MyGame.Example.MonsterStorage/GetMinMaxHitPoints', + requestStream: true, + responseStream: true, + requestType: flatbuffers.ByteBuffer, + responseType: MonsterStorage_fbs.Stat, + requestSerialize: serialize_Monster, + requestDeserialize: deserialize_Monster, + responseSerialize: serialize_Stat, + responseDeserialize: deserialize_Stat, + }, +}; +exports.MonsterStorageClient = grpc.makeGenericClientConstructor(MonsterStorageService); |