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 | |