Coverage Report

Created: 2026-07-05 06:38

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/flatbuffers/grpc/src/compiler/ts_generator.cc
Line
Count
Source
1
/*
2
 * Copyright 2020 Google Inc. All rights reserved.
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at
7
 *
8
 *     http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16
17
/*
18
 * NOTE: The following implementation is a translation for the Swift-grpc
19
 * generator since flatbuffers doesnt allow plugins for now. if an issue arises
20
 * please open an issue in the flatbuffers repository. This file should always
21
 * be maintained according to the Swift-grpc repository
22
 */
23
24
#include "src/compiler/ts_generator.h"
25
26
#include <map>
27
#include <sstream>
28
29
#include "flatbuffers/util.h"
30
#include "src/compiler/schema_interface.h"
31
32
namespace grpc_ts_generator {
33
namespace {
34
35
static grpc::string GenerateNamespace(const std::vector<std::string> ns,
36
                                      const std::string filename,
37
0
                                      const bool include_separator) {
38
0
  grpc::string path = "";
39
0
  if (include_separator) path += ".";
40
41
0
  for (auto it = ns.begin(); it < ns.end(); it++) {
42
0
    if (include_separator) path += "/";
43
0
    path += include_separator
44
0
                ? flatbuffers::ConvertCase(*it, flatbuffers::Case::kDasher,
45
0
                                           flatbuffers::Case::kUpperCamel)
46
0
                : *it + "_";
47
0
  }
48
49
0
  if (include_separator) path += "/";
50
0
  path += include_separator
51
0
              ? flatbuffers::ConvertCase(filename, flatbuffers::Case::kDasher,
52
0
                                         flatbuffers::Case::kUpperCamel)
53
0
              : filename;
54
0
  return path;
55
0
}
56
57
// MARK: - Shared code
58
59
static void GenerateImports(const grpc_generator::Service* service,
60
                            grpc_generator::Printer* printer,
61
                            std::map<grpc::string, grpc::string>* dictonary,
62
0
                            const bool grpc_var_import) {
63
0
  auto vars = *dictonary;
64
0
  printer->Print(
65
0
      "// Generated GRPC code for FlatBuffers TS *** DO NOT EDIT ***\n");
66
0
  printer->Print("import * as flatbuffers from 'flatbuffers';\n");
67
68
0
  std::set<grpc::string> generated_imports;
69
70
0
  for (auto it = 0; it < service->method_count(); it++) {
71
0
    auto method = service->method(it);
72
0
    auto output = method->get_output_type_name();
73
0
    auto input = method->get_input_type_name();
74
0
    auto input_namespace = method->get_input_namespace_parts();
75
76
0
    vars["OUTPUT"] = output;
77
0
    vars["INPUT"] = input;
78
79
0
    if (generated_imports.find(output) == generated_imports.end()) {
80
0
      generated_imports.insert(output);
81
0
      vars["OUTPUT_DIR"] =
82
0
          GenerateNamespace(method->get_output_namespace_parts(), output, true);
83
0
      vars["Output_alias"] = GenerateNamespace(
84
0
          method->get_output_namespace_parts(), output, false);
85
0
      printer->Print(
86
0
          vars, "import { $OUTPUT$ as $Output_alias$ } from '$OUTPUT_DIR$';\n");
87
0
    }
88
0
    if (generated_imports.find(input) == generated_imports.end()) {
89
0
      generated_imports.insert(input);
90
0
      vars["INPUT_DIR"] =
91
0
          GenerateNamespace(method->get_output_namespace_parts(), input, true);
92
0
      vars["Input_alias"] =
93
0
          GenerateNamespace(method->get_output_namespace_parts(), input, false);
94
0
      printer->Print(
95
0
          vars, "import { $INPUT$ as $Input_alias$ } from '$INPUT_DIR$';\n");
96
0
    }
97
0
  }
98
0
  printer->Print("\n");
99
0
  if (grpc_var_import)
100
0
    printer->Print("var grpc = require('@grpc/grpc-js');\n");
101
0
  else
102
0
    printer->Print("import * as grpc from '@grpc/grpc-js';\n");
103
0
  printer->Print("\n");
104
0
}
105
106
// MARK: - Generate Main GRPC Code
107
108
static void GetStreamType(grpc_generator::Printer* printer,
109
                          const grpc_generator::Method* method,
110
0
                          std::map<grpc::string, grpc::string>* dictonary) {
111
0
  auto vars = *dictonary;
112
0
  auto client_streaming = method->ClientStreaming() || method->BidiStreaming();
113
0
  auto server_streaming = method->ServerStreaming() || method->BidiStreaming();
114
0
  vars["ClientStreaming"] = client_streaming ? "true" : "false";
115
0
  vars["ServerStreaming"] = server_streaming ? "true" : "false";
116
0
  printer->Print(vars, "requestStream: $ClientStreaming$,\n");
117
0
  printer->Print(vars, "responseStream: $ServerStreaming$,\n");
118
0
}
119
120
static void GenerateSerializeMethod(
121
    grpc_generator::Printer* printer,
122
0
    std::map<grpc::string, grpc::string>* dictonary) {
123
0
  auto vars = *dictonary;
124
0
  printer->Print(vars, "function serialize_$Type$(buffer_args) {\n");
125
0
  printer->Indent();
126
0
  printer->Print(vars, "if (!(buffer_args instanceof $Type$)) {\n");
127
0
  printer->Indent();
128
0
  printer->Print(vars,
129
0
                 "throw new Error('Expected argument of type $VALUE$');\n");
130
0
  printer->Outdent();
131
0
  printer->Print("}\n");
132
0
  printer->Print(vars, "return Buffer.from(buffer_args.serialize());\n");
133
0
  printer->Outdent();
134
0
  printer->Print("}\n\n");
135
0
}
136
137
static void GenerateDeserializeMethod(
138
    grpc_generator::Printer* printer,
139
0
    std::map<grpc::string, grpc::string>* dictonary) {
140
0
  auto vars = *dictonary;
141
0
  printer->Print(vars, "function deserialize_$Type$(buffer) {\n");
142
0
  printer->Indent();
143
0
  printer->Print(vars,
144
0
                 "return $Type$.getRootAs$VALUE$(new "
145
0
                 "flatbuffers.ByteBuffer(buffer))\n");
146
0
  printer->Outdent();
147
0
  printer->Print("}\n\n");
148
0
}
149
150
static void GenerateMethods(const grpc_generator::Service* service,
151
                            grpc_generator::Printer* printer,
152
0
                            std::map<grpc::string, grpc::string>* dictonary) {
153
0
  auto vars = *dictonary;
154
155
0
  std::set<grpc::string> generated_functions;
156
157
0
  for (auto it = 0; it < service->method_count(); it++) {
158
0
    auto method = service->method(it);
159
0
    auto output = method->get_output_type_name();
160
0
    auto input = method->get_input_type_name();
161
162
0
    if (generated_functions.find(output) == generated_functions.end()) {
163
0
      generated_functions.insert(output);
164
0
      vars["VALUE"] = output;
165
0
      vars["Type"] = GenerateNamespace(method->get_output_namespace_parts(),
166
0
                                       output, false);
167
0
      GenerateSerializeMethod(printer, &vars);
168
0
      GenerateDeserializeMethod(printer, &vars);
169
0
    }
170
0
    printer->Print("\n");
171
0
    if (generated_functions.find(input) == generated_functions.end()) {
172
0
      generated_functions.insert(input);
173
0
      vars["VALUE"] = input;
174
0
      vars["Type"] =
175
0
          GenerateNamespace(method->get_input_namespace_parts(), input, false);
176
0
      GenerateSerializeMethod(printer, &vars);
177
0
      GenerateDeserializeMethod(printer, &vars);
178
0
    }
179
0
  }
180
0
}
181
182
static void GenerateService(const grpc_generator::Service* service,
183
                            grpc_generator::Printer* printer,
184
0
                            std::map<grpc::string, grpc::string>* dictonary) {
185
0
  auto vars = *dictonary;
186
0
  vars["NAME"] = service->name() + "Service";
187
188
0
  printer->Print(vars, "var $NAME$ = exports.$NAME$ = {\n");
189
0
  printer->Indent();
190
0
  for (auto it = 0; it < service->method_count(); it++) {
191
0
    auto method = service->method(it);
192
0
    vars["MethodName"] = method->name();
193
0
    vars["OUTPUT"] = GenerateNamespace(method->get_output_namespace_parts(),
194
0
                                       method->get_output_type_name(), false);
195
0
    vars["INPUT"] = GenerateNamespace(method->get_input_namespace_parts(),
196
0
                                      method->get_input_type_name(), false);
197
0
    printer->Print(vars, "$MethodName$: {\n");
198
0
    printer->Indent();
199
0
    printer->Print(vars, "path: '/$PATH$$ServiceName$/$MethodName$',\n");
200
0
    GetStreamType(printer, &*method, &vars);
201
0
    printer->Print(vars, "requestType: flatbuffers.ByteBuffer,\n");
202
0
    printer->Print(vars, "responseType: $OUTPUT$,\n");
203
0
    printer->Print(vars, "requestSerialize: serialize_$INPUT$,\n");
204
0
    printer->Print(vars, "requestDeserialize: deserialize_$INPUT$,\n");
205
0
    printer->Print(vars, "responseSerialize: serialize_$OUTPUT$,\n");
206
0
    printer->Print(vars, "responseDeserialize: deserialize_$OUTPUT$,\n");
207
0
    printer->Outdent();
208
0
    printer->Print("},\n");
209
0
  }
210
0
  printer->Outdent();
211
0
  printer->Print("};\n");
212
0
  printer->Print(vars,
213
0
                 "exports.$ServiceName$Client = "
214
0
                 "grpc.makeGenericClientConstructor($NAME$);");
215
0
}
216
217
}  // namespace
218
219
grpc::string Generate(grpc_generator::File* file,
220
                      const grpc_generator::Service* service,
221
0
                      const grpc::string& filename) {
222
0
  grpc::string output;
223
0
  std::map<grpc::string, grpc::string> vars;
224
225
0
  vars["PATH"] = file->package();
226
227
0
  if (!file->package().empty()) {
228
0
    vars["PATH"].append(".");
229
0
  }
230
231
0
  vars["ServiceName"] = service->name();
232
0
  vars["FBSFile"] = service->name() + "_fbs";
233
0
  vars["Filename"] = filename;
234
0
  auto printer = file->CreatePrinter(&output);
235
236
0
  GenerateImports(service, &*printer, &vars, true);
237
0
  GenerateMethods(service, &*printer, &vars);
238
0
  GenerateService(service, &*printer, &vars);
239
0
  return output;
240
0
}
241
242
namespace {
243
244
// MARK: - Generate Interface
245
246
static void FillInterface(grpc_generator::Printer* printer,
247
0
                          std::map<grpc::string, grpc::string>* dictonary) {
248
0
  auto vars = *dictonary;
249
0
  printer->Print(vars,
250
0
                 "interface I$ServiceName$Service_I$MethodName$ extends "
251
0
                 "grpc.MethodDefinition<$INPUT$, $OUTPUT$> {\n");
252
0
  printer->Indent();
253
0
  printer->Print(vars, "path: string; // /$PATH$$ServiceName$/$MethodName$\n");
254
0
  printer->Print(vars, "requestStream: boolean; // $ClientStreaming$\n");
255
0
  printer->Print(vars, "responseStream: boolean; // $ServerStreaming$\n");
256
0
  printer->Print(vars, "requestSerialize: grpc.serialize<$INPUT$>;\n");
257
0
  printer->Print(vars, "requestDeserialize: grpc.deserialize<$INPUT$>;\n");
258
0
  printer->Print(vars, "responseSerialize: grpc.serialize<$OUTPUT$>;\n");
259
0
  printer->Print(vars, "responseDeserialize: grpc.deserialize<$OUTPUT$>;\n");
260
0
  printer->Outdent();
261
0
  printer->Print("}\n");
262
0
}
263
264
static void GenerateInterfaces(
265
    const grpc_generator::Service* service, grpc_generator::Printer* printer,
266
0
    std::map<grpc::string, grpc::string>* dictonary) {
267
0
  auto vars = *dictonary;
268
0
  for (auto it = 0; it < service->method_count(); it++) {
269
0
    auto method = service->method(it);
270
0
    auto client_streaming =
271
0
        method->ClientStreaming() || method->BidiStreaming();
272
0
    auto server_streaming =
273
0
        method->ServerStreaming() || method->BidiStreaming();
274
0
    vars["ClientStreaming"] = client_streaming ? "true" : "false";
275
0
    vars["ServerStreaming"] = server_streaming ? "true" : "false";
276
0
    vars["MethodName"] = method->name();
277
0
    vars["OUTPUT"] = GenerateNamespace(method->get_output_namespace_parts(),
278
0
                                       method->get_output_type_name(), false);
279
0
    vars["INPUT"] = GenerateNamespace(method->get_input_namespace_parts(),
280
0
                                      method->get_input_type_name(), false);
281
0
    FillInterface(printer, &vars);
282
0
    printer->Print("\n");
283
0
  }
284
0
}
285
286
static void GenerateExportedInterface(
287
    const grpc_generator::Service* service, grpc_generator::Printer* printer,
288
0
    std::map<grpc::string, grpc::string>* dictonary) {
289
0
  auto vars = *dictonary;
290
0
  printer->Print(vars,
291
0
                 "export interface I$ServiceName$Server extends "
292
0
                 "grpc.UntypedServiceImplementation {\n");
293
0
  printer->Indent();
294
0
  for (auto it = 0; it < service->method_count(); it++) {
295
0
    auto method = service->method(it);
296
0
    vars["Name"] = method->name();
297
0
    vars["OUTPUT"] = GenerateNamespace(method->get_output_namespace_parts(),
298
0
                                       method->get_output_type_name(), false);
299
0
    vars["INPUT"] = GenerateNamespace(method->get_input_namespace_parts(),
300
0
                                      method->get_input_type_name(), false);
301
0
    if (method->BidiStreaming()) {
302
0
      printer->Print(vars,
303
0
                     "$Name$: grpc.handleBidiStreamingCall<$INPUT$, "
304
0
                     "$OUTPUT$>;\n");
305
0
      continue;
306
0
    }
307
0
    if (method->NoStreaming()) {
308
0
      printer->Print(vars,
309
0
                     "$Name$: grpc.handleUnaryCall<$INPUT$, "
310
0
                     "$OUTPUT$>;\n");
311
0
      continue;
312
0
    }
313
0
    if (method->ClientStreaming()) {
314
0
      printer->Print(vars,
315
0
                     "$Name$: grpc.handleClientStreamingCall<$INPUT$, "
316
0
                     "$OUTPUT$>;\n");
317
0
      continue;
318
0
    }
319
0
    if (method->ServerStreaming()) {
320
0
      printer->Print(vars,
321
0
                     "$Name$: grpc.handleServerStreamingCall<$INPUT$, "
322
0
                     "$OUTPUT$>;\n");
323
0
      continue;
324
0
    }
325
0
  }
326
0
  printer->Outdent();
327
0
  printer->Print("}\n");
328
0
}
329
330
static void GenerateMainInterface(
331
    const grpc_generator::Service* service, grpc_generator::Printer* printer,
332
0
    std::map<grpc::string, grpc::string>* dictonary) {
333
0
  auto vars = *dictonary;
334
0
  printer->Print(
335
0
      vars,
336
0
      "interface I$ServiceName$Service extends "
337
0
      "grpc.ServiceDefinition<grpc.UntypedServiceImplementation> {\n");
338
0
  printer->Indent();
339
0
  for (auto it = 0; it < service->method_count(); it++) {
340
0
    auto method = service->method(it);
341
0
    vars["MethodName"] = method->name();
342
0
    printer->Print(vars,
343
0
                   "$MethodName$: I$ServiceName$Service_I$MethodName$;\n");
344
0
  }
345
0
  printer->Outdent();
346
0
  printer->Print("}\n");
347
0
  GenerateInterfaces(service, printer, &vars);
348
0
  printer->Print("\n");
349
0
  printer->Print(vars,
350
0
                 "export const $ServiceName$Service: I$ServiceName$Service;\n");
351
0
  printer->Print("\n");
352
0
  GenerateExportedInterface(service, printer, &vars);
353
0
}
354
355
0
static grpc::string GenerateMetaData() { return "metadata: grpc.Metadata"; }
356
357
0
static grpc::string GenerateOptions() {
358
0
  return "options: Partial<grpc.CallOptions>";
359
0
}
360
361
static void GenerateUnaryClientInterface(
362
    grpc_generator::Printer* printer,
363
0
    std::map<grpc::string, grpc::string>* dictonary) {
364
0
  auto vars = *dictonary;
365
0
  grpc::string main = "$ISPUBLIC$$MethodName$(request: $INPUT$, ";
366
0
  grpc::string callback =
367
0
      "callback: (error: grpc.ServiceError | null, response: "
368
0
      "$OUTPUT$) => void): grpc.ClientUnaryCall;\n";
369
0
  auto meta_data = GenerateMetaData() + ", ";
370
0
  auto options = GenerateOptions() + ", ";
371
0
  printer->Print(vars, (main + callback).c_str());
372
0
  printer->Print(vars, (main + meta_data + callback).c_str());
373
0
  printer->Print(vars, (main + meta_data + options + callback).c_str());
374
0
}
375
376
static void GenerateClientWriteStreamInterface(
377
    grpc_generator::Printer* printer,
378
0
    std::map<grpc::string, grpc::string>* dictonary) {
379
0
  auto vars = *dictonary;
380
0
  grpc::string main = "$ISPUBLIC$$MethodName$(";
381
0
  grpc::string callback =
382
0
      "callback: (error: grpc.ServiceError | null, response: "
383
0
      "$INPUT$) => void): "
384
0
      "grpc.ClientWritableStream<$OUTPUT$>;\n";
385
0
  auto meta_data = GenerateMetaData() + ", ";
386
0
  auto options = GenerateOptions() + ", ";
387
0
  printer->Print(vars, (main + callback).c_str());
388
0
  printer->Print(vars, (main + meta_data + callback).c_str());
389
0
  printer->Print(vars, (main + options + callback).c_str());
390
0
  printer->Print(vars, (main + meta_data + options + callback).c_str());
391
0
}
392
393
static void GenerateClientReadableStreamInterface(
394
    grpc_generator::Printer* printer,
395
0
    std::map<grpc::string, grpc::string>* dictonary) {
396
0
  auto vars = *dictonary;
397
0
  grpc::string main = "$ISPUBLIC$$MethodName$(request: $INPUT$, ";
398
0
  grpc::string end_function = "): grpc.ClientReadableStream<$OUTPUT$>;\n";
399
0
  auto meta_data = GenerateMetaData();
400
0
  auto options = GenerateOptions();
401
0
  printer->Print(vars, (main + meta_data + end_function).c_str());
402
0
  printer->Print(vars, (main + options + end_function).c_str());
403
0
}
404
405
static void GenerateDepluxStreamInterface(
406
    grpc_generator::Printer* printer,
407
0
    std::map<grpc::string, grpc::string>* dictonary) {
408
0
  auto vars = *dictonary;
409
0
  grpc::string main = "$ISPUBLIC$$MethodName$(";
410
0
  grpc::string end_function =
411
0
      "): grpc.ClientDuplexStream<$INPUT$, $OUTPUT$>;\n";
412
0
  auto meta_data = GenerateMetaData();
413
0
  auto options = GenerateOptions();
414
0
  printer->Print(vars, (main + end_function).c_str());
415
0
  printer->Print(vars, (main + options + end_function).c_str());
416
0
  printer->Print(vars, (main + meta_data +
417
0
                        ", options?: Partial<grpc.CallOptions>" + end_function)
418
0
                           .c_str());
419
0
}
420
421
static void GenerateClientInterface(
422
    const grpc_generator::Service* service, grpc_generator::Printer* printer,
423
0
    std::map<grpc::string, grpc::string>* dictonary) {
424
0
  auto vars = *dictonary;
425
0
  printer->Print(vars, "export interface I$ServiceName$Client {\n");
426
0
  printer->Indent();
427
0
  for (auto it = 0; it < service->method_count(); it++) {
428
0
    auto method = service->method(it);
429
0
    vars["MethodName"] = method->name();
430
0
    vars["OUTPUT"] = GenerateNamespace(method->get_output_namespace_parts(),
431
0
                                       method->get_output_type_name(), false);
432
0
    vars["INPUT"] = GenerateNamespace(method->get_input_namespace_parts(),
433
0
                                      method->get_input_type_name(), false);
434
0
    vars["ISPUBLIC"] = "";
435
436
0
    if (method->NoStreaming()) {
437
0
      GenerateUnaryClientInterface(printer, &vars);
438
0
      continue;
439
0
    }
440
0
    if (method->BidiStreaming()) {
441
0
      GenerateDepluxStreamInterface(printer, &vars);
442
0
      continue;
443
0
    }
444
445
0
    if (method->ClientStreaming()) {
446
0
      GenerateClientWriteStreamInterface(printer, &vars);
447
0
      continue;
448
0
    }
449
450
0
    if (method->ServerStreaming()) {
451
0
      GenerateClientReadableStreamInterface(printer, &vars);
452
0
      continue;
453
0
    }
454
0
  }
455
0
  printer->Outdent();
456
0
  printer->Print("}\n");
457
0
}
458
459
static void GenerateClientClassInterface(
460
    const grpc_generator::Service* service, grpc_generator::Printer* printer,
461
0
    std::map<grpc::string, grpc::string>* dictonary) {
462
0
  auto vars = *dictonary;
463
0
  printer->Print(vars,
464
0
                 "export class $ServiceName$Client extends grpc.Client "
465
0
                 "implements I$ServiceName$Client {\n");
466
0
  printer->Indent();
467
0
  printer->Print(
468
0
      "constructor(address: string, credentials: grpc.ChannelCredentials, "
469
0
      "options?: object);\n");
470
0
  for (auto it = 0; it < service->method_count(); it++) {
471
0
    auto method = service->method(it);
472
0
    vars["MethodName"] = method->name();
473
0
    vars["OUTPUT"] = GenerateNamespace(method->get_output_namespace_parts(),
474
0
                                       method->get_output_type_name(), false);
475
0
    vars["INPUT"] = GenerateNamespace(method->get_input_namespace_parts(),
476
0
                                      method->get_input_type_name(), false);
477
0
    vars["ISPUBLIC"] = "public ";
478
0
    if (method->NoStreaming()) {
479
0
      GenerateUnaryClientInterface(printer, &vars);
480
0
      continue;
481
0
    }
482
0
    if (method->BidiStreaming()) {
483
0
      GenerateDepluxStreamInterface(printer, &vars);
484
0
      continue;
485
0
    }
486
487
0
    if (method->ClientStreaming()) {
488
0
      GenerateClientWriteStreamInterface(printer, &vars);
489
0
      continue;
490
0
    }
491
492
0
    if (method->ServerStreaming()) {
493
0
      GenerateClientReadableStreamInterface(printer, &vars);
494
0
      continue;
495
0
    }
496
0
  }
497
0
  printer->Outdent();
498
0
  printer->Print("}\n");
499
0
}
500
}  // namespace
501
502
grpc::string GenerateInterface(grpc_generator::File* file,
503
                               const grpc_generator::Service* service,
504
0
                               const grpc::string& filename) {
505
0
  grpc::string output;
506
507
0
  std::set<grpc::string> generated_functions;
508
0
  std::map<grpc::string, grpc::string> vars;
509
510
0
  vars["PATH"] = file->package();
511
512
0
  if (!file->package().empty()) {
513
0
    vars["PATH"].append(".");
514
0
  }
515
516
0
  vars["ServiceName"] = service->name();
517
0
  vars["FBSFile"] = service->name() + "_fbs";
518
0
  vars["Filename"] = filename;
519
0
  auto printer = file->CreatePrinter(&output);
520
521
0
  GenerateImports(service, &*printer, &vars, false);
522
0
  GenerateMainInterface(service, &*printer, &vars);
523
0
  printer->Print("\n");
524
0
  GenerateClientInterface(service, &*printer, &vars);
525
0
  printer->Print("\n");
526
0
  GenerateClientClassInterface(service, &*printer, &vars);
527
0
  return output;
528
0
}
529
}  // namespace grpc_ts_generator