/* * 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" #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; } std::string GRPCType(const StructDef &sd) const { return "flatbuffers::grpc::Message<" + sd.name + ">"; } std::string get_input_type_name() const { return (*method_->request).name; } 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 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::string name() const { return service_->name; } 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 || std::strlen(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 }; 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;"; } } 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*/), 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*/) {} 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(); } } // namespace flatbuffers #if defined(_MSC_VER) # pragma warning(pop) #endif