/* * Copyright 2014 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. */ // independent from idl_parser, since this code is not needed for most clients #include "flatbuffers/code_generators.h" #include "flatbuffers/flatbuffers.h" #include "flatbuffers/idl.h" #include "flatbuffers/util.h" #include "src/compiler/cpp_generator.h" #include "src/compiler/go_generator.h" #include "src/compiler/java_generator.h" #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) # pragma warning(disable : 4512) // C4512: 'class' : assignment operator could // not be generated #endif namespace flatbuffers { class FlatBufMethod : public grpc_generator::Method { public: enum Streaming { kNone, kClient, kServer, kBiDi }; FlatBufMethod(const RPCCall *method) : method_(method) { streaming_ = kNone; auto val = method_->attributes.Lookup("streaming"); if (val) { if (val->constant == "client") streaming_ = kClient; if (val->constant == "server") streaming_ = kServer; if (val->constant == "bidi") streaming_ = kBiDi; } } grpc::string GetLeadingComments(const grpc::string) const { return ""; } grpc::string GetTrailingComments(const grpc::string) const { return ""; } std::vector GetAllComments() const { return method_->doc_comment; } std::string name() const { return method_->name; } // TODO: This method need to incorporate namespace for C++ side. Other // language bindings simply don't use this method. std::string GRPCType(const StructDef &sd) const { return "flatbuffers::grpc::Message<" + sd.name + ">"; } std::vector get_input_namespace_parts() const { return (*method_->request).defined_namespace->components; } std::string get_input_type_name() const { return (*method_->request).name; } std::vector get_output_namespace_parts() const { return (*method_->response).defined_namespace->components; } std::string get_output_type_name() const { return (*method_->response).name; } bool get_module_and_message_path_input(grpc::string * /*str*/, grpc::string /*generator_file_name*/, bool /*generate_in_pb2_grpc*/, grpc::string /*import_prefix*/) const { return true; } bool get_module_and_message_path_output( grpc::string * /*str*/, grpc::string /*generator_file_name*/, bool /*generate_in_pb2_grpc*/, grpc::string /*import_prefix*/) const { return true; } std::string get_fb_builder() const { return "builder"; } std::string input_type_name() const { return GRPCType(*method_->request); } std::string output_type_name() const { return GRPCType(*method_->response); } bool NoStreaming() const { return streaming_ == kNone; } bool ClientStreaming() const { return streaming_ == kClient; } bool ServerStreaming() const { return streaming_ == kServer; } bool BidiStreaming() const { return streaming_ == kBiDi; } private: const RPCCall *method_; Streaming streaming_; }; class FlatBufService : public grpc_generator::Service { public: FlatBufService(const ServiceDef *service) : service_(service) {} grpc::string GetLeadingComments(const grpc::string) const { return ""; } grpc::string GetTrailingComments(const grpc::string) const { return ""; } std::vector GetAllComments() const { return service_->doc_comment; } std::vector namespace_parts() const { return service_->defined_namespace->components; } std::string name() const { return service_->name; } bool is_internal() const { return service_->Definition::attributes.Lookup("private") ? true : false; } int method_count() const { return static_cast(service_->calls.vec.size()); } std::unique_ptr method(int i) const { return std::unique_ptr( new FlatBufMethod(service_->calls.vec[i])); } private: const ServiceDef *service_; }; class FlatBufPrinter : public grpc_generator::Printer { public: FlatBufPrinter(std::string *str) : str_(str), escape_char_('$'), indent_(0) {} void Print(const std::map &vars, const char *string_template) { std::string s = string_template; // Replace any occurrences of strings in "vars" that are surrounded // by the escape character by what they're mapped to. size_t pos; while ((pos = s.find(escape_char_)) != std::string::npos) { // Found an escape char, must also find the closing one. size_t pos2 = s.find(escape_char_, pos + 1); // If placeholder not closed, ignore. if (pos2 == std::string::npos) break; auto it = vars.find(s.substr(pos + 1, pos2 - pos - 1)); // If unknown placeholder, ignore. if (it == vars.end()) break; // Subtitute placeholder. s.replace(pos, pos2 - pos + 1, it->second); } Print(s.c_str()); } void Print(const char *s) { if (s == nullptr || *s == '\0') { return; } // Add this string, but for each part separated by \n, add indentation. for (;;) { // Current indentation. str_->insert(str_->end(), indent_ * 2, ' '); // See if this contains more than one line. const char *lf = strchr(s, '\n'); if (lf) { (*str_) += std::string(s, lf + 1); s = lf + 1; if (!*s) break; // Only continue if there's more lines. } else { (*str_) += s; break; } } } void Indent() { indent_++; } void Outdent() { indent_--; FLATBUFFERS_ASSERT(indent_ >= 0); } private: std::string *str_; char escape_char_; int indent_; }; class FlatBufFile : public grpc_generator::File { public: enum Language { kLanguageGo, kLanguageCpp, kLanguageJava, kLanguagePython, kLanguageSwift, kLanguageTS }; FlatBufFile(const Parser &parser, const std::string &file_name, Language language) : parser_(parser), file_name_(file_name), language_(language) {} FlatBufFile &operator=(const FlatBufFile &); grpc::string GetLeadingComments(const grpc::string) const { return ""; } grpc::string GetTrailingComments(const grpc::string) const { return ""; } std::vector GetAllComments() const { return std::vector(); } std::string filename() const { return file_name_; } std::string filename_without_ext() const { return StripExtension(file_name_); } std::string message_header_ext() const { return "_generated.h"; } std::string service_header_ext() const { return ".grpc.fb.h"; } std::string package() const { return parser_.current_namespace_->GetFullyQualifiedName(""); } std::vector package_parts() const { return parser_.current_namespace_->components; } std::string additional_headers() const { switch (language_) { case kLanguageCpp: { return "#include \"flatbuffers/grpc.h\"\n"; } case kLanguageGo: { return "import \"github.com/google/flatbuffers/go\""; } case kLanguageJava: { return "import com.google.flatbuffers.grpc.FlatbuffersUtils;"; } case kLanguagePython: { return ""; } case kLanguageSwift: { return ""; } case kLanguageTS: { return ""; } } return ""; } int service_count() const { return static_cast(parser_.services_.vec.size()); } std::unique_ptr service(int i) const { return std::unique_ptr( new FlatBufService(parser_.services_.vec[i])); } std::unique_ptr CreatePrinter( std::string *str) const { return std::unique_ptr(new FlatBufPrinter(str)); } private: const Parser &parser_; const std::string &file_name_; const Language language_; }; class GoGRPCGenerator : public flatbuffers::BaseGenerator { public: GoGRPCGenerator(const Parser &parser, const std::string &path, const std::string &file_name) : BaseGenerator(parser, path, file_name, "", "" /*Unused*/, "go"), parser_(parser), path_(path), file_name_(file_name) {} bool generate() { FlatBufFile file(parser_, file_name_, FlatBufFile::kLanguageGo); grpc_go_generator::Parameters p; p.custom_method_io_type = "flatbuffers.Builder"; for (int i = 0; i < file.service_count(); i++) { auto service = file.service(i); const Definition *def = parser_.services_.vec[i]; p.package_name = LastNamespacePart(*(def->defined_namespace)); p.service_prefix = def->defined_namespace->GetFullyQualifiedName(""); // file.package(); std::string output = grpc_go_generator::GenerateServiceSource(&file, service.get(), &p); std::string filename = NamespaceDir(*def->defined_namespace) + def->name + "_grpc.go"; if (!flatbuffers::SaveFile(filename.c_str(), output, false)) return false; } return true; } protected: const Parser &parser_; const std::string &path_, &file_name_; }; bool GenerateGoGRPC(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 GoGRPCGenerator(parser, path, file_name).generate(); } bool GenerateCppGRPC(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; grpc_cpp_generator::Parameters generator_parameters; // TODO(wvo): make the other parameters in this struct configurable. generator_parameters.use_system_headers = true; FlatBufFile fbfile(parser, file_name, FlatBufFile::kLanguageCpp); std::string header_code = grpc_cpp_generator::GetHeaderPrologue(&fbfile, generator_parameters) + grpc_cpp_generator::GetHeaderIncludes(&fbfile, generator_parameters) + grpc_cpp_generator::GetHeaderServices(&fbfile, generator_parameters) + grpc_cpp_generator::GetHeaderEpilogue(&fbfile, generator_parameters); std::string source_code = grpc_cpp_generator::GetSourcePrologue(&fbfile, generator_parameters) + grpc_cpp_generator::GetSourceIncludes(&fbfile, generator_parameters) + grpc_cpp_generator::GetSourceServices(&fbfile, generator_parameters) + grpc_cpp_generator::GetSourceEpilogue(&fbfile, generator_parameters); return flatbuffers::SaveFile((path + file_name + ".grpc.fb.h").c_str(), header_code, false) && flatbuffers::SaveFile((path + file_name + ".grpc.fb.cc").c_str(), source_code, false); } class JavaGRPCGenerator : public flatbuffers::BaseGenerator { public: JavaGRPCGenerator(const Parser &parser, const std::string &path, const std::string &file_name) : BaseGenerator(parser, path, file_name, "", "." /*separator*/, "java") {} bool generate() { FlatBufFile file(parser_, file_name_, FlatBufFile::kLanguageJava); grpc_java_generator::Parameters p; for (int i = 0; i < file.service_count(); i++) { auto service = file.service(i); const Definition *def = parser_.services_.vec[i]; p.package_name = def->defined_namespace->GetFullyQualifiedName(""); // file.package(); std::string output = grpc_java_generator::GenerateServiceSource(&file, service.get(), &p); std::string filename = NamespaceDir(*def->defined_namespace) + def->name + "Grpc.java"; if (!flatbuffers::SaveFile(filename.c_str(), output, false)) return false; } return true; } }; bool GenerateJavaGRPC(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 JavaGRPCGenerator(parser, path, file_name).generate(); } bool GeneratePythonGRPC(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; grpc_python_generator::GeneratorConfiguration config; config.grpc_package_root = "grpc"; config.beta_package_root = "grpc.beta"; config.import_prefix = ""; FlatBufFile fbfile(parser, file_name, FlatBufFile::kLanguagePython); grpc_python_generator::PrivateGenerator generator(config, &fbfile); std::string code = generator.GetGrpcServices(); std::string namespace_dir; auto &namespaces = parser.namespaces_.back()->components; for (auto it = namespaces.begin(); it != namespaces.end(); ++it) { if (it != namespaces.begin()) namespace_dir += kPathSeparator; namespace_dir += *it; } std::string grpc_py_filename = namespace_dir; if (!namespace_dir.empty()) grpc_py_filename += kPathSeparator; grpc_py_filename += file_name + "_grpc_fb.py"; return flatbuffers::SaveFile(grpc_py_filename.c_str(), code, false); } class SwiftGRPCGenerator : public flatbuffers::BaseGenerator { private: CodeWriter code_; public: SwiftGRPCGenerator(const Parser &parser, const std::string &path, const std::string &filename) : BaseGenerator(parser, path, filename, "", "" /*Unused*/, "swift") {} bool generate() { code_.Clear(); code_ += "// Generated GRPC code for FlatBuffers swift!"; code_ += grpc_swift_generator::GenerateHeader(); FlatBufFile file(parser_, file_name_, FlatBufFile::kLanguageSwift); for (int i = 0; i < file.service_count(); i++) { auto service = file.service(i); code_ += grpc_swift_generator::Generate(&file, service.get()); } const auto final_code = code_.ToString(); const auto filename = GeneratedFileName(path_, file_name_); return SaveFile(filename.c_str(), final_code, false); } static std::string GeneratedFileName(const std::string &path, const std::string &file_name) { return path + file_name + ".grpc.swift"; } }; bool GenerateSwiftGRPC(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 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) # pragma warning(pop) #endif