1/*
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"
23#include "src/compiler/cpp_generator.h"
24#include "src/compiler/go_generator.h"
25#include "src/compiler/java_generator.h"
26#include "src/compiler/python_generator.h"
27#include "src/compiler/swift_generator.h"
28#include "src/compiler/ts_generator.h"
29
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:
40 enum Streaming { kNone, kClient, kServer, kBiDi };
41
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
62 // TODO: This method need to incorporate namespace for C++ side. Other
63 // language bindings simply don't use this method.
64 std::string GRPCType(const StructDef &sd) const {
65 return "flatbuffers::grpc::Message<" + sd.name + ">";
66 }
67
68 std::vector<std::string> get_input_namespace_parts() const {
69 return (*method_->request).defined_namespace->components;
70 }
71
72 std::string get_input_type_name() const { return (*method_->request).name; }
73
74 std::vector<std::string> get_output_namespace_parts() const {
75 return (*method_->response).defined_namespace->components;
76 }
77
78 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
93 std::string get_fb_builder() const { return "builder"; }
94
95 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
124 std::vector<grpc::string> namespace_parts() const {
125 return service_->defined_namespace->components;
126 }
127
128 std::string name() const { return service_->name; }
129 bool is_internal() const {
130 return service_->Definition::attributes.Lookup("private") ? true : false;
131 }
132
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:
148 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) {}
154
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.
180 str_->insert(str_->end(), indent_ * indentation_size_, indentation_type_);
181 // 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
194 void SetIndentationSize(const size_t size) {
195 FLATBUFFERS_ASSERT(str_->empty());
196 indentation_size_ = size;
197 }
198
199 void Indent() { indent_++; }
200
201 void Outdent() {
202 FLATBUFFERS_ASSERT(indent_ > 0);
203 indent_--;
204 }
205
206 private:
207 std::string *str_;
208 char escape_char_;
209 size_t indent_;
210 size_t indentation_size_;
211 char indentation_type_;
212};
213
214class FlatBufFile : public grpc_generator::File {
215 public:
216 enum Language {
217 kLanguageGo,
218 kLanguageCpp,
219 kLanguageJava,
220 kLanguagePython,
221 kLanguageSwift,
222 kLanguageTS
223 };
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
245 std::string message_header_ext() const {
246 return parser_.opts.filename_suffix + ".h";
247 }
248
249 std::string service_header_ext() const { return ".grpc.fb.h"; }
250
251 std::string package() const {
252 return parser_.current_namespace_->GetFullyQualifiedName("");
253 }
254
255 std::vector<std::string> package_parts() const {
256 return parser_.current_namespace_->components;
257 }
258
259 std::string additional_headers() const {
260 switch (language_) {
261 case kLanguageCpp: {
262 return "#include \"flatbuffers/grpc.h\"\n";
263 }
264 case kLanguageGo: {
265 return "import \"github.com/google/flatbuffers/go\"";
266 }
267 case kLanguageJava: {
268 return "import com.google.flatbuffers.grpc.FlatbuffersUtils;";
269 }
270 case kLanguagePython: {
271 return "";
272 }
273 case kLanguageSwift: {
274 return "";
275 }
276 case kLanguageTS: {
277 return "";
278 }
279 }
280 return "";
281 }
282
283 int service_count() const {
284 return static_cast<int>(parser_.services_.vec.size());
285 }
286
287 std::unique_ptr<const grpc_generator::Service> service(int i) const {
288 return std::unique_ptr<const grpc_generator::Service>(
289 new FlatBufService(parser_.services_.vec[i]));
290 }
291
292 std::unique_ptr<grpc_generator::Printer> CreatePrinter(
293 std::string *str, const char indentation_type = ' ') const {
294 return std::unique_ptr<grpc_generator::Printer>(
295 new FlatBufPrinter(str, indentation_type));
296 }
297
298 private:
299 const Parser &parser_;
300 const std::string &file_name_;
301 const Language language_;
302};
303
304class GoGRPCGenerator : public flatbuffers::BaseGenerator {
305 public:
306 GoGRPCGenerator(const Parser &parser, const std::string &path,
307 const std::string &file_name)
308 : BaseGenerator(parser, path, file_name, "", "" /*Unused*/, "go"),
309 parser_(parser),
310 path_(path),
311 file_name_(file_name) {}
312
313 bool generate() {
314 FlatBufFile file(parser_, file_name_, FlatBufFile::kLanguageGo);
315 grpc_go_generator::Parameters p;
316 p.custom_method_io_type = "flatbuffers.Builder";
317 for (int i = 0; i < file.service_count(); i++) {
318 auto service = file.service(i);
319 const Definition *def = parser_.services_.vec[i];
320 p.package_name = LastNamespacePart(*(def->defined_namespace));
321 p.service_prefix =
322 def->defined_namespace->GetFullyQualifiedName(""); // file.package();
323 std::string output =
324 grpc_go_generator::GenerateServiceSource(&file, service.get(), &p);
325 std::string filename =
326 NamespaceDir(*def->defined_namespace) + def->name + "_grpc.go";
327 if (!flatbuffers::SaveFile(filename.c_str(), output, false)) return false;
328 }
329 return true;
330 }
331
332 protected:
333 const Parser &parser_;
334 const std::string &path_, &file_name_;
335};
336
337bool GenerateGoGRPC(const Parser &parser, const std::string &path,
338 const std::string &file_name) {
339 int nservices = 0;
340 for (auto it = parser.services_.vec.begin(); it != parser.services_.vec.end();
341 ++it) {
342 if (!(*it)->generated) nservices++;
343 }
344 if (!nservices) return true;
345 return GoGRPCGenerator(parser, path, file_name).generate();
346}
347
348bool GenerateCppGRPC(const Parser &parser, const std::string &path,
349 const std::string &file_name) {
350 int nservices = 0;
351 for (auto it = parser.services_.vec.begin(); it != parser.services_.vec.end();
352 ++it) {
353 if (!(*it)->generated) nservices++;
354 }
355 if (!nservices) return true;
356
357 grpc_cpp_generator::Parameters generator_parameters;
358 // TODO(wvo): make the other parameters in this struct configurable.
359 generator_parameters.use_system_headers = true;
360
361 FlatBufFile fbfile(parser, file_name, FlatBufFile::kLanguageCpp);
362
363 std::string header_code =
364 grpc_cpp_generator::GetHeaderPrologue(&fbfile, generator_parameters) +
365 grpc_cpp_generator::GetHeaderIncludes(&fbfile, generator_parameters) +
366 grpc_cpp_generator::GetHeaderServices(&fbfile, generator_parameters) +
367 grpc_cpp_generator::GetHeaderEpilogue(&fbfile, generator_parameters);
368
369 std::string source_code =
370 grpc_cpp_generator::GetSourcePrologue(&fbfile, generator_parameters) +
371 grpc_cpp_generator::GetSourceIncludes(&fbfile, generator_parameters) +
372 grpc_cpp_generator::GetSourceServices(&fbfile, generator_parameters) +
373 grpc_cpp_generator::GetSourceEpilogue(&fbfile, generator_parameters);
374
375 return flatbuffers::SaveFile((path + file_name + ".grpc.fb.h").c_str(),
376 header_code, false) &&
377 flatbuffers::SaveFile((path + file_name + ".grpc.fb.cc").c_str(),
378 source_code, false);
379}
380
381class JavaGRPCGenerator : public flatbuffers::BaseGenerator {
382 public:
383 JavaGRPCGenerator(const Parser &parser, const std::string &path,
384 const std::string &file_name)
385 : BaseGenerator(parser, path, file_name, "", "." /*separator*/, "java") {}
386
387 bool generate() {
388 FlatBufFile file(parser_, file_name_, FlatBufFile::kLanguageJava);
389 grpc_java_generator::Parameters p;
390 for (int i = 0; i < file.service_count(); i++) {
391 auto service = file.service(i);
392 const Definition *def = parser_.services_.vec[i];
393 p.package_name =
394 def->defined_namespace->GetFullyQualifiedName(""); // file.package();
395 std::string output =
396 grpc_java_generator::GenerateServiceSource(&file, service.get(), &p);
397 std::string filename =
398 NamespaceDir(*def->defined_namespace) + def->name + "Grpc.java";
399 if (!flatbuffers::SaveFile(filename.c_str(), output, false)) return false;
400 }
401 return true;
402 }
403};
404
405bool GenerateJavaGRPC(const Parser &parser, const std::string &path,
406 const std::string &file_name) {
407 int nservices = 0;
408 for (auto it = parser.services_.vec.begin(); it != parser.services_.vec.end();
409 ++it) {
410 if (!(*it)->generated) nservices++;
411 }
412 if (!nservices) return true;
413 return JavaGRPCGenerator(parser, path, file_name).generate();
414}
415
416class PythonGRPCGenerator : public flatbuffers::BaseGenerator {
417 private:
418 CodeWriter code_;
419
420 public:
421 PythonGRPCGenerator(const Parser &parser, const std::string &filename)
422 : BaseGenerator(parser, "", filename, "", "" /*Unused*/, "swift") {}
423
424 bool generate() {
425 code_.Clear();
426 code_ +=
427 "# Generated by the gRPC Python protocol compiler plugin. "
428 "DO NOT EDIT!\n";
429 code_ += "import grpc\n";
430
431 FlatBufFile file(parser_, file_name_, FlatBufFile::kLanguagePython);
432
433 for (int i = 0; i < file.service_count(); i++) {
434 auto service = file.service(i);
435 code_ += grpc_python_generator::Generate(&file, service.get());
436 }
437 const auto final_code = code_.ToString();
438 const auto filename = GenerateFileName();
439 return SaveFile(filename.c_str(), final_code, false);
440 }
441
442 std::string GenerateFileName() {
443 std::string namespace_dir;
444 auto &namespaces = parser_.namespaces_.back()->components;
445 for (auto it = namespaces.begin(); it != namespaces.end(); ++it) {
446 if (it != namespaces.begin()) namespace_dir += kPathSeparator;
447 namespace_dir += *it;
448 }
449 std::string grpc_py_filename = namespace_dir;
450 if (!namespace_dir.empty()) grpc_py_filename += kPathSeparator;
451 return grpc_py_filename + file_name_ + "_grpc_fb.py";
452 }
453};
454
455bool GeneratePythonGRPC(const Parser &parser, const std::string & /*path*/,
456 const std::string &file_name) {
457 int nservices = 0;
458 for (auto it = parser.services_.vec.begin(); it != parser.services_.vec.end();
459 ++it) {
460 if (!(*it)->generated) nservices++;
461 }
462 if (!nservices) return true;
463
464 return PythonGRPCGenerator(parser, file_name).generate();
465}
466
467class SwiftGRPCGenerator : public flatbuffers::BaseGenerator {
468 private:
469 CodeWriter code_;
470
471 public:
472 SwiftGRPCGenerator(const Parser &parser, const std::string &path,
473 const std::string &filename)
474 : BaseGenerator(parser, path, filename, "", "" /*Unused*/, "swift") {}
475
476 bool generate() {
477 code_.Clear();
478 code_ += "// Generated GRPC code for FlatBuffers swift!";
479 code_ += grpc_swift_generator::GenerateHeader();
480 FlatBufFile file(parser_, file_name_, FlatBufFile::kLanguageSwift);
481 for (int i = 0; i < file.service_count(); i++) {
482 auto service = file.service(i);
483 code_ += grpc_swift_generator::Generate(&file, service.get());
484 }
485 const auto final_code = code_.ToString();
486 const auto filename = GeneratedFileName(path_, file_name_);
487 return SaveFile(filename.c_str(), final_code, false);
488 }
489
490 static std::string GeneratedFileName(const std::string &path,
491 const std::string &file_name) {
492 return path + file_name + ".grpc.swift";
493 }
494};
495
496bool GenerateSwiftGRPC(const Parser &parser, const std::string &path,
497 const std::string &file_name) {
498 int nservices = 0;
499 for (auto it = parser.services_.vec.begin(); it != parser.services_.vec.end();
500 ++it) {
501 if (!(*it)->generated) nservices++;
502 }
503 if (!nservices) return true;
504 return SwiftGRPCGenerator(parser, path, file_name).generate();
505}
506
507class TSGRPCGenerator : public flatbuffers::BaseGenerator {
508 private:
509 CodeWriter code_;
510
511 public:
512 TSGRPCGenerator(const Parser &parser, const std::string &path,
513 const std::string &filename)
514 : BaseGenerator(parser, path, filename, "", "" /*Unused*/, "ts") {}
515
516 bool generate() {
517 code_.Clear();
518 FlatBufFile file(parser_, file_name_, FlatBufFile::kLanguageTS);
519
520 for (int i = 0; i < file.service_count(); i++) {
521 auto service = file.service(i);
522 code_ += grpc_ts_generator::Generate(&file, service.get(), file_name_);
523 const auto ts_name = GeneratedFileName(path_, file_name_);
524 if (!SaveFile(ts_name.c_str(), code_.ToString(), false)) return false;
525
526 code_.Clear();
527 code_ += grpc_ts_generator::GenerateInterface(&file, service.get(),
528 file_name_);
529 const auto ts_interface_name = GeneratedFileName(path_, file_name_, true);
530 if (!SaveFile(ts_interface_name.c_str(), code_.ToString(), false))
531 return false;
532 }
533 return true;
534 }
535
536 static std::string GeneratedFileName(const std::string &path,
537 const std::string &file_name,
538 const bool is_interface = false) {
539 if (is_interface) return path + file_name + "_grpc.d.ts";
540 return path + file_name + "_grpc.js";
541 }
542};
543
544bool GenerateTSGRPC(const Parser &parser, const std::string &path,
545 const std::string &file_name) {
546 int nservices = 0;
547 for (auto it = parser.services_.vec.begin(); it != parser.services_.vec.end();
548 ++it) {
549 if (!(*it)->generated) nservices++;
550 }
551 if (!nservices) return true;
552 return TSGRPCGenerator(parser, path, file_name).generate();
553}
554
555} // namespace flatbuffers
556
557#if defined(_MSC_VER)
558# pragma warning(pop)
559#endif
560