blob: 2be24e71a271b6c72c81aa51721980c82e282e87 [file] [log] [blame]
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001/*
2 * Copyright 2014 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// independent from idl_parser, since this code is not needed for most clients
18
19#include "flatbuffers/code_generators.h"
20#include "flatbuffers/flatbuffers.h"
21#include "flatbuffers/idl.h"
22#include "flatbuffers/util.h"
Austin Schuhe89fa2d2019-08-14 20:24:23 -070023#include "src/compiler/cpp_generator.h"
24#include "src/compiler/go_generator.h"
25#include "src/compiler/java_generator.h"
Austin Schuh272c6132020-11-14 16:37:52 -080026#include "src/compiler/python_generator.h"
Austin Schuh272c6132020-11-14 16:37:52 -080027#include "src/compiler/swift_generator.h"
28#include "src/compiler/ts_generator.h"
Austin Schuhe89fa2d2019-08-14 20:24:23 -070029
30#if defined(_MSC_VER)
31# pragma warning(push)
32# pragma warning(disable : 4512) // C4512: 'class' : assignment operator could
33// not be generated
34#endif
35
36namespace flatbuffers {
37
38class FlatBufMethod : public grpc_generator::Method {
39 public:
Austin Schuh272c6132020-11-14 16:37:52 -080040 enum Streaming { kNone, kClient, kServer, kBiDi };
Austin Schuhe89fa2d2019-08-14 20:24:23 -070041
42 FlatBufMethod(const RPCCall *method) : method_(method) {
43 streaming_ = kNone;
44 auto val = method_->attributes.Lookup("streaming");
45 if (val) {
46 if (val->constant == "client") streaming_ = kClient;
47 if (val->constant == "server") streaming_ = kServer;
48 if (val->constant == "bidi") streaming_ = kBiDi;
49 }
50 }
51
52 grpc::string GetLeadingComments(const grpc::string) const { return ""; }
53
54 grpc::string GetTrailingComments(const grpc::string) const { return ""; }
55
56 std::vector<grpc::string> GetAllComments() const {
57 return method_->doc_comment;
58 }
59
60 std::string name() const { return method_->name; }
61
Austin Schuh272c6132020-11-14 16:37:52 -080062 // TODO: This method need to incorporate namespace for C++ side. Other
63 // language bindings simply don't use this method.
Austin Schuhe89fa2d2019-08-14 20:24:23 -070064 std::string GRPCType(const StructDef &sd) const {
65 return "flatbuffers::grpc::Message<" + sd.name + ">";
66 }
67
Austin Schuh272c6132020-11-14 16:37:52 -080068 std::vector<std::string> get_input_namespace_parts() const {
69 return (*method_->request).defined_namespace->components;
70 }
71
Austin Schuhe89fa2d2019-08-14 20:24:23 -070072 std::string get_input_type_name() const { return (*method_->request).name; }
73
Austin Schuh272c6132020-11-14 16:37:52 -080074 std::vector<std::string> get_output_namespace_parts() const {
75 return (*method_->response).defined_namespace->components;
76 }
77
Austin Schuhe89fa2d2019-08-14 20:24:23 -070078 std::string get_output_type_name() const { return (*method_->response).name; }
79
80 bool get_module_and_message_path_input(grpc::string * /*str*/,
81 grpc::string /*generator_file_name*/,
82 bool /*generate_in_pb2_grpc*/,
83 grpc::string /*import_prefix*/) const {
84 return true;
85 }
86
87 bool get_module_and_message_path_output(
88 grpc::string * /*str*/, grpc::string /*generator_file_name*/,
89 bool /*generate_in_pb2_grpc*/, grpc::string /*import_prefix*/) const {
90 return true;
91 }
92
Austin Schuh272c6132020-11-14 16:37:52 -080093 std::string get_fb_builder() const { return "builder"; }
94
Austin Schuhe89fa2d2019-08-14 20:24:23 -070095 std::string input_type_name() const { return GRPCType(*method_->request); }
96
97 std::string output_type_name() const { return GRPCType(*method_->response); }
98
99 bool NoStreaming() const { return streaming_ == kNone; }
100
101 bool ClientStreaming() const { return streaming_ == kClient; }
102
103 bool ServerStreaming() const { return streaming_ == kServer; }
104
105 bool BidiStreaming() const { return streaming_ == kBiDi; }
106
107 private:
108 const RPCCall *method_;
109 Streaming streaming_;
110};
111
112class FlatBufService : public grpc_generator::Service {
113 public:
114 FlatBufService(const ServiceDef *service) : service_(service) {}
115
116 grpc::string GetLeadingComments(const grpc::string) const { return ""; }
117
118 grpc::string GetTrailingComments(const grpc::string) const { return ""; }
119
120 std::vector<grpc::string> GetAllComments() const {
121 return service_->doc_comment;
122 }
123
Austin Schuh272c6132020-11-14 16:37:52 -0800124 std::vector<grpc::string> namespace_parts() const {
125 return service_->defined_namespace->components;
126 }
127
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700128 std::string name() const { return service_->name; }
Austin Schuh272c6132020-11-14 16:37:52 -0800129 bool is_internal() const {
130 return service_->Definition::attributes.Lookup("private") ? true : false;
131 }
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700132
133 int method_count() const {
134 return static_cast<int>(service_->calls.vec.size());
135 }
136
137 std::unique_ptr<const grpc_generator::Method> method(int i) const {
138 return std::unique_ptr<const grpc_generator::Method>(
139 new FlatBufMethod(service_->calls.vec[i]));
140 }
141
142 private:
143 const ServiceDef *service_;
144};
145
146class FlatBufPrinter : public grpc_generator::Printer {
147 public:
James Kuszmaul8e62b022022-03-22 09:33:25 -0700148 FlatBufPrinter(std::string *str, const char indentation_type)
149 : str_(str),
150 escape_char_('$'),
151 indent_(0),
152 indentation_size_(2),
153 indentation_type_(indentation_type) {}
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700154
155 void Print(const std::map<std::string, std::string> &vars,
156 const char *string_template) {
157 std::string s = string_template;
158 // Replace any occurrences of strings in "vars" that are surrounded
159 // by the escape character by what they're mapped to.
160 size_t pos;
161 while ((pos = s.find(escape_char_)) != std::string::npos) {
162 // Found an escape char, must also find the closing one.
163 size_t pos2 = s.find(escape_char_, pos + 1);
164 // If placeholder not closed, ignore.
165 if (pos2 == std::string::npos) break;
166 auto it = vars.find(s.substr(pos + 1, pos2 - pos - 1));
167 // If unknown placeholder, ignore.
168 if (it == vars.end()) break;
169 // Subtitute placeholder.
170 s.replace(pos, pos2 - pos + 1, it->second);
171 }
172 Print(s.c_str());
173 }
174
175 void Print(const char *s) {
176 if (s == nullptr || *s == '\0') { return; }
177 // Add this string, but for each part separated by \n, add indentation.
178 for (;;) {
179 // Current indentation.
James Kuszmaul8e62b022022-03-22 09:33:25 -0700180 str_->insert(str_->end(), indent_ * indentation_size_, indentation_type_);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700181 // See if this contains more than one line.
182 const char *lf = strchr(s, '\n');
183 if (lf) {
184 (*str_) += std::string(s, lf + 1);
185 s = lf + 1;
186 if (!*s) break; // Only continue if there's more lines.
187 } else {
188 (*str_) += s;
189 break;
190 }
191 }
192 }
193
James Kuszmaul8e62b022022-03-22 09:33:25 -0700194 void SetIndentationSize(const size_t size) {
195 FLATBUFFERS_ASSERT(str_->empty());
196 indentation_size_ = size;
197 }
198
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700199 void Indent() { indent_++; }
200
201 void Outdent() {
James Kuszmaul8e62b022022-03-22 09:33:25 -0700202 FLATBUFFERS_ASSERT(indent_ > 0);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700203 indent_--;
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700204 }
205
206 private:
207 std::string *str_;
208 char escape_char_;
James Kuszmaul8e62b022022-03-22 09:33:25 -0700209 size_t indent_;
210 size_t indentation_size_;
211 char indentation_type_;
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700212};
213
214class FlatBufFile : public grpc_generator::File {
215 public:
216 enum Language {
Austin Schuh272c6132020-11-14 16:37:52 -0800217 kLanguageGo,
218 kLanguageCpp,
219 kLanguageJava,
220 kLanguagePython,
221 kLanguageSwift,
222 kLanguageTS
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700223 };
224
225 FlatBufFile(const Parser &parser, const std::string &file_name,
226 Language language)
227 : parser_(parser), file_name_(file_name), language_(language) {}
228
229 FlatBufFile &operator=(const FlatBufFile &);
230
231 grpc::string GetLeadingComments(const grpc::string) const { return ""; }
232
233 grpc::string GetTrailingComments(const grpc::string) const { return ""; }
234
235 std::vector<grpc::string> GetAllComments() const {
236 return std::vector<grpc::string>();
237 }
238
239 std::string filename() const { return file_name_; }
240
241 std::string filename_without_ext() const {
242 return StripExtension(file_name_);
243 }
244
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700245 std::string package() const {
246 return parser_.current_namespace_->GetFullyQualifiedName("");
247 }
248
249 std::vector<std::string> package_parts() const {
250 return parser_.current_namespace_->components;
251 }
252
253 std::string additional_headers() const {
254 switch (language_) {
255 case kLanguageCpp: {
256 return "#include \"flatbuffers/grpc.h\"\n";
257 }
258 case kLanguageGo: {
259 return "import \"github.com/google/flatbuffers/go\"";
260 }
261 case kLanguageJava: {
262 return "import com.google.flatbuffers.grpc.FlatbuffersUtils;";
263 }
Austin Schuh272c6132020-11-14 16:37:52 -0800264 case kLanguagePython: {
265 return "";
266 }
267 case kLanguageSwift: {
268 return "";
269 }
270 case kLanguageTS: {
271 return "";
272 }
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700273 }
274 return "";
275 }
276
277 int service_count() const {
278 return static_cast<int>(parser_.services_.vec.size());
279 }
280
281 std::unique_ptr<const grpc_generator::Service> service(int i) const {
282 return std::unique_ptr<const grpc_generator::Service>(
283 new FlatBufService(parser_.services_.vec[i]));
284 }
285
286 std::unique_ptr<grpc_generator::Printer> CreatePrinter(
James Kuszmaul8e62b022022-03-22 09:33:25 -0700287 std::string *str, const char indentation_type = ' ') const {
288 return std::unique_ptr<grpc_generator::Printer>(
289 new FlatBufPrinter(str, indentation_type));
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700290 }
291
292 private:
293 const Parser &parser_;
294 const std::string &file_name_;
295 const Language language_;
296};
297
298class GoGRPCGenerator : public flatbuffers::BaseGenerator {
299 public:
300 GoGRPCGenerator(const Parser &parser, const std::string &path,
301 const std::string &file_name)
Austin Schuh272c6132020-11-14 16:37:52 -0800302 : BaseGenerator(parser, path, file_name, "", "" /*Unused*/, "go"),
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700303 parser_(parser),
304 path_(path),
305 file_name_(file_name) {}
306
307 bool generate() {
308 FlatBufFile file(parser_, file_name_, FlatBufFile::kLanguageGo);
309 grpc_go_generator::Parameters p;
310 p.custom_method_io_type = "flatbuffers.Builder";
311 for (int i = 0; i < file.service_count(); i++) {
312 auto service = file.service(i);
313 const Definition *def = parser_.services_.vec[i];
314 p.package_name = LastNamespacePart(*(def->defined_namespace));
Austin Schuh272c6132020-11-14 16:37:52 -0800315 p.service_prefix =
316 def->defined_namespace->GetFullyQualifiedName(""); // file.package();
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700317 std::string output =
318 grpc_go_generator::GenerateServiceSource(&file, service.get(), &p);
319 std::string filename =
320 NamespaceDir(*def->defined_namespace) + def->name + "_grpc.go";
321 if (!flatbuffers::SaveFile(filename.c_str(), output, false)) return false;
322 }
323 return true;
324 }
325
326 protected:
327 const Parser &parser_;
328 const std::string &path_, &file_name_;
329};
330
331bool GenerateGoGRPC(const Parser &parser, const std::string &path,
332 const std::string &file_name) {
333 int nservices = 0;
334 for (auto it = parser.services_.vec.begin(); it != parser.services_.vec.end();
335 ++it) {
336 if (!(*it)->generated) nservices++;
337 }
338 if (!nservices) return true;
339 return GoGRPCGenerator(parser, path, file_name).generate();
340}
341
342bool GenerateCppGRPC(const Parser &parser, const std::string &path,
343 const std::string &file_name) {
Austin Schuh2dd86a92022-09-14 21:19:23 -0700344 const auto &opts = parser.opts;
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700345 int nservices = 0;
346 for (auto it = parser.services_.vec.begin(); it != parser.services_.vec.end();
347 ++it) {
348 if (!(*it)->generated) nservices++;
349 }
350 if (!nservices) return true;
351
Austin Schuh2dd86a92022-09-14 21:19:23 -0700352 std::string suffix = "";
353 suffix += opts.filename_suffix.empty() ? "_generated" : opts.filename_suffix;
354 suffix += ".";
355 suffix += opts.filename_extension.empty() ? "h" : opts.filename_extension;
356
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700357 grpc_cpp_generator::Parameters generator_parameters;
358 // TODO(wvo): make the other parameters in this struct configurable.
359 generator_parameters.use_system_headers = true;
Austin Schuh2dd86a92022-09-14 21:19:23 -0700360 generator_parameters.message_header_extension = suffix;
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700361
362 FlatBufFile fbfile(parser, file_name, FlatBufFile::kLanguageCpp);
363
364 std::string header_code =
365 grpc_cpp_generator::GetHeaderPrologue(&fbfile, generator_parameters) +
Austin Schuh272c6132020-11-14 16:37:52 -0800366 grpc_cpp_generator::GetHeaderIncludes(&fbfile, generator_parameters) +
367 grpc_cpp_generator::GetHeaderServices(&fbfile, generator_parameters) +
368 grpc_cpp_generator::GetHeaderEpilogue(&fbfile, generator_parameters);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700369
370 std::string source_code =
371 grpc_cpp_generator::GetSourcePrologue(&fbfile, generator_parameters) +
Austin Schuh272c6132020-11-14 16:37:52 -0800372 grpc_cpp_generator::GetSourceIncludes(&fbfile, generator_parameters) +
373 grpc_cpp_generator::GetSourceServices(&fbfile, generator_parameters) +
374 grpc_cpp_generator::GetSourceEpilogue(&fbfile, generator_parameters);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700375
376 return flatbuffers::SaveFile((path + file_name + ".grpc.fb.h").c_str(),
377 header_code, false) &&
Austin Schuh272c6132020-11-14 16:37:52 -0800378 flatbuffers::SaveFile((path + file_name + ".grpc.fb.cc").c_str(),
379 source_code, false);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700380}
381
382class JavaGRPCGenerator : public flatbuffers::BaseGenerator {
383 public:
384 JavaGRPCGenerator(const Parser &parser, const std::string &path,
385 const std::string &file_name)
Austin Schuh272c6132020-11-14 16:37:52 -0800386 : BaseGenerator(parser, path, file_name, "", "." /*separator*/, "java") {}
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700387
388 bool generate() {
389 FlatBufFile file(parser_, file_name_, FlatBufFile::kLanguageJava);
390 grpc_java_generator::Parameters p;
391 for (int i = 0; i < file.service_count(); i++) {
392 auto service = file.service(i);
393 const Definition *def = parser_.services_.vec[i];
394 p.package_name =
395 def->defined_namespace->GetFullyQualifiedName(""); // file.package();
396 std::string output =
397 grpc_java_generator::GenerateServiceSource(&file, service.get(), &p);
398 std::string filename =
399 NamespaceDir(*def->defined_namespace) + def->name + "Grpc.java";
400 if (!flatbuffers::SaveFile(filename.c_str(), output, false)) return false;
401 }
402 return true;
403 }
404};
405
406bool GenerateJavaGRPC(const Parser &parser, const std::string &path,
407 const std::string &file_name) {
408 int nservices = 0;
409 for (auto it = parser.services_.vec.begin(); it != parser.services_.vec.end();
410 ++it) {
411 if (!(*it)->generated) nservices++;
412 }
413 if (!nservices) return true;
414 return JavaGRPCGenerator(parser, path, file_name).generate();
415}
416
James Kuszmaul8e62b022022-03-22 09:33:25 -0700417class PythonGRPCGenerator : public flatbuffers::BaseGenerator {
418 private:
419 CodeWriter code_;
420
421 public:
422 PythonGRPCGenerator(const Parser &parser, const std::string &filename)
423 : BaseGenerator(parser, "", filename, "", "" /*Unused*/, "swift") {}
424
425 bool generate() {
426 code_.Clear();
427 code_ +=
428 "# Generated by the gRPC Python protocol compiler plugin. "
429 "DO NOT EDIT!\n";
430 code_ += "import grpc\n";
431
432 FlatBufFile file(parser_, file_name_, FlatBufFile::kLanguagePython);
433
434 for (int i = 0; i < file.service_count(); i++) {
435 auto service = file.service(i);
436 code_ += grpc_python_generator::Generate(&file, service.get());
437 }
438 const auto final_code = code_.ToString();
439 const auto filename = GenerateFileName();
440 return SaveFile(filename.c_str(), final_code, false);
441 }
442
443 std::string GenerateFileName() {
444 std::string namespace_dir;
445 auto &namespaces = parser_.namespaces_.back()->components;
446 for (auto it = namespaces.begin(); it != namespaces.end(); ++it) {
447 if (it != namespaces.begin()) namespace_dir += kPathSeparator;
448 namespace_dir += *it;
449 }
450 std::string grpc_py_filename = namespace_dir;
451 if (!namespace_dir.empty()) grpc_py_filename += kPathSeparator;
452 return grpc_py_filename + file_name_ + "_grpc_fb.py";
453 }
454};
455
Austin Schuh272c6132020-11-14 16:37:52 -0800456bool GeneratePythonGRPC(const Parser &parser, const std::string & /*path*/,
457 const std::string &file_name) {
458 int nservices = 0;
459 for (auto it = parser.services_.vec.begin(); it != parser.services_.vec.end();
460 ++it) {
461 if (!(*it)->generated) nservices++;
462 }
463 if (!nservices) return true;
464
James Kuszmaul8e62b022022-03-22 09:33:25 -0700465 return PythonGRPCGenerator(parser, file_name).generate();
Austin Schuh272c6132020-11-14 16:37:52 -0800466}
467
468class SwiftGRPCGenerator : public flatbuffers::BaseGenerator {
469 private:
470 CodeWriter code_;
471
472 public:
473 SwiftGRPCGenerator(const Parser &parser, const std::string &path,
474 const std::string &filename)
475 : BaseGenerator(parser, path, filename, "", "" /*Unused*/, "swift") {}
476
477 bool generate() {
478 code_.Clear();
479 code_ += "// Generated GRPC code for FlatBuffers swift!";
480 code_ += grpc_swift_generator::GenerateHeader();
481 FlatBufFile file(parser_, file_name_, FlatBufFile::kLanguageSwift);
482 for (int i = 0; i < file.service_count(); i++) {
483 auto service = file.service(i);
484 code_ += grpc_swift_generator::Generate(&file, service.get());
485 }
486 const auto final_code = code_.ToString();
487 const auto filename = GeneratedFileName(path_, file_name_);
488 return SaveFile(filename.c_str(), final_code, false);
489 }
490
491 static std::string GeneratedFileName(const std::string &path,
492 const std::string &file_name) {
493 return path + file_name + ".grpc.swift";
494 }
495};
496
497bool GenerateSwiftGRPC(const Parser &parser, const std::string &path,
498 const std::string &file_name) {
499 int nservices = 0;
500 for (auto it = parser.services_.vec.begin(); it != parser.services_.vec.end();
501 ++it) {
502 if (!(*it)->generated) nservices++;
503 }
504 if (!nservices) return true;
505 return SwiftGRPCGenerator(parser, path, file_name).generate();
506}
507
508class TSGRPCGenerator : public flatbuffers::BaseGenerator {
509 private:
510 CodeWriter code_;
511
512 public:
513 TSGRPCGenerator(const Parser &parser, const std::string &path,
514 const std::string &filename)
515 : BaseGenerator(parser, path, filename, "", "" /*Unused*/, "ts") {}
516
517 bool generate() {
518 code_.Clear();
519 FlatBufFile file(parser_, file_name_, FlatBufFile::kLanguageTS);
520
521 for (int i = 0; i < file.service_count(); i++) {
522 auto service = file.service(i);
523 code_ += grpc_ts_generator::Generate(&file, service.get(), file_name_);
524 const auto ts_name = GeneratedFileName(path_, file_name_);
525 if (!SaveFile(ts_name.c_str(), code_.ToString(), false)) return false;
526
527 code_.Clear();
528 code_ += grpc_ts_generator::GenerateInterface(&file, service.get(),
529 file_name_);
530 const auto ts_interface_name = GeneratedFileName(path_, file_name_, true);
531 if (!SaveFile(ts_interface_name.c_str(), code_.ToString(), false))
532 return false;
533 }
534 return true;
535 }
536
537 static std::string GeneratedFileName(const std::string &path,
538 const std::string &file_name,
539 const bool is_interface = false) {
540 if (is_interface) return path + file_name + "_grpc.d.ts";
541 return path + file_name + "_grpc.js";
542 }
543};
544
545bool GenerateTSGRPC(const Parser &parser, const std::string &path,
546 const std::string &file_name) {
547 int nservices = 0;
548 for (auto it = parser.services_.vec.begin(); it != parser.services_.vec.end();
549 ++it) {
550 if (!(*it)->generated) nservices++;
551 }
552 if (!nservices) return true;
553 return TSGRPCGenerator(parser, path, file_name).generate();
554}
555
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700556} // namespace flatbuffers
557
558#if defined(_MSC_VER)
559# pragma warning(pop)
560#endif