| 1 | // Protocol Buffers - Google's data interchange format | 
| 2 | // Copyright 2008 Google Inc.  All rights reserved. | 
| 3 | // https://developers.google.com/protocol-buffers/ | 
| 4 | // | 
| 5 | // Redistribution and use in source and binary forms, with or without | 
| 6 | // modification, are permitted provided that the following conditions are | 
| 7 | // met: | 
| 8 | // | 
| 9 | //     * Redistributions of source code must retain the above copyright | 
| 10 | // notice, this list of conditions and the following disclaimer. | 
| 11 | //     * Redistributions in binary form must reproduce the above | 
| 12 | // copyright notice, this list of conditions and the following disclaimer | 
| 13 | // in the documentation and/or other materials provided with the | 
| 14 | // distribution. | 
| 15 | //     * Neither the name of Google Inc. nor the names of its | 
| 16 | // contributors may be used to endorse or promote products derived from | 
| 17 | // this software without specific prior written permission. | 
| 18 | // | 
| 19 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | 
| 20 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | 
| 21 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | 
| 22 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | 
| 23 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | 
| 24 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | 
| 25 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | 
| 26 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 
| 27 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 
| 28 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 
| 29 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 
| 30 |  | 
| 31 | #include <iomanip> | 
| 32 | #include <sstream> | 
| 33 |  | 
| 34 | #include <google/protobuf/compiler/code_generator.h> | 
| 35 | #include <google/protobuf/compiler/plugin.h> | 
| 36 | #include <google/protobuf/descriptor.h> | 
| 37 | #include <google/protobuf/descriptor.pb.h> | 
| 38 | #include <google/protobuf/io/printer.h> | 
| 39 | #include <google/protobuf/io/zero_copy_stream.h> | 
| 40 |  | 
| 41 | #include <google/protobuf/compiler/ruby/ruby_generator.h> | 
| 42 |  | 
| 43 | namespace google { | 
| 44 | namespace protobuf { | 
| 45 | namespace compiler { | 
| 46 | namespace ruby { | 
| 47 |  | 
| 48 | // Forward decls. | 
| 49 | template <class numeric_type> | 
| 50 | std::string NumberToString(numeric_type value); | 
| 51 | std::string GetRequireName(const std::string& proto_file); | 
| 52 | std::string LabelForField(FieldDescriptor* field); | 
| 53 | std::string TypeName(FieldDescriptor* field); | 
| 54 | bool GenerateMessage(const Descriptor* message, io::Printer* printer, | 
| 55 |                      std::string* error); | 
| 56 | void GenerateEnum(const EnumDescriptor* en, io::Printer* printer); | 
| 57 | void GenerateMessageAssignment(const std::string& prefix, | 
| 58 |                                const Descriptor* message, io::Printer* printer); | 
| 59 | void GenerateEnumAssignment(const std::string& prefix, const EnumDescriptor* en, | 
| 60 |                             io::Printer* printer); | 
| 61 | std::string DefaultValueForField(const FieldDescriptor* field); | 
| 62 |  | 
| 63 | template<class numeric_type> | 
| 64 | std::string NumberToString(numeric_type value) { | 
| 65 |   std::ostringstream os; | 
| 66 |   os << value; | 
| 67 |   return os.str(); | 
| 68 | } | 
| 69 |  | 
| 70 | std::string GetRequireName(const std::string& proto_file) { | 
| 71 |   int lastindex = proto_file.find_last_of(s: "." ); | 
| 72 |   return proto_file.substr(pos: 0, n: lastindex) + "_pb" ; | 
| 73 | } | 
| 74 |  | 
| 75 | std::string GetOutputFilename(const std::string& proto_file) { | 
| 76 |   return GetRequireName(proto_file) + ".rb" ; | 
| 77 | } | 
| 78 |  | 
| 79 | std::string LabelForField(const FieldDescriptor* field) { | 
| 80 |   if (field->has_optional_keyword() && | 
| 81 |       field->file()->syntax() == FileDescriptor::SYNTAX_PROTO3) { | 
| 82 |     return "proto3_optional" ; | 
| 83 |   } | 
| 84 |   switch (field->label()) { | 
| 85 |     case FieldDescriptor::LABEL_OPTIONAL: return "optional" ; | 
| 86 |     case FieldDescriptor::LABEL_REQUIRED: return "required" ; | 
| 87 |     case FieldDescriptor::LABEL_REPEATED: return "repeated" ; | 
| 88 |     default: assert(false); return "" ; | 
| 89 |   } | 
| 90 | } | 
| 91 |  | 
| 92 | std::string TypeName(const FieldDescriptor* field) { | 
| 93 |   switch (field->type()) { | 
| 94 |     case FieldDescriptor::TYPE_INT32: return "int32" ; | 
| 95 |     case FieldDescriptor::TYPE_INT64: return "int64" ; | 
| 96 |     case FieldDescriptor::TYPE_UINT32: return "uint32" ; | 
| 97 |     case FieldDescriptor::TYPE_UINT64: return "uint64" ; | 
| 98 |     case FieldDescriptor::TYPE_SINT32: return "sint32" ; | 
| 99 |     case FieldDescriptor::TYPE_SINT64: return "sint64" ; | 
| 100 |     case FieldDescriptor::TYPE_FIXED32: return "fixed32" ; | 
| 101 |     case FieldDescriptor::TYPE_FIXED64: return "fixed64" ; | 
| 102 |     case FieldDescriptor::TYPE_SFIXED32: return "sfixed32" ; | 
| 103 |     case FieldDescriptor::TYPE_SFIXED64: return "sfixed64" ; | 
| 104 |     case FieldDescriptor::TYPE_DOUBLE: return "double" ; | 
| 105 |     case FieldDescriptor::TYPE_FLOAT: return "float" ; | 
| 106 |     case FieldDescriptor::TYPE_BOOL: return "bool" ; | 
| 107 |     case FieldDescriptor::TYPE_ENUM: return "enum" ; | 
| 108 |     case FieldDescriptor::TYPE_STRING: return "string" ; | 
| 109 |     case FieldDescriptor::TYPE_BYTES: return "bytes" ; | 
| 110 |     case FieldDescriptor::TYPE_MESSAGE: return "message" ; | 
| 111 |     case FieldDescriptor::TYPE_GROUP: return "group" ; | 
| 112 |     default: assert(false); return "" ; | 
| 113 |   } | 
| 114 | } | 
| 115 |  | 
| 116 | std::string StringifySyntax(FileDescriptor::Syntax syntax) { | 
| 117 |   switch (syntax) { | 
| 118 |     case FileDescriptor::SYNTAX_PROTO2: | 
| 119 |       return "proto2" ; | 
| 120 |     case FileDescriptor::SYNTAX_PROTO3: | 
| 121 |       return "proto3" ; | 
| 122 |     case FileDescriptor::SYNTAX_UNKNOWN: | 
| 123 |     default: | 
| 124 |       GOOGLE_LOG(FATAL) << "Unsupported syntax; this generator only supports "  | 
| 125 |                            "proto2 and proto3 syntax." ; | 
| 126 |       return "" ; | 
| 127 |   } | 
| 128 | } | 
| 129 |  | 
| 130 | std::string DefaultValueForField(const FieldDescriptor* field) { | 
| 131 |   switch(field->cpp_type()) { | 
| 132 |     case FieldDescriptor::CPPTYPE_INT32: | 
| 133 |       return NumberToString(value: field->default_value_int32()); | 
| 134 |     case FieldDescriptor::CPPTYPE_INT64: | 
| 135 |       return NumberToString(value: field->default_value_int64()); | 
| 136 |     case FieldDescriptor::CPPTYPE_UINT32: | 
| 137 |       return NumberToString(value: field->default_value_uint32()); | 
| 138 |     case FieldDescriptor::CPPTYPE_UINT64: | 
| 139 |       return NumberToString(value: field->default_value_uint64()); | 
| 140 |     case FieldDescriptor::CPPTYPE_FLOAT: | 
| 141 |       return NumberToString(value: field->default_value_float()); | 
| 142 |     case FieldDescriptor::CPPTYPE_DOUBLE: | 
| 143 |       return NumberToString(value: field->default_value_double()); | 
| 144 |     case FieldDescriptor::CPPTYPE_BOOL: | 
| 145 |       return field->default_value_bool() ? "true"  : "false" ; | 
| 146 |     case FieldDescriptor::CPPTYPE_ENUM: | 
| 147 |       return NumberToString(value: field->default_value_enum()->number()); | 
| 148 |     case FieldDescriptor::CPPTYPE_STRING: { | 
| 149 |       std::ostringstream os; | 
| 150 |       std::string default_str = field->default_value_string(); | 
| 151 |  | 
| 152 |       if (field->type() == FieldDescriptor::TYPE_STRING) { | 
| 153 |         os << "\""  << default_str << "\"" ; | 
| 154 |       } else if (field->type() == FieldDescriptor::TYPE_BYTES) { | 
| 155 |         os << "\"" ; | 
| 156 |  | 
| 157 |         os.fill(ch: '0'); | 
| 158 |         for (int i = 0; i < default_str.length(); ++i) { | 
| 159 |           // Write the hex form of each byte. | 
| 160 |           os << "\\x"  << std::hex << std::setw(2) | 
| 161 |              << ((uint16_t)((unsigned char)default_str.at(n: i))); | 
| 162 |         } | 
| 163 |         os << "\".force_encoding(\"ASCII-8BIT\")" ; | 
| 164 |       } | 
| 165 |  | 
| 166 |       return os.str(); | 
| 167 |     } | 
| 168 |     default: assert(false); return "" ; | 
| 169 |   } | 
| 170 | } | 
| 171 |  | 
| 172 | void GenerateField(const FieldDescriptor* field, io::Printer* printer) { | 
| 173 |   if (field->is_map()) { | 
| 174 |     const FieldDescriptor* key_field = | 
| 175 |         field->message_type()->FindFieldByNumber(number: 1); | 
| 176 |     const FieldDescriptor* value_field = | 
| 177 |         field->message_type()->FindFieldByNumber(number: 2); | 
| 178 |  | 
| 179 |     printer->Print( | 
| 180 |       text: "map :$name$, :$key_type$, :$value_type$, $number$" , | 
| 181 |       args: "name" , args: field->name(), | 
| 182 |       args: "key_type" , args: TypeName(field: key_field), | 
| 183 |       args: "value_type" , args: TypeName(field: value_field), | 
| 184 |       args: "number" , args: NumberToString(value: field->number())); | 
| 185 |  | 
| 186 |     if (value_field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { | 
| 187 |       printer->Print( | 
| 188 |         text: ", \"$subtype$\"\n" , | 
| 189 |         args: "subtype" , args: value_field->message_type()->full_name()); | 
| 190 |     } else if (value_field->cpp_type() == FieldDescriptor::CPPTYPE_ENUM) { | 
| 191 |       printer->Print( | 
| 192 |         text: ", \"$subtype$\"\n" , | 
| 193 |         args: "subtype" , args: value_field->enum_type()->full_name()); | 
| 194 |     } else { | 
| 195 |       printer->Print(text: "\n" ); | 
| 196 |     } | 
| 197 |   } else { | 
| 198 |  | 
| 199 |     printer->Print( | 
| 200 |       text: "$label$ :$name$, " , | 
| 201 |       args: "label" , args: LabelForField(field), | 
| 202 |       args: "name" , args: field->name()); | 
| 203 |     printer->Print( | 
| 204 |       text: ":$type$, $number$" , | 
| 205 |       args: "type" , args: TypeName(field), | 
| 206 |       args: "number" , args: NumberToString(value: field->number())); | 
| 207 |  | 
| 208 |     if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { | 
| 209 |       printer->Print( | 
| 210 |         text: ", \"$subtype$\"" , | 
| 211 |        args: "subtype" , args: field->message_type()->full_name()); | 
| 212 |     } else if (field->cpp_type() == FieldDescriptor::CPPTYPE_ENUM) { | 
| 213 |       printer->Print( | 
| 214 |         text: ", \"$subtype$\"" , | 
| 215 |         args: "subtype" , args: field->enum_type()->full_name()); | 
| 216 |     } | 
| 217 |  | 
| 218 |     if (field->has_default_value()) { | 
| 219 |       printer->Print(text: ", default: $default$" , args: "default" , | 
| 220 |                      args: DefaultValueForField(field)); | 
| 221 |     } | 
| 222 |  | 
| 223 |     if (field->has_json_name()) { | 
| 224 |       printer->Print(text: ", json_name: \"$json_name$\"" , args: "json_name" , | 
| 225 |                     args: field->json_name()); | 
| 226 |     } | 
| 227 |  | 
| 228 |     printer->Print(text: "\n" ); | 
| 229 |   } | 
| 230 | } | 
| 231 |  | 
| 232 | void GenerateOneof(const OneofDescriptor* oneof, io::Printer* printer) { | 
| 233 |   printer->Print( | 
| 234 |       text: "oneof :$name$ do\n" , | 
| 235 |       args: "name" , args: oneof->name()); | 
| 236 |   printer->Indent(); | 
| 237 |  | 
| 238 |   for (int i = 0; i < oneof->field_count(); i++) { | 
| 239 |     const FieldDescriptor* field = oneof->field(index: i); | 
| 240 |     GenerateField(field, printer); | 
| 241 |   } | 
| 242 |  | 
| 243 |   printer->Outdent(); | 
| 244 |   printer->Print(text: "end\n" ); | 
| 245 | } | 
| 246 |  | 
| 247 | bool GenerateMessage(const Descriptor* message, io::Printer* printer, | 
| 248 |                      std::string* error) { | 
| 249 |   if (message->extension_range_count() > 0 || message->extension_count() > 0) { | 
| 250 |     GOOGLE_LOG(WARNING) << "Extensions are not yet supported for proto2 .proto files." ; | 
| 251 |   } | 
| 252 |  | 
| 253 |   // Don't generate MapEntry messages -- we use the Ruby extension's native | 
| 254 |   // support for map fields instead. | 
| 255 |   if (message->options().map_entry()) { | 
| 256 |     return true; | 
| 257 |   } | 
| 258 |  | 
| 259 |   printer->Print( | 
| 260 |     text: "add_message \"$name$\" do\n" , | 
| 261 |     args: "name" , args: message->full_name()); | 
| 262 |   printer->Indent(); | 
| 263 |  | 
| 264 |   for (int i = 0; i < message->field_count(); i++) { | 
| 265 |     const FieldDescriptor* field = message->field(index: i); | 
| 266 |     if (!field->real_containing_oneof()) { | 
| 267 |       GenerateField(field, printer); | 
| 268 |     } | 
| 269 |   } | 
| 270 |  | 
| 271 |   for (int i = 0; i < message->real_oneof_decl_count(); i++) { | 
| 272 |     const OneofDescriptor* oneof = message->oneof_decl(index: i); | 
| 273 |     GenerateOneof(oneof, printer); | 
| 274 |   } | 
| 275 |  | 
| 276 |   printer->Outdent(); | 
| 277 |   printer->Print(text: "end\n" ); | 
| 278 |  | 
| 279 |   for (int i = 0; i < message->nested_type_count(); i++) { | 
| 280 |     if (!GenerateMessage(message: message->nested_type(index: i), printer, error)) { | 
| 281 |       return false; | 
| 282 |     } | 
| 283 |   } | 
| 284 |   for (int i = 0; i < message->enum_type_count(); i++) { | 
| 285 |     GenerateEnum(en: message->enum_type(index: i), printer); | 
| 286 |   } | 
| 287 |  | 
| 288 |   return true; | 
| 289 | } | 
| 290 |  | 
| 291 | void GenerateEnum(const EnumDescriptor* en, io::Printer* printer) { | 
| 292 |   printer->Print( | 
| 293 |     text: "add_enum \"$name$\" do\n" , | 
| 294 |     args: "name" , args: en->full_name()); | 
| 295 |   printer->Indent(); | 
| 296 |  | 
| 297 |   for (int i = 0; i < en->value_count(); i++) { | 
| 298 |     const EnumValueDescriptor* value = en->value(index: i); | 
| 299 |     printer->Print( | 
| 300 |       text: "value :$name$, $number$\n" , | 
| 301 |       args: "name" , args: value->name(), | 
| 302 |       args: "number" , args: NumberToString(value: value->number())); | 
| 303 |   } | 
| 304 |  | 
| 305 |   printer->Outdent(); | 
| 306 |   printer->Print( | 
| 307 |     text: "end\n" ); | 
| 308 | } | 
| 309 |  | 
| 310 | // Locale-agnostic utility functions. | 
| 311 | bool IsLower(char ch) { return ch >= 'a' && ch <= 'z'; } | 
| 312 |  | 
| 313 | bool IsUpper(char ch) { return ch >= 'A' && ch <= 'Z'; } | 
| 314 |  | 
| 315 | bool IsAlpha(char ch) { return IsLower(ch) || IsUpper(ch); } | 
| 316 |  | 
| 317 | char UpperChar(char ch) { return IsLower(ch) ? (ch - 'a' + 'A') : ch; } | 
| 318 |  | 
| 319 |  | 
| 320 | // Package names in protobuf are snake_case by convention, but Ruby module | 
| 321 | // names must be PascalCased. | 
| 322 | // | 
| 323 | //   foo_bar_baz -> FooBarBaz | 
| 324 | std::string PackageToModule(const std::string& name) { | 
| 325 |   bool next_upper = true; | 
| 326 |   std::string result; | 
| 327 |   result.reserve(res_arg: name.size()); | 
| 328 |  | 
| 329 |   for (int i = 0; i < name.size(); i++) { | 
| 330 |     if (name[i] == '_') { | 
| 331 |       next_upper = true; | 
| 332 |     } else { | 
| 333 |       if (next_upper) { | 
| 334 |         result.push_back(c: UpperChar(ch: name[i])); | 
| 335 |       } else { | 
| 336 |         result.push_back(c: name[i]); | 
| 337 |       } | 
| 338 |       next_upper = false; | 
| 339 |     } | 
| 340 |   } | 
| 341 |  | 
| 342 |   return result; | 
| 343 | } | 
| 344 |  | 
| 345 | // Class and enum names in protobuf should be PascalCased by convention, but | 
| 346 | // since there is nothing enforcing this we need to ensure that they are valid | 
| 347 | // Ruby constants.  That mainly means making sure that the first character is | 
| 348 | // an upper-case letter. | 
| 349 | std::string RubifyConstant(const std::string& name) { | 
| 350 |   std::string ret = name; | 
| 351 |   if (!ret.empty()) { | 
| 352 |     if (IsLower(ch: ret[0])) { | 
| 353 |       // If it starts with a lowercase letter, capitalize it. | 
| 354 |       ret[0] = UpperChar(ch: ret[0]); | 
| 355 |     } else if (!IsAlpha(ch: ret[0])) { | 
| 356 |       // Otherwise (e.g. if it begins with an underscore), we need to come up | 
| 357 |       // with some prefix that starts with a capital letter. We could be smarter | 
| 358 |       // here, e.g. try to strip leading underscores, but this may cause other | 
| 359 |       // problems if the user really intended the name. So let's just prepend a | 
| 360 |       // well-known suffix. | 
| 361 |       ret = "PB_"  + ret; | 
| 362 |     } | 
| 363 |   } | 
| 364 |  | 
| 365 |   return ret; | 
| 366 | } | 
| 367 |  | 
| 368 | void GenerateMessageAssignment(const std::string& prefix, | 
| 369 |                                const Descriptor* message, | 
| 370 |                                io::Printer* printer) { | 
| 371 |   // Don't generate MapEntry messages -- we use the Ruby extension's native | 
| 372 |   // support for map fields instead. | 
| 373 |   if (message->options().map_entry()) { | 
| 374 |     return; | 
| 375 |   } | 
| 376 |  | 
| 377 |   printer->Print( | 
| 378 |     text: "$prefix$$name$ = " , | 
| 379 |     args: "prefix" , args: prefix, | 
| 380 |     args: "name" , args: RubifyConstant(name: message->name())); | 
| 381 |   printer->Print( | 
| 382 |     text: "::Google::Protobuf::DescriptorPool.generated_pool."  | 
| 383 |     "lookup(\"$full_name$\").msgclass\n" , | 
| 384 |     args: "full_name" , args: message->full_name()); | 
| 385 |  | 
| 386 |   std::string nested_prefix = prefix + RubifyConstant(name: message->name()) + "::" ; | 
| 387 |   for (int i = 0; i < message->nested_type_count(); i++) { | 
| 388 |     GenerateMessageAssignment(prefix: nested_prefix, message: message->nested_type(index: i), printer); | 
| 389 |   } | 
| 390 |   for (int i = 0; i < message->enum_type_count(); i++) { | 
| 391 |     GenerateEnumAssignment(prefix: nested_prefix, en: message->enum_type(index: i), printer); | 
| 392 |   } | 
| 393 | } | 
| 394 |  | 
| 395 | void GenerateEnumAssignment(const std::string& prefix, const EnumDescriptor* en, | 
| 396 |                             io::Printer* printer) { | 
| 397 |   printer->Print( | 
| 398 |     text: "$prefix$$name$ = " , | 
| 399 |     args: "prefix" , args: prefix, | 
| 400 |     args: "name" , args: RubifyConstant(name: en->name())); | 
| 401 |   printer->Print( | 
| 402 |     text: "::Google::Protobuf::DescriptorPool.generated_pool."  | 
| 403 |     "lookup(\"$full_name$\").enummodule\n" , | 
| 404 |     args: "full_name" , args: en->full_name()); | 
| 405 | } | 
| 406 |  | 
| 407 | int GeneratePackageModules(const FileDescriptor* file, io::Printer* printer) { | 
| 408 |   int levels = 0; | 
| 409 |   bool need_change_to_module = true; | 
| 410 |   std::string package_name; | 
| 411 |  | 
| 412 |   // Determine the name to use in either format: | 
| 413 |   //   proto package:         one.two.three | 
| 414 |   //   option ruby_package:   One::Two::Three | 
| 415 |   if (file->options().has_ruby_package()) { | 
| 416 |     package_name = file->options().ruby_package(); | 
| 417 |  | 
| 418 |     // If :: is in the package use the Ruby formatted name as-is | 
| 419 |     //    -> A::B::C | 
| 420 |     // otherwise, use the dot separator | 
| 421 |     //    -> A.B.C | 
| 422 |     if (package_name.find(s: "::" ) != std::string::npos) { | 
| 423 |       need_change_to_module = false; | 
| 424 |     } else if (package_name.find(s: "." ) != std::string::npos) { | 
| 425 |       GOOGLE_LOG(WARNING) << "ruby_package option should be in the form of:"  | 
| 426 |                           << " 'A::B::C' and not 'A.B.C'" ; | 
| 427 |     } | 
| 428 |   } else { | 
| 429 |     package_name = file->package(); | 
| 430 |   } | 
| 431 |  | 
| 432 |   // Use the appropriate delimiter | 
| 433 |   std::string delimiter = need_change_to_module ? "."  : "::" ; | 
| 434 |   int delimiter_size = need_change_to_module ? 1 : 2; | 
| 435 |  | 
| 436 |   // Extract each module name and indent | 
| 437 |   while (!package_name.empty()) { | 
| 438 |     size_t dot_index = package_name.find(str: delimiter); | 
| 439 |     std::string component; | 
| 440 |     if (dot_index == std::string::npos) { | 
| 441 |       component = package_name; | 
| 442 |       package_name = "" ; | 
| 443 |     } else { | 
| 444 |       component = package_name.substr(pos: 0, n: dot_index); | 
| 445 |       package_name = package_name.substr(pos: dot_index + delimiter_size); | 
| 446 |     } | 
| 447 |     if (need_change_to_module) { | 
| 448 |       component = PackageToModule(name: component); | 
| 449 |     } | 
| 450 |     printer->Print( | 
| 451 |       text: "module $name$\n" , | 
| 452 |       args: "name" , args: component); | 
| 453 |     printer->Indent(); | 
| 454 |     levels++; | 
| 455 |   } | 
| 456 |   return levels; | 
| 457 | } | 
| 458 |  | 
| 459 | void EndPackageModules(int levels, io::Printer* printer) { | 
| 460 |   while (levels > 0) { | 
| 461 |     levels--; | 
| 462 |     printer->Outdent(); | 
| 463 |     printer->Print( | 
| 464 |       text: "end\n" ); | 
| 465 |   } | 
| 466 | } | 
| 467 |  | 
| 468 | bool GenerateDslDescriptor(const FileDescriptor* file, io::Printer* printer, | 
| 469 |                            std::string* error) { | 
| 470 |   printer->Print(text: "Google::Protobuf::DescriptorPool.generated_pool.build do\n" ); | 
| 471 |   printer->Indent(); | 
| 472 |   printer->Print(text: "add_file(\"$filename$\", :syntax => :$syntax$) do\n" , | 
| 473 |                  args: "filename" , args: file->name(), args: "syntax" , | 
| 474 |                  args: StringifySyntax(syntax: file->syntax())); | 
| 475 |   printer->Indent(); | 
| 476 |   for (int i = 0; i < file->message_type_count(); i++) { | 
| 477 |     if (!GenerateMessage(message: file->message_type(index: i), printer, error)) { | 
| 478 |       return false; | 
| 479 |     } | 
| 480 |   } | 
| 481 |   for (int i = 0; i < file->enum_type_count(); i++) { | 
| 482 |     GenerateEnum(en: file->enum_type(index: i), printer); | 
| 483 |   } | 
| 484 |   printer->Outdent(); | 
| 485 |   printer->Print(text: "end\n" ); | 
| 486 |   printer->Outdent(); | 
| 487 |   printer->Print( | 
| 488 |     text: "end\n\n" ); | 
| 489 |   return true; | 
| 490 | } | 
| 491 |  | 
| 492 | bool GenerateBinaryDescriptor(const FileDescriptor* file, io::Printer* printer, | 
| 493 |                               std::string* error) { | 
| 494 |   printer->Print( | 
| 495 |       text: R"(descriptor_data = File.binread(__FILE__).split("\n__END__\n", 2)[1])" ); | 
| 496 |   printer->Print( | 
| 497 |       text: "\nGoogle::Protobuf::DescriptorPool.generated_pool.add_serialized_file("  | 
| 498 |       "descriptor_data)\n\n" ); | 
| 499 |   return true; | 
| 500 | } | 
| 501 |  | 
| 502 | bool GenerateFile(const FileDescriptor* file, io::Printer* printer, | 
| 503 |                   std::string* error) { | 
| 504 |   printer->Print( | 
| 505 |     text: "# Generated by the protocol buffer compiler.  DO NOT EDIT!\n"  | 
| 506 |     "# source: $filename$\n"  | 
| 507 |     "\n" , | 
| 508 |     args: "filename" , args: file->name()); | 
| 509 |  | 
| 510 |   printer->Print(text: "require 'google/protobuf'\n\n" ); | 
| 511 |  | 
| 512 |   if (file->dependency_count() != 0) { | 
| 513 |     for (int i = 0; i < file->dependency_count(); i++) { | 
| 514 |       printer->Print(text: "require '$name$'\n" , args: "name" , args: GetRequireName(proto_file: file->dependency(index: i)->name())); | 
| 515 |     } | 
| 516 |     printer->Print(text: "\n" ); | 
| 517 |   } | 
| 518 |  | 
| 519 |   // TODO: Remove this when ruby supports extensions for proto2 syntax. | 
| 520 |   if (file->syntax() == FileDescriptor::SYNTAX_PROTO2 && | 
| 521 |       file->extension_count() > 0) { | 
| 522 |     GOOGLE_LOG(WARNING) << "Extensions are not yet supported for proto2 .proto files." ; | 
| 523 |   } | 
| 524 |  | 
| 525 |   bool use_raw_descriptor = file->name() == "google/protobuf/descriptor.proto" ; | 
| 526 |  | 
| 527 |   if (use_raw_descriptor) { | 
| 528 |     GenerateBinaryDescriptor(file, printer, error); | 
| 529 |   } else { | 
| 530 |     GenerateDslDescriptor(file, printer, error); | 
| 531 |   } | 
| 532 |  | 
| 533 |   int levels = GeneratePackageModules(file, printer); | 
| 534 |   for (int i = 0; i < file->message_type_count(); i++) { | 
| 535 |     GenerateMessageAssignment(prefix: "" , message: file->message_type(index: i), printer); | 
| 536 |   } | 
| 537 |   for (int i = 0; i < file->enum_type_count(); i++) { | 
| 538 |     GenerateEnumAssignment(prefix: "" , en: file->enum_type(index: i), printer); | 
| 539 |   } | 
| 540 |   EndPackageModules(levels, printer); | 
| 541 |  | 
| 542 |   if (use_raw_descriptor) { | 
| 543 |     printer->Print(text: "\n__END__\n" ); | 
| 544 |     FileDescriptorProto file_proto; | 
| 545 |     file->CopyTo(proto: &file_proto); | 
| 546 |     std::string file_data; | 
| 547 |     file_proto.SerializeToString(output: &file_data); | 
| 548 |     printer->Print(text: "$raw_descriptor$" , args: "raw_descriptor" , args: file_data); | 
| 549 |   } | 
| 550 |   return true; | 
| 551 | } | 
| 552 |  | 
| 553 | bool Generator::Generate( | 
| 554 |     const FileDescriptor* file, | 
| 555 |     const std::string& parameter, | 
| 556 |     GeneratorContext* generator_context, | 
| 557 |     std::string* error) const { | 
| 558 |  | 
| 559 |   if (file->syntax() != FileDescriptor::SYNTAX_PROTO3 && | 
| 560 |       file->syntax() != FileDescriptor::SYNTAX_PROTO2) { | 
| 561 |     *error = "Invalid or unsupported proto syntax" ; | 
| 562 |     return false; | 
| 563 |   } | 
| 564 |  | 
| 565 |   std::unique_ptr<io::ZeroCopyOutputStream> output( | 
| 566 |       generator_context->Open(filename: GetOutputFilename(proto_file: file->name()))); | 
| 567 |   io::Printer printer(output.get(), '$'); | 
| 568 |  | 
| 569 |   return GenerateFile(file, printer: &printer, error); | 
| 570 | } | 
| 571 |  | 
| 572 | }  // namespace ruby | 
| 573 | }  // namespace compiler | 
| 574 | }  // namespace protobuf | 
| 575 | }  // namespace google | 
| 576 |  |