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 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 44 std::string name() const { return method_->name; } 45 46 std::string GRPCType(const StructDef &sd) const { 47 return "flatbuffers::BufferRef<" + sd.name + ">"; 48 } 49 50 std::string input_type_name() const { 51 return GRPCType(*method_->request); 52 } 53 std::string output_type_name() const { 54 return GRPCType(*method_->response); 55 } 56 57 std::string input_name() const { 58 return (*method_->request).name; 59 } 60 61 std::string output_name() const { 62 return (*method_->response).name; 63 } 64 65 bool NoStreaming() const { return streaming_ == kNone; } 66 bool ClientOnlyStreaming() const { return streaming_ == kClient; } 67 bool ServerOnlyStreaming() const { return streaming_ == kServer; } 68 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: 77 FlatBufService(const ServiceDef *service) : service_(service) {} 78 79 std::string name() const { return service_->name; } 80 81 int method_count() const { 82 return static_cast<int>(service_->calls.vec.size()); 83 }; 84 85 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: 96 FlatBufPrinter(std::string *str) 97 : str_(str), escape_char_('$'), indent_(0) {} 98 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 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 137 void Indent() { indent_++; } 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: 148 FlatBufFile(const Parser &parser, const std::string &file_name) 149 : parser_(parser), file_name_(file_name) {} 150 FlatBufFile &operator=(const FlatBufFile &); 151 152 std::string filename() const { return file_name_; } 153 std::string filename_without_ext() const { 154 return StripExtension(file_name_); 155 } 156 157 std::string message_header_ext() const { return "_generated.h"; } 158 std::string service_header_ext() const { return ".grpc.fb.h"; } 159 160 std::string package() const { 161 return parser_.namespaces_.back()->GetFullyQualifiedName(""); 162 } 163 164 std::vector<std::string> package_parts() const { 165 return parser_.namespaces_.back()->components; 166 } 167 168 std::string additional_headers() const { 169 return "#include \"flatbuffers/grpc.h\"\n"; 170 } 171 172 std::string additional_imports() const { 173 return "import \"github.com/google/flatbuffers/go\""; 174 } 175 176 int service_count() const { 177 return static_cast<int>(parser_.services_.vec.size()); 178 }; 179 180 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 185 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: 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 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 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 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