[C++] Fix code gen indentation when empty Print (#4313)
[platform/upstream/flatbuffers.git] / src / idl_gen_grpc.cpp
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 #if defined(_MSC_VER)
28 #pragma warning(push)
29 #pragma warning(disable: 4512) // C4512: 'class' : assignment operator could not be generated
30 #endif
31
32 namespace flatbuffers {
33
34 class FlatBufMethod : public grpc_generator::Method {
35  public:
36   enum Streaming { kNone, kClient, kServer, kBiDi };
37
38   FlatBufMethod(const RPCCall *method)
39     : method_(method) {
40     streaming_ = kNone;
41     auto val = method_->attributes.Lookup("streaming");
42     if (val) {
43       if (val->constant == "client") streaming_ = kClient;
44       if (val->constant == "server") streaming_ = kServer;
45       if (val->constant == "bidi") streaming_ = kBiDi;
46     }
47   }
48
49   grpc::string GetLeadingComments(const grpc::string) const {
50     return "";
51   }
52   grpc::string GetTrailingComments(const grpc::string) const {
53     return "";
54   }
55   std::vector<grpc::string> GetAllComments() const {
56     return std::vector<grpc::string>();
57   }
58
59   std::string name() const { return method_->name; }
60
61   std::string GRPCType(const StructDef &sd) const {
62     return "flatbuffers::BufferRef<" + sd.name + ">";
63   }
64
65   std::string get_input_type_name() const {
66     return (*method_->request).name;
67   }
68   std::string get_output_type_name() const {
69     return (*method_->response).name;
70   }
71
72   bool get_module_and_message_path_input(
73       grpc::string * /*str*/, grpc::string /*generator_file_name*/,
74       bool /*generate_in_pb2_grpc*/, grpc::string /*import_prefix*/) const {
75     return true;
76   }
77
78   bool get_module_and_message_path_output(
79       grpc::string * /*str*/, grpc::string /*generator_file_name*/,
80       bool /*generate_in_pb2_grpc*/, grpc::string /*import_prefix*/) const {
81     return true;
82   }
83
84   std::string input_type_name() const {
85     return GRPCType(*method_->request);
86   }
87
88   std::string output_type_name() const {
89     return GRPCType(*method_->response);
90   }
91
92   bool NoStreaming() const { return streaming_ == kNone; }
93   bool ClientStreaming() const { return streaming_ == kClient; }
94   bool ServerStreaming() const { return streaming_ == kServer; }
95   bool BidiStreaming() const { return streaming_ == kBiDi; }
96
97  private:
98   const RPCCall *method_;
99   Streaming streaming_;
100 };
101
102 class FlatBufService : public grpc_generator::Service {
103  public:
104   FlatBufService(const ServiceDef *service) : service_(service) {}
105
106   grpc::string GetLeadingComments(const grpc::string) const {
107     return "";
108   }
109   grpc::string GetTrailingComments(const grpc::string) const {
110     return "";
111   }
112   std::vector<grpc::string> GetAllComments() const {
113     return std::vector<grpc::string>();
114   }
115
116   std::string name() const { return service_->name; }
117
118   int method_count() const {
119     return static_cast<int>(service_->calls.vec.size());
120   };
121
122   std::unique_ptr<const grpc_generator::Method> method(int i) const {
123     return std::unique_ptr<const grpc_generator::Method>(
124           new FlatBufMethod(service_->calls.vec[i]));
125   };
126
127  private:
128   const ServiceDef *service_;
129 };
130
131 class FlatBufPrinter : public grpc_generator::Printer {
132  public:
133   FlatBufPrinter(std::string *str)
134     : str_(str), escape_char_('$'), indent_(0) {}
135
136   void Print(const std::map<std::string, std::string> &vars,
137              const char *string_template) {
138     std::string s = string_template;
139     // Replace any occurrences of strings in "vars" that are surrounded
140     // by the escape character by what they're mapped to.
141     size_t pos;
142     while ((pos = s.find(escape_char_)) != std::string::npos) {
143       // Found an escape char, must also find the closing one.
144       size_t pos2 = s.find(escape_char_, pos + 1);
145       // If placeholder not closed, ignore.
146       if (pos2 == std::string::npos) break;
147       auto it = vars.find(s.substr(pos + 1, pos2 - pos - 1));
148       // If unknown placeholder, ignore.
149       if (it == vars.end()) break;
150       // Subtitute placeholder.
151       s.replace(pos, pos2 - pos + 1, it->second);
152     }
153     Print(s.c_str());
154   }
155
156   void Print(const char *s) {
157     if (s == nullptr || std::strlen(s) == 0) {
158       return;
159     }
160     // Add this string, but for each part separated by \n, add indentation.
161     for (;;) {
162       // Current indentation.
163       str_->insert(str_->end(), indent_ * 2, ' ');
164       // See if this contains more than one line.
165       const char * lf = strchr(s, '\n');
166       if (lf) {
167         (*str_) += std::string(s, lf + 1);
168         s = lf + 1;
169         if (!*s) break;  // Only continue if there's more lines.
170       } else {
171         (*str_) += s;
172         break;
173       }
174     }
175   }
176
177   void Indent() { indent_++; }
178   void Outdent() { indent_--; assert(indent_ >= 0); }
179
180  private:
181   std::string *str_;
182   char escape_char_;
183   int indent_;
184 };
185
186 class FlatBufFile : public grpc_generator::File {
187  public:
188   enum Language {
189     kLanguageGo,
190     kLanguageCpp
191   };
192
193   FlatBufFile(
194       const Parser &parser, const std::string &file_name, Language language)
195     : parser_(parser), file_name_(file_name), language_(language) {}
196   FlatBufFile &operator=(const FlatBufFile &);
197
198   grpc::string GetLeadingComments(const grpc::string) const {
199     return "";
200   }
201   grpc::string GetTrailingComments(const grpc::string) const {
202     return "";
203   }
204   std::vector<grpc::string> GetAllComments() const {
205     return std::vector<grpc::string>();
206   }
207
208   std::string filename() const { return file_name_; }
209   std::string filename_without_ext() const {
210     return StripExtension(file_name_);
211   }
212
213   std::string message_header_ext() const { return "_generated.h"; }
214   std::string service_header_ext() const { return ".grpc.fb.h"; }
215
216   std::string package() const {
217     return parser_.namespaces_.back()->GetFullyQualifiedName("");
218   }
219
220   std::vector<std::string> package_parts() const {
221     return parser_.namespaces_.back()->components;
222   }
223
224   std::string additional_headers() const {
225     switch (language_) {
226       case kLanguageCpp: {
227         return "#include \"flatbuffers/grpc.h\"\n";
228       }
229       case kLanguageGo: {
230         return "import \"github.com/google/flatbuffers/go\"";
231       }
232     }
233     return "";
234   }
235
236   int service_count() const {
237     return static_cast<int>(parser_.services_.vec.size());
238   };
239
240   std::unique_ptr<const grpc_generator::Service> service(int i) const {
241     return std::unique_ptr<const grpc_generator::Service> (
242           new FlatBufService(parser_.services_.vec[i]));
243   }
244
245   std::unique_ptr<grpc_generator::Printer> CreatePrinter(std::string *str) const {
246     return std::unique_ptr<grpc_generator::Printer>(
247           new FlatBufPrinter(str));
248   }
249
250  private:
251   const Parser &parser_;
252   const std::string &file_name_;
253   const Language language_;
254 };
255
256 class GoGRPCGenerator : public flatbuffers::BaseGenerator {
257  public:
258   GoGRPCGenerator(const Parser &parser, const std::string &path,
259                   const std::string &file_name)
260     : BaseGenerator(parser, path, file_name, "", "" /*Unused*/),
261       parser_(parser), path_(path), file_name_(file_name) {}
262
263   bool generate() {
264     FlatBufFile file(parser_, file_name_, FlatBufFile::kLanguageGo);
265     grpc_go_generator::Parameters p;
266     p.custom_method_io_type = "flatbuffers.Builder";
267     for (int i = 0; i < file.service_count(); i++) {
268       auto service = file.service(i);
269       const Definition *def = parser_.services_.vec[i];
270       p.package_name = LastNamespacePart(*(def->defined_namespace));
271       std::string output = grpc_go_generator::GenerateServiceSource(&file, service.get(), &p);
272       std::string filename = NamespaceDir(*def->defined_namespace) + def->name + "_grpc.go";
273       if (!flatbuffers::SaveFile(filename.c_str(), output, false))
274         return false;
275     }
276     return true;
277   }
278
279  protected:
280   const Parser &parser_;
281   const std::string &path_, &file_name_;
282 };
283
284 bool GenerateGoGRPC(const Parser &parser,
285                     const std::string &path,
286                     const std::string &file_name) {
287   int nservices = 0;
288   for (auto it = parser.services_.vec.begin();
289        it != parser.services_.vec.end(); ++it) {
290     if (!(*it)->generated) nservices++;
291   }
292   if (!nservices) return true;
293   return GoGRPCGenerator(parser, path, file_name).generate();
294 }
295
296 bool GenerateCppGRPC(const Parser &parser,
297                   const std::string &path,
298                   const std::string &file_name) {
299
300   int nservices = 0;
301   for (auto it = parser.services_.vec.begin();
302        it != parser.services_.vec.end(); ++it) {
303     if (!(*it)->generated) nservices++;
304   }
305   if (!nservices) return true;
306
307   grpc_cpp_generator::Parameters generator_parameters;
308   // TODO(wvo): make the other parameters in this struct configurable.
309   generator_parameters.use_system_headers = true;
310
311   FlatBufFile fbfile(parser, file_name, FlatBufFile::kLanguageCpp);
312
313   std::string header_code =
314       grpc_cpp_generator::GetHeaderPrologue(&fbfile, generator_parameters) +
315       grpc_cpp_generator::GetHeaderIncludes(&fbfile, generator_parameters) +
316       grpc_cpp_generator::GetHeaderServices(&fbfile, generator_parameters) +
317       grpc_cpp_generator::GetHeaderEpilogue(&fbfile, generator_parameters);
318
319   std::string source_code =
320       grpc_cpp_generator::GetSourcePrologue(&fbfile, generator_parameters) +
321       grpc_cpp_generator::GetSourceIncludes(&fbfile, generator_parameters) +
322       grpc_cpp_generator::GetSourceServices(&fbfile, generator_parameters) +
323       grpc_cpp_generator::GetSourceEpilogue(&fbfile, generator_parameters);
324
325   return flatbuffers::SaveFile((path + file_name + ".grpc.fb.h").c_str(),
326                                header_code, false) &&
327          flatbuffers::SaveFile((path + file_name + ".grpc.fb.cc").c_str(),
328                                source_code, false);
329 }
330
331 }  // namespace flatbuffers
332
333 #if defined(_MSC_VER)
334 #pragma warning(pop)
335 #endif
336