561 lines
18 KiB
C++
561 lines
18 KiB
C++
/*
|
|
* 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/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: {
|
|
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 (!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) {
|
|
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 = true;
|
|
generator_parameters.message_header_extension = suffix;
|
|
|
|
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();
|
|
}
|
|
|
|
class PythonGRPCGenerator : public flatbuffers::BaseGenerator {
|
|
private:
|
|
CodeWriter code_;
|
|
|
|
public:
|
|
PythonGRPCGenerator(const Parser &parser, const std::string &filename)
|
|
: BaseGenerator(parser, "", filename, "", "" /*Unused*/, "swift") {}
|
|
|
|
bool generate() {
|
|
code_.Clear();
|
|
code_ +=
|
|
"# Generated by the gRPC Python protocol compiler plugin. "
|
|
"DO NOT EDIT!\n";
|
|
code_ += "import grpc\n";
|
|
|
|
FlatBufFile file(parser_, file_name_, FlatBufFile::kLanguagePython);
|
|
|
|
for (int i = 0; i < file.service_count(); i++) {
|
|
auto service = file.service(i);
|
|
code_ += grpc_python_generator::Generate(&file, service.get());
|
|
}
|
|
const auto final_code = code_.ToString();
|
|
const auto filename = GenerateFileName();
|
|
return SaveFile(filename.c_str(), final_code, false);
|
|
}
|
|
|
|
std::string GenerateFileName() {
|
|
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;
|
|
return grpc_py_filename + file_name_ + "_grpc_fb.py";
|
|
}
|
|
};
|
|
|
|
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;
|
|
|
|
return PythonGRPCGenerator(parser, file_name).generate();
|
|
}
|
|
|
|
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
|