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 | |
36 | namespace flatbuffers { |
37 | |
38 | class 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 (const grpc::string) const { return "" ; } |
53 | |
54 | grpc::string (const grpc::string) const { return "" ; } |
55 | |
56 | std::vector<grpc::string> () 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 | |
112 | class FlatBufService : public grpc_generator::Service { |
113 | public: |
114 | FlatBufService(const ServiceDef *service) : service_(service) {} |
115 | |
116 | grpc::string (const grpc::string) const { return "" ; } |
117 | |
118 | grpc::string (const grpc::string) const { return "" ; } |
119 | |
120 | std::vector<grpc::string> () 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 | |
146 | class 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 | |
214 | class 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 (const grpc::string) const { return "" ; } |
232 | |
233 | grpc::string (const grpc::string) const { return "" ; } |
234 | |
235 | std::vector<grpc::string> () 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 () const { |
246 | return parser_.opts.filename_suffix + ".h" ; |
247 | } |
248 | |
249 | std::string () 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 () 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 | |
304 | class 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 | |
337 | bool 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 | |
348 | bool 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 = |
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 | |
381 | class 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 | |
405 | bool 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 | |
416 | class 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 | |
455 | bool 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 | |
467 | class 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 | |
496 | bool 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 | |
507 | class 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 | |
544 | bool 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 | |