| /* | |
| * 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 <cstdio> | |
| #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/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<grpc::string> 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<std::string> get_input_namespace_parts() const { | |
| return (*method_->request).defined_namespace->components; | |
| } | |
| std::string get_input_type_name() const { return (*method_->request).name; } | |
| std::vector<std::string> 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<grpc::string> GetAllComments() const { | |
| return service_->doc_comment; | |
| } | |
| std::vector<grpc::string> 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<int>(service_->calls.vec.size()); | |
| } | |
| std::unique_ptr<const grpc_generator::Method> method(int i) const { | |
| return std::unique_ptr<const grpc_generator::Method>( | |
| new FlatBufMethod(service_->calls.vec[i])); | |
| } | |
| private: | |
| const ServiceDef* service_; | |
| }; | |
| class FlatBufPrinter : public grpc_generator::Printer { | |
| public: | |
| FlatBufPrinter(std::string* str, const char indentation_type) | |
| : str_(str), | |
| escape_char_('$'), | |
| indent_(0), | |
| indentation_size_(2), | |
| indentation_type_(indentation_type) {} | |
| void Print(const std::map<std::string, std::string>& 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_ * indentation_size_, indentation_type_); | |
| // 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 SetIndentationSize(const size_t size) { | |
| FLATBUFFERS_ASSERT(str_->empty()); | |
| indentation_size_ = size; | |
| } | |
| void Indent() { indent_++; } | |
| void Outdent() { | |
| FLATBUFFERS_ASSERT(indent_ > 0); | |
| indent_--; | |
| } | |
| private: | |
| std::string* str_; | |
| char escape_char_; | |
| size_t indent_; | |
| size_t indentation_size_; | |
| char indentation_type_; | |
| }; | |
| 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<grpc::string> GetAllComments() const { | |
| return std::vector<grpc::string>(); | |
| } | |
| std::string filename() const { return file_name_; } | |
| std::string filename_without_ext() const { | |
| return StripExtension(file_name_); | |
| } | |
| std::string package() const { | |
| return parser_.current_namespace_->GetFullyQualifiedName(""); | |
| } | |
| std::vector<std::string> package_parts() const { | |
| return parser_.current_namespace_->components; | |
| } | |
| std::string additional_headers() const { | |
| switch (language_) { | |
| case kLanguageCpp: { | |
| if (!parser_.opts.grpc_additional_headers.empty()) { | |
| std::string result = ""; | |
| for (const std::string& header : | |
| parser_.opts.grpc_additional_headers) { | |
| if (!result.empty()) result += "\n"; | |
| result += "#include \"" + header + "\""; | |
| } | |
| return result; | |
| } | |
| 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<int>(parser_.services_.vec.size()); | |
| } | |
| std::unique_ptr<const grpc_generator::Service> service(int i) const { | |
| return std::unique_ptr<const grpc_generator::Service>( | |
| new FlatBufService(parser_.services_.vec[i])); | |
| } | |
| std::unique_ptr<grpc_generator::Printer> CreatePrinter( | |
| std::string* str, const char indentation_type = ' ') const { | |
| return std::unique_ptr<grpc_generator::Printer>( | |
| new FlatBufPrinter(str, indentation_type)); | |
| } | |
| 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 (!parser_.opts.file_saver->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) { | |
| const auto& opts = parser.opts; | |
| int nservices = 0; | |
| for (auto it = parser.services_.vec.begin(); it != parser.services_.vec.end(); | |
| ++it) { | |
| if (!(*it)->generated) nservices++; | |
| } | |
| if (!nservices) return true; | |
| std::string suffix = ""; | |
| suffix += opts.filename_suffix.empty() ? "_generated" : opts.filename_suffix; | |
| suffix += "."; | |
| suffix += opts.filename_extension.empty() ? "h" : opts.filename_extension; | |
| grpc_cpp_generator::Parameters generator_parameters; | |
| // TODO(wvo): make the other parameters in this struct configurable. | |
| generator_parameters.use_system_headers = opts.grpc_use_system_headers; | |
| generator_parameters.message_header_extension = suffix; | |
| generator_parameters.service_header_extension = | |
| ".grpc" + opts.grpc_filename_suffix + ".h"; | |
| generator_parameters.grpc_search_path = opts.grpc_search_path; | |
| generator_parameters.generate_callback_api = opts.grpc_callback_api; | |
| std::string filename = flatbuffers::StripExtension(parser.file_being_parsed_); | |
| if (!opts.keep_prefix) { | |
| filename = flatbuffers::StripPath(filename); | |
| } | |
| FlatBufFile fbfile(parser, filename, 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 parser.opts.file_saver->SaveFile( | |
| (path + file_name + ".grpc" + opts.grpc_filename_suffix + ".h") | |
| .c_str(), | |
| header_code, false) && | |
| parser.opts.file_saver->SaveFile( | |
| (path + file_name + ".grpc" + opts.grpc_filename_suffix + ".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 (!parser_.opts.file_saver->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; | |
| flatbuffers::python::Version version{parser.opts.python_version}; | |
| if (!version.IsValid()) return false; | |
| if (!flatbuffers::python::grpc::Generate(parser, path, version)) { | |
| return false; | |
| } | |
| if (parser.opts.python_typing) { | |
| return flatbuffers::python::grpc::GenerateStub(parser, path, version); | |
| } | |
| return true; | |
| } | |
| 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 parser_.opts.file_saver->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 (!parser_.opts.file_saver->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 (!parser_.opts.file_saver->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 |