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/flatbuffers.h"
20 #include "flatbuffers/idl.h"
21 #include "flatbuffers/util.h"
22 #include "flatbuffers/code_generators.h"
23
24 #include "src/compiler/cpp_generator.h"
25 #include "src/compiler/go_generator.h"
26
27 namespace flatbuffers {
28
29 class FlatBufMethod : public grpc_generator::Method {
30 public:
31 enum Streaming { kNone, kClient, kServer, kBiDi };
32
FlatBufMethod(const RPCCall * method)33 FlatBufMethod(const RPCCall *method)
34 : method_(method) {
35 streaming_ = kNone;
36 auto val = method_->attributes.Lookup("streaming");
37 if (val) {
38 if (val->constant == "client") streaming_ = kClient;
39 if (val->constant == "server") streaming_ = kServer;
40 if (val->constant == "bidi") streaming_ = kBiDi;
41 }
42 }
43
name() const44 std::string name() const { return method_->name; }
45
GRPCType(const StructDef & sd) const46 std::string GRPCType(const StructDef &sd) const {
47 return "flatbuffers::BufferRef<" + sd.name + ">";
48 }
49
input_type_name() const50 std::string input_type_name() const {
51 return GRPCType(*method_->request);
52 }
output_type_name() const53 std::string output_type_name() const {
54 return GRPCType(*method_->response);
55 }
56
input_name() const57 std::string input_name() const {
58 return (*method_->request).name;
59 }
60
output_name() const61 std::string output_name() const {
62 return (*method_->response).name;
63 }
64
NoStreaming() const65 bool NoStreaming() const { return streaming_ == kNone; }
ClientOnlyStreaming() const66 bool ClientOnlyStreaming() const { return streaming_ == kClient; }
ServerOnlyStreaming() const67 bool ServerOnlyStreaming() const { return streaming_ == kServer; }
BidiStreaming() const68 bool BidiStreaming() const { return streaming_ == kBiDi; }
69
70 private:
71 const RPCCall *method_;
72 Streaming streaming_;
73 };
74
75 class FlatBufService : public grpc_generator::Service {
76 public:
FlatBufService(const ServiceDef * service)77 FlatBufService(const ServiceDef *service) : service_(service) {}
78
name() const79 std::string name() const { return service_->name; }
80
method_count() const81 int method_count() const {
82 return static_cast<int>(service_->calls.vec.size());
83 };
84
method(int i) const85 std::unique_ptr<const grpc_generator::Method> method(int i) const {
86 return std::unique_ptr<const grpc_generator::Method>(
87 new FlatBufMethod(service_->calls.vec[i]));
88 };
89
90 private:
91 const ServiceDef *service_;
92 };
93
94 class FlatBufPrinter : public grpc_generator::Printer {
95 public:
FlatBufPrinter(std::string * str)96 FlatBufPrinter(std::string *str)
97 : str_(str), escape_char_('$'), indent_(0) {}
98
Print(const std::map<std::string,std::string> & vars,const char * string_template)99 void Print(const std::map<std::string, std::string> &vars,
100 const char *string_template) {
101 std::string s = string_template;
102 // Replace any occurrences of strings in "vars" that are surrounded
103 // by the escape character by what they're mapped to.
104 size_t pos;
105 while ((pos = s.find(escape_char_)) != std::string::npos) {
106 // Found an escape char, must also find the closing one.
107 size_t pos2 = s.find(escape_char_, pos + 1);
108 // If placeholder not closed, ignore.
109 if (pos2 == std::string::npos) break;
110 auto it = vars.find(s.substr(pos + 1, pos2 - pos - 1));
111 // If unknown placeholder, ignore.
112 if (it == vars.end()) break;
113 // Subtitute placeholder.
114 s.replace(pos, pos2 - pos + 1, it->second);
115 }
116 Print(s.c_str());
117 }
118
Print(const char * s)119 void Print(const char *s) {
120 // Add this string, but for each part separated by \n, add indentation.
121 for (;;) {
122 // Current indentation.
123 str_->insert(str_->end(), indent_ * 2, ' ');
124 // See if this contains more than one line.
125 const char * lf = strchr(s, '\n');
126 if (lf) {
127 (*str_) += std::string(s, lf + 1);
128 s = lf + 1;
129 if (!*s) break; // Only continue if there's more lines.
130 } else {
131 (*str_) += s;
132 break;
133 }
134 }
135 }
136
Indent()137 void Indent() { indent_++; }
Outdent()138 void Outdent() { indent_--; assert(indent_ >= 0); }
139
140 private:
141 std::string *str_;
142 char escape_char_;
143 int indent_;
144 };
145
146 class FlatBufFile : public grpc_generator::File {
147 public:
FlatBufFile(const Parser & parser,const std::string & file_name)148 FlatBufFile(const Parser &parser, const std::string &file_name)
149 : parser_(parser), file_name_(file_name) {}
150 FlatBufFile &operator=(const FlatBufFile &);
151
filename() const152 std::string filename() const { return file_name_; }
filename_without_ext() const153 std::string filename_without_ext() const {
154 return StripExtension(file_name_);
155 }
156
message_header_ext() const157 std::string message_header_ext() const { return "_generated.h"; }
service_header_ext() const158 std::string service_header_ext() const { return ".grpc.fb.h"; }
159
package() const160 std::string package() const {
161 return parser_.namespaces_.back()->GetFullyQualifiedName("");
162 }
163
package_parts() const164 std::vector<std::string> package_parts() const {
165 return parser_.namespaces_.back()->components;
166 }
167
additional_headers() const168 std::string additional_headers() const {
169 return "#include \"flatbuffers/grpc.h\"\n";
170 }
171
additional_imports() const172 std::string additional_imports() const {
173 return "import \"github.com/google/flatbuffers/go\"";
174 }
175
service_count() const176 int service_count() const {
177 return static_cast<int>(parser_.services_.vec.size());
178 };
179
service(int i) const180 std::unique_ptr<const grpc_generator::Service> service(int i) const {
181 return std::unique_ptr<const grpc_generator::Service> (
182 new FlatBufService(parser_.services_.vec[i]));
183 }
184
CreatePrinter(std::string * str) const185 std::unique_ptr<grpc_generator::Printer> CreatePrinter(std::string *str) const {
186 return std::unique_ptr<grpc_generator::Printer>(
187 new FlatBufPrinter(str));
188 }
189
190 private:
191 const Parser &parser_;
192 const std::string &file_name_;
193 };
194
195 class GoGRPCGenerator : public flatbuffers::BaseGenerator {
196 public:
GoGRPCGenerator(const Parser & parser,const std::string & path,const std::string & file_name)197 GoGRPCGenerator(const Parser &parser, const std::string &path,
198 const std::string &file_name)
199 : BaseGenerator(parser, path, file_name, "", "" /*Unused*/),
200 parser_(parser), path_(path), file_name_(file_name) {}
201
generate()202 bool generate() {
203 FlatBufFile file(parser_, file_name_);
204 grpc_go_generator::Parameters p;
205 p.custom_method_io_type = "flatbuffers.Builder";
206 for (int i = 0; i < file.service_count(); i++) {
207 auto service = file.service(i);
208 const Definition *def = parser_.services_.vec[i];
209 p.package_name = LastNamespacePart(*(def->defined_namespace));
210 std::string output = grpc_go_generator::GenerateServiceSource(&file, service.get(), &p);
211 std::string filename = NamespaceDir(*def->defined_namespace) + def->name + "_grpc.go";
212 if (!flatbuffers::SaveFile(filename.c_str(), output, false))
213 return false;
214 }
215 return true;
216 }
217
218 protected:
219 const Parser &parser_;
220 const std::string &path_, &file_name_;
221 };
222
GenerateGoGRPC(const Parser & parser,const std::string & path,const std::string & file_name)223 bool GenerateGoGRPC(const Parser &parser,
224 const std::string &path,
225 const std::string &file_name) {
226 int nservices = 0;
227 for (auto it = parser.services_.vec.begin();
228 it != parser.services_.vec.end(); ++it) {
229 if (!(*it)->generated) nservices++;
230 }
231 if (!nservices) return true;
232 return GoGRPCGenerator(parser, path, file_name).generate();
233 }
234
GenerateCppGRPC(const Parser & parser,const std::string &,const std::string & file_name)235 bool GenerateCppGRPC(const Parser &parser,
236 const std::string &/*path*/,
237 const std::string &file_name) {
238
239 int nservices = 0;
240 for (auto it = parser.services_.vec.begin();
241 it != parser.services_.vec.end(); ++it) {
242 if (!(*it)->generated) nservices++;
243 }
244 if (!nservices) return true;
245
246 grpc_cpp_generator::Parameters generator_parameters;
247 // TODO(wvo): make the other parameters in this struct configurable.
248 generator_parameters.use_system_headers = true;
249
250 FlatBufFile fbfile(parser, file_name);
251
252 std::string header_code =
253 grpc_cpp_generator::GetHeaderPrologue(&fbfile, generator_parameters) +
254 grpc_cpp_generator::GetHeaderIncludes(&fbfile, generator_parameters) +
255 grpc_cpp_generator::GetHeaderServices(&fbfile, generator_parameters) +
256 grpc_cpp_generator::GetHeaderEpilogue(&fbfile, generator_parameters);
257
258 std::string source_code =
259 grpc_cpp_generator::GetSourcePrologue(&fbfile, generator_parameters) +
260 grpc_cpp_generator::GetSourceIncludes(&fbfile, generator_parameters) +
261 grpc_cpp_generator::GetSourceServices(&fbfile, generator_parameters) +
262 grpc_cpp_generator::GetSourceEpilogue(&fbfile, generator_parameters);
263
264 return flatbuffers::SaveFile((file_name + ".grpc.fb.h").c_str(),
265 header_code, false) &&
266 flatbuffers::SaveFile((file_name + ".grpc.fb.cc").c_str(),
267 source_code, false);
268 }
269
270 } // namespace flatbuffers
271
272