1 /*
2 * Copyright (c) Facebook, Inc. and its affiliates.
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 #include <cassert>
18 #include <cctype>
19 #include <set>
20 #include <string>
21 #include <unordered_set>
22
23 #include <boost/algorithm/string/predicate.hpp>
24 #include <boost/algorithm/string/replace.hpp>
25
26 #include <thrift/compiler/ast/t_struct.h>
27 #include <thrift/compiler/generate/t_mstch_generator.h>
28 #include <thrift/compiler/generate/t_mstch_objects.h>
29
30 using namespace std;
31
32 namespace apache {
33 namespace thrift {
34 namespace compiler {
35
36 namespace {
mangle(const std::string & name)37 std::string mangle(const std::string& name) {
38 static const char* raw_identifiable_keywords[] = {
39 "abstract", "alignof", "as", "async", "await", "become",
40 "box", "break", "const", "continue", "do", "else",
41 "enum", "extern", "false", "final", "fn", "for",
42 "if", "impl", "in", "let", "loop", "macro",
43 "match", "mod", "move", "mut", "offsetof", "override",
44 "priv", "proc", "pub", "pure", "ref", "return",
45 "sizeof", "static", "struct", "trait", "true", "type",
46 "typeof", "unsafe", "unsized", "use", "virtual", "where",
47 "while", "yield",
48 };
49
50 static const char* keywords_that_participate_in_name_resolution[] = {
51 "crate",
52 "super",
53 "self",
54 "Self",
55 };
56
57 constexpr const char* keyword_error_message = R"ERROR(
58 Found a rust keyword that participates in name resolution.
59 Please use the `rust.name` annotation to create an alias for)ERROR";
60
61 for (auto& s : keywords_that_participate_in_name_resolution) {
62 if (name == s) {
63 std::ostringstream error_message;
64 error_message << keyword_error_message << " " << name;
65 throw std::runtime_error(error_message.str());
66 }
67 }
68
69 for (auto& s : raw_identifiable_keywords) {
70 if (name == s) {
71 return "r#" + name;
72 }
73 }
74
75 return name;
76 }
77
mangle_type(const std::string & name)78 std::string mangle_type(const std::string& name) {
79 static const char* primitives[] = {
80 "i8",
81 "u8",
82 "i16",
83 "u16",
84 "i32",
85 "u32",
86 "i64",
87 "u64",
88 "i128",
89 "u128",
90 "f32",
91 "f64",
92 "isize",
93 "usize",
94 "str",
95 "bool",
96 };
97
98 for (auto s : primitives) {
99 if (name == s) {
100 return name + '_';
101 }
102 }
103
104 return mangle(name);
105 }
106
107 // Convert CamelCase to snake_case.
snakecase(const std::string & name)108 std::string snakecase(const std::string& name) {
109 std::ostringstream snake;
110
111 char last = '_';
112 for (auto ch : name) {
113 if (isupper(ch)) {
114 if (last != '_') {
115 // Don't insert '_' after an existing one, such as in `Sample_CalcRs`.
116 // Also don't put a '_' right at the front.
117 snake << '_';
118 }
119 last = (char)tolower(ch);
120 } else {
121 last = ch;
122 }
123 snake << last;
124 }
125
126 return snake.str();
127 }
128
129 // Convert snake_case to UpperCamelCase.
camelcase(const std::string & name)130 std::string camelcase(const std::string& name) {
131 std::ostringstream camel;
132
133 size_t i = 0;
134 for (; i < name.size() && name[i] == '_'; i++) {
135 // Copy same number of leading underscores.
136 camel << '_';
137 }
138
139 auto underscore = true;
140 for (; i < name.size(); i++) {
141 if (name[i] == '_') {
142 underscore = true;
143 } else if (underscore) {
144 camel << (char)toupper(name[i]);
145 underscore = false;
146 } else {
147 camel << name[i];
148 }
149 }
150
151 return camel.str();
152 }
153
quote(const std::string & data)154 std::string quote(const std::string& data) {
155 std::ostringstream quoted;
156 quoted << '"';
157
158 for (auto ch : data) {
159 if (ch == '\t') {
160 quoted << '\\' << 't';
161 } else if (ch == '\r') {
162 quoted << '\\' << 'r';
163 } else if (ch == '\n') {
164 quoted << '\\' << 'n';
165 } else if (ch == '\\' || ch == '"') {
166 quoted << '\\' << ch;
167 } else if (ch < '\x7f') {
168 quoted << ch;
169 } else {
170 throw std::runtime_error("Non-ASCII string literal not implemented");
171 }
172 }
173
174 quoted << '"';
175 return quoted.str();
176 }
177
quoted_rust_doc(const t_node * node)178 std::string quoted_rust_doc(const t_node* node) {
179 const std::string doc = node->get_doc();
180
181 // strip leading/trailing whitespace
182 static const std::string whitespace = "\n\r\t ";
183 const auto first = doc.find_first_not_of(whitespace);
184 if (first == std::string::npos) {
185 // empty string
186 return "\"\"";
187 }
188
189 const auto last = doc.find_last_not_of(whitespace);
190 return quote(doc.substr(first, last - first + 1));
191 }
192
can_derive_ord(const t_type * type)193 bool can_derive_ord(const t_type* type) {
194 type = type->get_true_type();
195 if (type->is_string() || type->is_binary() || type->is_bool() ||
196 type->is_byte() || type->is_i16() || type->is_i32() || type->is_i64() ||
197 type->is_enum() || type->is_void()) {
198 return true;
199 }
200 if (type->has_annotation("rust.ord")) {
201 return true;
202 }
203 if (type->is_list()) {
204 auto elem_type = dynamic_cast<const t_list*>(type)->get_elem_type();
205 return elem_type && can_derive_ord(elem_type);
206 }
207 return false;
208 }
209
210 struct rust_codegen_options {
211 // Key: package name according to Thrift.
212 // Value: rust_crate_name to use in generated code.
213 std::map<std::string, std::string> cratemap;
214
215 // Whether to emit derive(Serialize, Deserialize).
216 // Enabled by `--gen rust:serde`.
217 bool serde = false;
218
219 // Whether fields w/optional values of None should
220 // be skipped during serialization. Enabled w/ `--gen
221 // rust:skip_none_serialization` Note: `rust:serde` must also be set for this
222 // to affect codegen.
223 bool skip_none_serialization = false;
224
225 // Whether to skip server stubs. Server stubs are built by default, but can
226 // be turned off via `--gen rust:noserver`.
227 bool noserver = false;
228
229 // True if we are generating a submodule rather than the whole crate.
230 bool multifile_mode = false;
231
232 // List of extra sources to include at top-level of the crate.
233 std::vector<std::string> include_srcs;
234
235 // The current program being generated and its Rust module path.
236 const t_program* current_program;
237 std::string current_crate;
238 };
239
get_import_name(const t_program * program,const rust_codegen_options & options)240 std::string get_import_name(
241 const t_program* program, const rust_codegen_options& options) {
242 if (program == options.current_program) {
243 return options.current_crate;
244 }
245
246 auto program_name = program->name();
247 auto crate_name = options.cratemap.find(program_name);
248 if (crate_name != options.cratemap.end()) {
249 return crate_name->second;
250 }
251 return program_name;
252 }
253
254 enum class FieldKind { Box, Arc, Inline };
255
field_kind(const t_named & node)256 FieldKind field_kind(const t_named& node) {
257 if (node.has_annotation("rust.arc")) {
258 return FieldKind::Arc;
259 }
260 if (node.has_annotation("rust.box")) {
261 return FieldKind::Box;
262 }
263 return FieldKind::Inline;
264 }
265
266 // For example `set<Value> (rust.type = "indexmap::IndexSet")` or `map<string,
267 // Value> (rust.type = "indexmap::IndexMap")`. Unlike for standard library
268 // collections, serialization impls for these types are not provided in the
269 // fbthrift Rust runtime library and instead that logic will need to be emitted
270 // into the generated crate.
has_nonstandard_type_annotation(const t_type * type)271 bool has_nonstandard_type_annotation(const t_type* type) {
272 return type->get_annotation("rust.type").find("::") != string::npos;
273 }
274 } // namespace
275
276 class t_mstch_rust_generator : public t_mstch_generator {
277 public:
t_mstch_rust_generator(t_program * program,t_generation_context context,const std::map<std::string,std::string> & parsed_options,const std::string &)278 t_mstch_rust_generator(
279 t_program* program,
280 t_generation_context context,
281 const std::map<std::string, std::string>& parsed_options,
282 const std::string& /* option_string */)
283 : t_mstch_generator(program, std::move(context), "rust", parsed_options) {
284 auto cratemap_flag = parsed_options.find("cratemap");
285 if (cratemap_flag != parsed_options.end()) {
286 load_crate_map(cratemap_flag->second);
287 }
288
289 options_.serde = parsed_options.count("serde");
290 options_.noserver = parsed_options.count("noserver");
291 options_.skip_none_serialization =
292 parsed_options.count("skip_none_serialization");
293 if (options_.skip_none_serialization) {
294 assert(options_.serde);
295 }
296
297 auto include_prefix_flag = parsed_options.find("include_prefix");
298 if (include_prefix_flag != parsed_options.end()) {
299 program->set_include_prefix(include_prefix_flag->second);
300 }
301
302 auto include_srcs = parsed_options.find("include_srcs");
303 if (include_srcs != parsed_options.end()) {
304 auto paths = include_srcs->second;
305
306 string::size_type pos = 0;
307 while (pos != string::npos && pos < paths.size()) {
308 string::size_type next_pos = paths.find(':', pos);
309 auto path = paths.substr(pos, next_pos - pos);
310 options_.include_srcs.push_back(path);
311 pos = ((next_pos == string::npos) ? next_pos : next_pos + 1);
312 }
313 }
314
315 if (options_.multifile_mode) {
316 options_.current_crate = "crate::" + mangle(program->name());
317 } else {
318 options_.current_crate = "crate";
319 }
320
321 options_.current_program = program;
322 out_dir_base_ = "gen-rust";
323 }
324
325 void generate_program() override;
326 void fill_validator_list(validator_list&) const override;
327
328 private:
329 void set_mstch_generators();
330 void load_crate_map(const std::string& path);
331 rust_codegen_options options_;
332 };
333
334 class mstch_rust_program : public mstch_program {
335 public:
mstch_rust_program(t_program const * program,std::shared_ptr<mstch_generators const> generators,std::shared_ptr<mstch_cache> cache,ELEMENT_POSITION const pos,const rust_codegen_options & options)336 mstch_rust_program(
337 t_program const* program,
338 std::shared_ptr<mstch_generators const> generators,
339 std::shared_ptr<mstch_cache> cache,
340 ELEMENT_POSITION const pos,
341 const rust_codegen_options& options)
342 : mstch_program(program, generators, cache, pos), options_(options) {
343 register_methods(
344 this,
345 {
346 {"program:types?", &mstch_rust_program::rust_has_types},
347 {"program:structsOrEnums?",
348 &mstch_rust_program::rust_structs_or_enums},
349 {"program:nonexhaustiveStructs?",
350 &mstch_rust_program::rust_nonexhaustive_structs},
351 {"program:serde?", &mstch_rust_program::rust_serde},
352 {"program:skip_none_serialization?",
353 &mstch_rust_program::rust_skip_none_serialization},
354 {"program:server?", &mstch_rust_program::rust_server},
355 {"program:multifile?", &mstch_rust_program::rust_multifile},
356 {"program:crate", &mstch_rust_program::rust_crate},
357 {"program:package", &mstch_rust_program::rust_package},
358 {"program:includes", &mstch_rust_program::rust_includes},
359 {"program:anyServiceWithoutParent?",
360 &mstch_rust_program::rust_any_service_without_parent},
361 {"program:nonstandardTypes?",
362 &mstch_rust_program::rust_has_nonstandard_types},
363 {"program:nonstandardTypes",
364 &mstch_rust_program::rust_nonstandard_types},
365 {"program:docs?", &mstch_rust_program::rust_has_docs},
366 {"program:docs", &mstch_rust_program::rust_docs},
367 {"program:include_srcs", &mstch_rust_program::rust_include_srcs},
368 });
369 }
rust_has_types()370 mstch::node rust_has_types() {
371 return !program_->structs().empty() || !program_->enums().empty() ||
372 !program_->typedefs().empty() || !program_->xceptions().empty();
373 }
rust_structs_or_enums()374 mstch::node rust_structs_or_enums() {
375 return !program_->structs().empty() || !program_->enums().empty() ||
376 !program_->xceptions().empty();
377 }
rust_nonexhaustive_structs()378 mstch::node rust_nonexhaustive_structs() {
379 for (auto& strct : program_->structs()) {
380 // The is_union is because `union` are also in this collection.
381 if (!strct->is_union() && !strct->has_annotation("rust.exhaustive")) {
382 return true;
383 }
384 }
385 for (auto& strct : program_->xceptions()) {
386 if (!strct->has_annotation("rust.exhaustive")) {
387 return true;
388 }
389 }
390 return false;
391 }
rust_serde()392 mstch::node rust_serde() { return options_.serde; }
rust_skip_none_serialization()393 mstch::node rust_skip_none_serialization() {
394 return options_.skip_none_serialization;
395 }
rust_server()396 mstch::node rust_server() { return !options_.noserver; }
rust_multifile()397 mstch::node rust_multifile() { return options_.multifile_mode; }
rust_crate()398 mstch::node rust_crate() {
399 if (options_.multifile_mode) {
400 return "crate::" + mangle(program_->name());
401 }
402 return std::string("crate");
403 }
rust_package()404 mstch::node rust_package() { return get_import_name(program_, options_); }
rust_includes()405 mstch::node rust_includes() {
406 mstch::array includes;
407 for (auto* program : program_->get_included_programs()) {
408 includes.push_back(generators_->program_generator_->generate(
409 program, generators_, cache_, pos_));
410 }
411 return includes;
412 }
rust_any_service_without_parent()413 mstch::node rust_any_service_without_parent() {
414 for (const t_service* service : program_->services()) {
415 if (service->get_extends() == nullptr) {
416 return true;
417 }
418 }
419 return false;
420 }
421 template <typename F>
foreach_type(F && f) const422 void foreach_type(F&& f) const {
423 for (const auto* strct : program_->structs()) {
424 for (const auto& field : strct->fields()) {
425 f(field.get_type());
426 }
427 }
428 for (const auto* service : program_->services()) {
429 for (const auto& function : service->functions()) {
430 for (const auto& param : function.get_paramlist()->fields()) {
431 f(param.get_type());
432 }
433 f(function.get_returntype());
434 }
435 }
436 for (auto typedf : program_->typedefs()) {
437 f(typedf);
438 }
439 }
rust_has_nonstandard_types()440 mstch::node rust_has_nonstandard_types() {
441 bool has_nonstandard_types = false;
442 foreach_type([&](const t_type* type) {
443 if (has_nonstandard_type_annotation(type)) {
444 has_nonstandard_types = true;
445 }
446 });
447 return has_nonstandard_types;
448 }
rust_nonstandard_types()449 mstch::node rust_nonstandard_types() {
450 // Sort/deduplicate by value of `rust.type` annotation.
451 struct rust_type_less {
452 bool operator()(const t_type* lhs, const t_type* rhs) const {
453 auto& lhs_annotation = lhs->get_annotation("rust.type");
454 auto& rhs_annotation = rhs->get_annotation("rust.type");
455 if (lhs_annotation != rhs_annotation) {
456 return lhs_annotation < rhs_annotation;
457 }
458 return lhs->get_full_name() < rhs->get_full_name();
459 }
460 };
461 std::set<const t_type*, rust_type_less> nonstandard_types;
462 foreach_type([&](const t_type* type) {
463 if (has_nonstandard_type_annotation(type)) {
464 nonstandard_types.insert(type);
465 }
466 });
467 std::vector<const t_type*> elements(
468 nonstandard_types.begin(), nonstandard_types.end());
469 return generate_types(elements);
470 }
rust_has_docs()471 mstch::node rust_has_docs() { return program_->has_doc(); }
rust_docs()472 mstch::node rust_docs() { return quoted_rust_doc(program_); }
rust_include_srcs()473 mstch::node rust_include_srcs() {
474 mstch::array elements;
475 for (auto elem : options_.include_srcs) {
476 mstch::map node;
477 node["program:include_src"] = elem;
478 elements.push_back(node);
479 }
480 return elements;
481 }
482
483 private:
484 const rust_codegen_options& options_;
485 };
486
487 class mstch_rust_struct : public mstch_struct {
488 public:
mstch_rust_struct(const t_struct * strct,std::shared_ptr<mstch_generators const> generators,std::shared_ptr<mstch_cache> cache,ELEMENT_POSITION const pos,const rust_codegen_options & options)489 mstch_rust_struct(
490 const t_struct* strct,
491 std::shared_ptr<mstch_generators const> generators,
492 std::shared_ptr<mstch_cache> cache,
493 ELEMENT_POSITION const pos,
494 const rust_codegen_options& options)
495 : mstch_struct(strct, generators, cache, pos), options_(options) {
496 register_methods(
497 this,
498 {
499 {"struct:rust_name", &mstch_rust_struct::rust_name},
500 {"struct:package", &mstch_rust_struct::rust_package},
501 {"struct:ord?", &mstch_rust_struct::rust_is_ord},
502 {"struct:copy?", &mstch_rust_struct::rust_is_copy},
503 {"struct:exhaustive?", &mstch_rust_struct::rust_is_exhaustive},
504 {"struct:fields_by_name", &mstch_rust_struct::rust_fields_by_name},
505 {"struct:docs?", &mstch_rust_struct::rust_has_doc},
506 {"struct:docs", &mstch_rust_struct::rust_doc},
507 {"struct:derive", &mstch_rust_struct::rust_derive},
508 {"struct:has_exception_message?",
509 &mstch_rust_struct::has_exception_message},
510 {"struct:is_exception_message_optional?",
511 &mstch_rust_struct::is_exception_message_optional},
512 {"struct:exception_message", &mstch_rust_struct::exception_message},
513 });
514 }
rust_name()515 mstch::node rust_name() {
516 if (!strct_->has_annotation("rust.name")) {
517 return mangle_type(strct_->get_name());
518 }
519 return strct_->get_annotation("rust.name");
520 }
rust_package()521 mstch::node rust_package() {
522 return get_import_name(strct_->program(), options_);
523 }
rust_is_ord()524 mstch::node rust_is_ord() {
525 if (strct_->has_annotation("rust.ord")) {
526 return true;
527 }
528 for (const auto& field : strct_->fields()) {
529 if (!can_derive_ord(field.get_type())) {
530 return false;
531 }
532 }
533 return true;
534 }
rust_is_copy()535 mstch::node rust_is_copy() { return strct_->has_annotation("rust.copy"); }
rust_is_exhaustive()536 mstch::node rust_is_exhaustive() {
537 return strct_->has_annotation("rust.exhaustive");
538 }
rust_fields_by_name()539 mstch::node rust_fields_by_name() {
540 auto fields = strct_->fields().copy();
541 std::sort(fields.begin(), fields.end(), [](auto a, auto b) {
542 return a->get_name() < b->get_name();
543 });
544 return generate_fields(fields);
545 }
rust_has_doc()546 mstch::node rust_has_doc() { return strct_->has_doc(); }
rust_doc()547 mstch::node rust_doc() { return quoted_rust_doc(strct_); }
rust_derive()548 mstch::node rust_derive() {
549 if (!strct_->has_annotation("rust.derive")) {
550 return nullptr;
551 }
552 return strct_->get_annotation("rust.derive");
553 }
has_exception_message()554 mstch::node has_exception_message() {
555 return strct_->has_annotation("message");
556 }
is_exception_message_optional()557 mstch::node is_exception_message_optional() {
558 if (!strct_->has_annotation("message")) {
559 return nullptr;
560 }
561 return strct_->get_field_by_name(strct_->get_annotation("message"))
562 ->get_req() == t_field::e_req::optional;
563 }
exception_message()564 mstch::node exception_message() { return strct_->get_annotation("message"); }
565
566 private:
567 const rust_codegen_options& options_;
568 };
569
570 class mstch_rust_service : public mstch_service {
571 public:
mstch_rust_service(const t_service * service,std::shared_ptr<mstch_generators const> generators,std::shared_ptr<mstch_cache> cache,ELEMENT_POSITION const pos,const rust_codegen_options & options)572 mstch_rust_service(
573 const t_service* service,
574 std::shared_ptr<mstch_generators const> generators,
575 std::shared_ptr<mstch_cache> cache,
576 ELEMENT_POSITION const pos,
577 const rust_codegen_options& options)
578 : mstch_service(service, generators, cache, pos), options_(options) {
579 for (auto function : service->get_functions()) {
580 function_upcamel_names_.insert(camelcase(function->get_name()));
581 }
582 register_methods(
583 this,
584 {
585 {"service:rustFunctions", &mstch_rust_service::rust_functions},
586 {"service:rust_exceptions",
587 &mstch_rust_service::rust_all_exceptions},
588 {"service:package", &mstch_rust_service::rust_package},
589 {"service:snake", &mstch_rust_service::rust_snake},
590 {"service:requestContext?",
591 &mstch_rust_service::rust_request_context},
592 {"service:extendedServices",
593 &mstch_rust_service::rust_extended_services},
594 {"service:docs?", &mstch_rust_service::rust_has_doc},
595 {"service:docs", &mstch_rust_service::rust_doc},
596 });
597 }
598 mstch::node rust_functions();
rust_package()599 mstch::node rust_package() {
600 return get_import_name(service_->program(), options_);
601 }
rust_snake()602 mstch::node rust_snake() {
603 return service_->get_annotation(
604 "rust.mod", mangle_type(snakecase(service_->get_name())));
605 }
rust_request_context()606 mstch::node rust_request_context() {
607 return service_->has_annotation("rust.request_context");
608 }
rust_extended_services()609 mstch::node rust_extended_services() {
610 mstch::array extended_services;
611 const t_service* service = service_;
612 std::string type_prefix = get_import_name(service_->program(), options_);
613 std::string as_ref_impl = "&self.parent";
614 while (true) {
615 const t_service* parent_service = service->get_extends();
616 if (parent_service == nullptr) {
617 break;
618 }
619 if (parent_service->program() != service->program()) {
620 type_prefix += "::dependencies::" + parent_service->program()->name();
621 }
622 mstch::map node;
623 node["extendedService:packagePrefix"] = type_prefix;
624 node["extendedService:asRefImpl"] = as_ref_impl;
625 node["extendedService:service"] =
626 generate_cached_extended_service(parent_service);
627 extended_services.push_back(node);
628 as_ref_impl = "self.parent.as_ref()";
629 service = parent_service;
630 }
631 return extended_services;
632 }
633
634 mstch::node rust_all_exceptions();
rust_has_doc()635 mstch::node rust_has_doc() { return service_->has_doc(); }
rust_doc()636 mstch::node rust_doc() { return quoted_rust_doc(service_); }
637
638 private:
639 std::unordered_multiset<std::string> function_upcamel_names_;
640 const rust_codegen_options& options_;
641 };
642
643 class mstch_rust_function : public mstch_function {
644 public:
mstch_rust_function(const t_function * function,std::shared_ptr<mstch_generators const> generators,std::shared_ptr<mstch_cache> cache,ELEMENT_POSITION const pos,int32_t index,const std::unordered_multiset<std::string> & function_upcamel_names)645 mstch_rust_function(
646 const t_function* function,
647 std::shared_ptr<mstch_generators const> generators,
648 std::shared_ptr<mstch_cache> cache,
649 ELEMENT_POSITION const pos,
650 int32_t index,
651 const std::unordered_multiset<std::string>& function_upcamel_names)
652 : mstch_function(function, generators, cache, pos),
653 index_(index),
654 function_upcamel_names_(function_upcamel_names),
655 success_return(function->get_returntype(), "Success", 0) {
656 register_methods(
657 this,
658 {
659 {"function:rust_name", &mstch_rust_function::rust_name},
660 {"function:upcamel", &mstch_rust_function::rust_upcamel},
661 {"function:index", &mstch_rust_function::rust_index},
662 {"function:void?", &mstch_rust_function::rust_void},
663 {"function:uniqueExceptions",
664 &mstch_rust_function::rust_unique_exceptions},
665 {"function:uniqueStreamExceptions",
666 &mstch_rust_function::rust_unique_stream_exceptions},
667 {"function:args_by_name", &mstch_rust_function::rust_args_by_name},
668 {"function:returns_by_name",
669 &mstch_rust_function::rust_returns_by_name},
670 {"function:docs?", &mstch_rust_function::rust_has_doc},
671 {"function:docs", &mstch_rust_function::rust_doc},
672 });
673 }
rust_name()674 mstch::node rust_name() {
675 if (!function_->has_annotation("rust.name")) {
676 return mangle(function_->get_name());
677 }
678 return function_->get_annotation("rust.name");
679 }
rust_upcamel()680 mstch::node rust_upcamel() {
681 auto upcamel_name = camelcase(function_->get_name());
682 if (function_upcamel_names_.count(upcamel_name) > 1) {
683 // If a service contains a pair of methods that collide converted to
684 // CamelCase, like a service containing both create_shard and createShard,
685 // then we name the exception types without any case conversion; instead
686 // of a CreateShardExn they'll get create_shardExn and createShardExn.
687 return function_->get_name();
688 }
689 return upcamel_name;
690 }
rust_index()691 mstch::node rust_index() { return index_; }
rust_void()692 mstch::node rust_void() { return function_->get_returntype()->is_void(); }
rust_unique_exceptions()693 mstch::node rust_unique_exceptions() {
694 return rust_make_unique_exceptions(function_->get_xceptions());
695 }
rust_unique_stream_exceptions()696 mstch::node rust_unique_stream_exceptions() {
697 return rust_make_unique_exceptions(function_->get_stream_xceptions());
698 }
rust_make_unique_exceptions(const t_struct * a)699 mstch::node rust_make_unique_exceptions(const t_struct* a) {
700 // When generating From<> impls for an error type, we must not generate one
701 // where more than one variant contains the same type of exception. Find
702 // only those exceptions that map uniquely to a variant.
703
704 const auto& exceptions = a->fields();
705 std::map<const t_type*, unsigned> type_count;
706 for (const auto& x : exceptions) {
707 type_count[x.get_type()] += 1;
708 }
709
710 std::vector<const t_field*> unique_exceptions;
711 for (const auto& x : exceptions) {
712 if (type_count.at(x.get_type()) == 1) {
713 unique_exceptions.emplace_back(&x);
714 }
715 }
716
717 return generate_fields(unique_exceptions);
718 }
rust_args_by_name()719 mstch::node rust_args_by_name() {
720 auto params = function_->get_paramlist()->fields().copy();
721 std::sort(params.begin(), params.end(), [](auto a, auto b) {
722 return a->get_name() < b->get_name();
723 });
724 return generate_fields(params);
725 }
rust_returns_by_name()726 mstch::node rust_returns_by_name() {
727 auto returns = function_->get_xceptions()->fields().copy();
728 returns.push_back(&success_return);
729 std::sort(returns.begin(), returns.end(), [](auto a, auto b) {
730 return a->get_name() < b->get_name();
731 });
732 return generate_fields(returns);
733 }
rust_has_doc()734 mstch::node rust_has_doc() { return function_->has_doc(); }
rust_doc()735 mstch::node rust_doc() { return quoted_rust_doc(function_); }
736
737 private:
738 int32_t index_;
739 const std::unordered_multiset<std::string>& function_upcamel_names_;
740 t_field success_return;
741 };
742
743 class mstch_rust_enum_value : public mstch_enum_value {
744 public:
mstch_rust_enum_value(const t_enum_value * enm_value,std::shared_ptr<mstch_generators const> generators,std::shared_ptr<mstch_cache> cache,ELEMENT_POSITION const pos)745 mstch_rust_enum_value(
746 const t_enum_value* enm_value,
747 std::shared_ptr<mstch_generators const> generators,
748 std::shared_ptr<mstch_cache> cache,
749 ELEMENT_POSITION const pos)
750 : mstch_enum_value(enm_value, generators, cache, pos) {
751 register_methods(
752 this,
753 {
754 {"enum_value:rust_name", &mstch_rust_enum_value::rust_name},
755 {"enum_value:docs?", &mstch_rust_enum_value::rust_has_doc},
756 {"enum_value:docs", &mstch_rust_enum_value::rust_doc},
757 });
758 }
rust_name()759 mstch::node rust_name() {
760 if (!enm_value_->has_annotation("rust.name")) {
761 return mangle(enm_value_->get_name());
762 }
763 return enm_value_->get_annotation("rust.name");
764 }
rust_has_doc()765 mstch::node rust_has_doc() { return enm_value_->has_doc(); }
rust_doc()766 mstch::node rust_doc() { return quoted_rust_doc(enm_value_); }
767 };
768
769 class mstch_rust_enum : public mstch_enum {
770 public:
mstch_rust_enum(const t_enum * enm,std::shared_ptr<mstch_generators const> generators,std::shared_ptr<mstch_cache> cache,ELEMENT_POSITION const pos,const rust_codegen_options & options)771 mstch_rust_enum(
772 const t_enum* enm,
773 std::shared_ptr<mstch_generators const> generators,
774 std::shared_ptr<mstch_cache> cache,
775 ELEMENT_POSITION const pos,
776 const rust_codegen_options& options)
777 : mstch_enum(enm, generators, cache, pos), options_(options) {
778 register_methods(
779 this,
780 {
781 {"enum:rust_name", &mstch_rust_enum::rust_name},
782 {"enum:package", &mstch_rust_enum::rust_package},
783 {"enum:variants_by_name", &mstch_rust_enum::variants_by_name},
784 {"enum:variants_by_number", &mstch_rust_enum::variants_by_number},
785 {"enum:docs?", &mstch_rust_enum::rust_has_doc},
786 {"enum:docs", &mstch_rust_enum::rust_doc},
787 });
788 }
rust_name()789 mstch::node rust_name() {
790 if (!enm_->has_annotation("rust.name")) {
791 return mangle_type(enm_->get_name());
792 }
793 return enm_->get_annotation("rust.name");
794 }
rust_package()795 mstch::node rust_package() {
796 return get_import_name(enm_->program(), options_);
797 }
variants_by_name()798 mstch::node variants_by_name() {
799 std::vector<t_enum_value*> variants = enm_->get_enum_values();
800 std::sort(variants.begin(), variants.end(), [](auto a, auto b) {
801 return a->get_name() < b->get_name();
802 });
803 return generate_enum_values(variants);
804 }
variants_by_number()805 mstch::node variants_by_number() {
806 std::vector<t_enum_value*> variants = enm_->get_enum_values();
807 std::sort(variants.begin(), variants.end(), [](auto a, auto b) {
808 return a->get_value() < b->get_value();
809 });
810 return generate_enum_values(variants);
811 }
rust_has_doc()812 mstch::node rust_has_doc() { return enm_->has_doc(); }
rust_doc()813 mstch::node rust_doc() { return quoted_rust_doc(enm_); }
814
815 private:
816 const rust_codegen_options& options_;
817 };
818
819 class mstch_rust_type : public mstch_type {
820 public:
mstch_rust_type(const t_type * type,std::shared_ptr<mstch_generators const> generators,std::shared_ptr<mstch_cache> cache,ELEMENT_POSITION const pos,const rust_codegen_options & options)821 mstch_rust_type(
822 const t_type* type,
823 std::shared_ptr<mstch_generators const> generators,
824 std::shared_ptr<mstch_cache> cache,
825 ELEMENT_POSITION const pos,
826 const rust_codegen_options& options)
827 : mstch_type(type, generators, cache, pos), options_(options) {
828 register_methods(
829 this,
830 {
831 {"type:rust_name", &mstch_rust_type::rust_name},
832 {"type:rust_name_snake", &mstch_rust_type::rust_name_snake},
833 {"type:package", &mstch_rust_type::rust_package},
834 {"type:rust", &mstch_rust_type::rust_type},
835 {"type:nonstandard?", &mstch_rust_type::rust_nonstandard},
836 });
837 }
rust_name()838 mstch::node rust_name() {
839 if (!type_->has_annotation("rust.name")) {
840 return mangle_type(type_->get_name());
841 }
842 return type_->get_annotation("rust.name");
843 }
rust_name_snake()844 mstch::node rust_name_snake() {
845 return snakecase(mangle_type(type_->get_name()));
846 }
rust_package()847 mstch::node rust_package() {
848 return get_import_name(type_->program(), options_);
849 }
rust_type()850 mstch::node rust_type() {
851 const std::string& rust_type = type_->get_annotation("rust.type");
852 if (!rust_type.empty() && rust_type.find("::") == std::string::npos) {
853 return "fbthrift::builtin_types::" + rust_type;
854 }
855 return rust_type;
856 }
rust_nonstandard()857 mstch::node rust_nonstandard() {
858 return has_nonstandard_type_annotation(type_) &&
859 !(type_->is_typedef() && type_->has_annotation("rust.newtype"));
860 }
861
862 private:
863 const rust_codegen_options& options_;
864 };
865
866 class mstch_rust_value : public mstch_base {
867 public:
868 using value_type = t_const_value::t_const_value_type;
mstch_rust_value(const t_const_value * const_value,const t_type * type,unsigned depth,std::shared_ptr<mstch_generators const> generators,std::shared_ptr<mstch_cache> cache,ELEMENT_POSITION pos,const rust_codegen_options & options)869 mstch_rust_value(
870 const t_const_value* const_value,
871 const t_type* type,
872 unsigned depth,
873 std::shared_ptr<mstch_generators const> generators,
874 std::shared_ptr<mstch_cache> cache,
875 ELEMENT_POSITION pos,
876 const rust_codegen_options& options)
877 : mstch_base(generators, cache, pos),
878 const_value_(const_value),
879 type_(type),
880 depth_(depth),
881 options_(options) {
882 // Step through any non-newtype typedefs.
883 while (type_->is_typedef() && !type_->has_annotation("rust.newtype")) {
884 auto typedef_type = dynamic_cast<const t_typedef*>(type_);
885 if (!typedef_type) {
886 break;
887 }
888 type_ = typedef_type->get_type();
889 }
890
891 register_methods(
892 this,
893 {
894 {"value:type", &mstch_rust_value::type},
895 {"value:newtype?", &mstch_rust_value::is_newtype},
896 {"value:inner", &mstch_rust_value::inner},
897 {"value:bool?", &mstch_rust_value::is_bool},
898 {"value:bool_value", &mstch_rust_value::bool_value},
899 {"value:integer?", &mstch_rust_value::is_integer},
900 {"value:integer_value", &mstch_rust_value::integer_value},
901 {"value:floatingPoint?", &mstch_rust_value::is_floating_point},
902 {"value:floatingPointValue",
903 &mstch_rust_value::floating_point_value},
904 {"value:string?", &mstch_rust_value::is_string},
905 {"value:binary?", &mstch_rust_value::is_binary},
906 {"value:quoted", &mstch_rust_value::string_quoted},
907 {"value:list?", &mstch_rust_value::is_list},
908 {"value:list_elements", &mstch_rust_value::list_elements},
909 {"value:set?", &mstch_rust_value::is_set},
910 {"value:setMembers", &mstch_rust_value::set_members},
911 {"value:map?", &mstch_rust_value::is_map},
912 {"value:mapEntries", &mstch_rust_value::map_entries},
913 {"value:struct?", &mstch_rust_value::is_struct},
914 {"value:structFields", &mstch_rust_value::struct_fields},
915 {"value:exhaustive?", &mstch_rust_value::is_exhaustive},
916 {"value:union?", &mstch_rust_value::is_union},
917 {"value:unionVariant", &mstch_rust_value::union_variant},
918 {"value:unionValue", &mstch_rust_value::union_value},
919 {"value:enum?", &mstch_rust_value::is_enum},
920 {"value:enumPackage", &mstch_rust_value::enum_package},
921 {"value:enumName", &mstch_rust_value::enum_name},
922 {"value:enumVariant", &mstch_rust_value::enum_variant},
923 {"value:empty?", &mstch_rust_value::is_empty},
924 {"value:indent", &mstch_rust_value::indent},
925 });
926 }
type()927 mstch::node type() {
928 return std::make_shared<mstch_rust_type>(
929 type_, generators_, cache_, pos_, options_);
930 }
is_newtype()931 mstch::node is_newtype() {
932 return type_->is_typedef() && type_->has_annotation("rust.newtype");
933 }
inner()934 mstch::node inner() {
935 auto typedef_type = dynamic_cast<const t_typedef*>(type_);
936 if (typedef_type) {
937 auto inner_type = typedef_type->get_type();
938 return std::make_shared<mstch_rust_value>(
939 const_value_,
940 inner_type,
941 depth_,
942 generators_,
943 cache_,
944 pos_,
945 options_);
946 }
947 return mstch::node();
948 }
is_bool()949 mstch::node is_bool() { return type_->is_bool(); }
bool_value()950 mstch::node bool_value() {
951 if (const_value_->get_type() == value_type::CV_INTEGER) {
952 return const_value_->get_integer() != 0;
953 }
954 return const_value_->get_bool();
955 }
is_integer()956 mstch::node is_integer() {
957 return type_->is_byte() || type_->is_i16() || type_->is_i32() ||
958 type_->is_i64();
959 }
integer_value()960 mstch::node integer_value() {
961 return std::to_string(const_value_->get_integer());
962 }
is_floating_point()963 mstch::node is_floating_point() {
964 return type_->is_float() || type_->is_double();
965 }
floating_point_value()966 mstch::node floating_point_value() {
967 std::ostringstream oss;
968 oss << std::setprecision(std::numeric_limits<double>::digits10);
969 oss << const_value_->get_double();
970 auto digits = oss.str();
971 if (digits.find('.') == std::string::npos &&
972 digits.find('e') == std::string::npos &&
973 digits.find('E') == std::string::npos) {
974 digits += ".0";
975 }
976 return digits;
977 }
is_string()978 mstch::node is_string() { return type_->is_string(); }
is_binary()979 mstch::node is_binary() { return type_->is_binary(); }
string_quoted()980 mstch::node string_quoted() { return quote(const_value_->get_string()); }
is_list()981 mstch::node is_list() {
982 return type_->is_list() &&
983 (const_value_->get_type() == value_type::CV_LIST ||
984 (const_value_->get_type() == value_type::CV_MAP &&
985 const_value_->get_map().empty()));
986 }
list_elements()987 mstch::node list_elements() {
988 const t_type* elem_type;
989 if (type_->is_set()) {
990 auto set_type = dynamic_cast<const t_set*>(type_);
991 if (!set_type) {
992 return mstch::node();
993 }
994 elem_type = set_type->get_elem_type();
995 } else {
996 auto list_type = dynamic_cast<const t_list*>(type_);
997 if (!list_type) {
998 return mstch::node();
999 }
1000 elem_type = list_type->get_elem_type();
1001 }
1002
1003 mstch::array elements;
1004 for (auto elem : const_value_->get_list()) {
1005 elements.push_back(std::make_shared<mstch_rust_value>(
1006 elem, elem_type, depth_ + 1, generators_, cache_, pos_, options_));
1007 }
1008 return elements;
1009 }
is_set()1010 mstch::node is_set() {
1011 return type_->is_set() &&
1012 (const_value_->get_type() == value_type::CV_LIST ||
1013 (const_value_->get_type() == value_type::CV_MAP &&
1014 const_value_->get_map().empty()));
1015 }
set_members()1016 mstch::node set_members() { return list_elements(); }
is_map()1017 mstch::node is_map() {
1018 return type_->is_map() &&
1019 (const_value_->get_type() == value_type::CV_MAP ||
1020 (const_value_->get_type() == value_type::CV_LIST &&
1021 const_value_->get_list().empty()));
1022 }
1023 mstch::node map_entries();
is_struct()1024 mstch::node is_struct() {
1025 return (type_->is_struct() || type_->is_xception()) && !type_->is_union() &&
1026 const_value_->get_type() == value_type::CV_MAP;
1027 }
1028 mstch::node struct_fields();
1029 mstch::node is_exhaustive();
is_union()1030 mstch::node is_union() {
1031 if (!type_->is_union() || const_value_->get_type() != value_type::CV_MAP) {
1032 return false;
1033 }
1034 if (const_value_->get_map().empty()) {
1035 // value will be the union's Default
1036 return true;
1037 }
1038 return const_value_->get_map().size() == 1 &&
1039 const_value_->get_map().at(0).first->get_type() ==
1040 value_type::CV_STRING;
1041 }
union_variant()1042 mstch::node union_variant() {
1043 if (const_value_->get_map().empty()) {
1044 return mstch::node();
1045 }
1046 return const_value_->get_map().at(0).first->get_string();
1047 }
union_value()1048 mstch::node union_value() {
1049 auto struct_type = dynamic_cast<const t_struct*>(type_);
1050 if (!struct_type) {
1051 return mstch::node();
1052 }
1053
1054 auto entry = const_value_->get_map().at(0);
1055 auto variant = entry.first->get_string();
1056 auto content = entry.second;
1057
1058 for (auto&& field : struct_type->fields()) {
1059 if (field.name() == variant) {
1060 return std::make_shared<mstch_rust_value>(
1061 content,
1062 field.get_type(),
1063 depth_ + 1,
1064 generators_,
1065 cache_,
1066 pos_,
1067 options_);
1068 }
1069 }
1070 return mstch::node();
1071 }
is_enum()1072 mstch::node is_enum() { return type_->is_enum(); }
enum_package()1073 mstch::node enum_package() {
1074 if (const_value_->is_enum()) {
1075 return get_import_name(const_value_->get_enum()->program(), options_);
1076 }
1077 return mstch::node();
1078 }
enum_name()1079 mstch::node enum_name() {
1080 if (const_value_->is_enum()) {
1081 return mangle_type(const_value_->get_enum()->get_name());
1082 }
1083 return mstch::node();
1084 }
enum_variant()1085 mstch::node enum_variant() {
1086 if (const_value_->is_enum()) {
1087 auto enum_value = const_value_->get_enum_value();
1088 if (enum_value) {
1089 return mangle(enum_value->get_name());
1090 }
1091 }
1092 return mstch::node();
1093 }
is_empty()1094 mstch::node is_empty() {
1095 auto type = const_value_->get_type();
1096 if (type == value_type::CV_LIST) {
1097 return const_value_->get_list().empty();
1098 }
1099 if (type == value_type::CV_MAP) {
1100 return const_value_->get_map().empty();
1101 }
1102 if (type == value_type::CV_STRING) {
1103 return const_value_->get_string().empty();
1104 }
1105 return false;
1106 }
indent()1107 mstch::node indent() { return std::string(4 * depth_, ' '); }
1108
1109 private:
1110 const t_const_value* const_value_;
1111 const t_type* type_;
1112 unsigned depth_;
1113 const rust_codegen_options& options_;
1114 };
1115
1116 class mstch_rust_map_entry : public mstch_base {
1117 public:
mstch_rust_map_entry(const t_const_value * key,const t_type * key_type,const t_const_value * value,const t_type * value_type,unsigned depth,std::shared_ptr<mstch_generators const> generators,std::shared_ptr<mstch_cache> cache,ELEMENT_POSITION pos,const rust_codegen_options & options)1118 mstch_rust_map_entry(
1119 const t_const_value* key,
1120 const t_type* key_type,
1121 const t_const_value* value,
1122 const t_type* value_type,
1123 unsigned depth,
1124 std::shared_ptr<mstch_generators const> generators,
1125 std::shared_ptr<mstch_cache> cache,
1126 ELEMENT_POSITION pos,
1127 const rust_codegen_options& options)
1128 : mstch_base(generators, cache, pos),
1129 key_(key),
1130 key_type_(key_type),
1131 value_(value),
1132 value_type_(value_type),
1133 depth_(depth),
1134 options_(options) {
1135 register_methods(
1136 this,
1137 {
1138 {"entry:key", &mstch_rust_map_entry::key},
1139 {"entry:value", &mstch_rust_map_entry::value},
1140 });
1141 }
key()1142 mstch::node key() {
1143 return std::make_shared<mstch_rust_value>(
1144 key_, key_type_, depth_, generators_, cache_, pos_, options_);
1145 }
value()1146 mstch::node value() {
1147 return std::make_shared<mstch_rust_value>(
1148 value_, value_type_, depth_, generators_, cache_, pos_, options_);
1149 }
1150
1151 private:
1152 const t_const_value* key_;
1153 const t_type* key_type_;
1154 const t_const_value* value_;
1155 const t_type* value_type_;
1156 unsigned depth_;
1157 const rust_codegen_options& options_;
1158 };
1159
1160 class mstch_rust_struct_field : public mstch_base {
1161 public:
mstch_rust_struct_field(const t_field * field,const t_const_value * value,unsigned depth,std::shared_ptr<mstch_generators const> generators,std::shared_ptr<mstch_cache> cache,ELEMENT_POSITION pos,const rust_codegen_options & options)1162 mstch_rust_struct_field(
1163 const t_field* field,
1164 const t_const_value* value,
1165 unsigned depth,
1166 std::shared_ptr<mstch_generators const> generators,
1167 std::shared_ptr<mstch_cache> cache,
1168 ELEMENT_POSITION pos,
1169 const rust_codegen_options& options)
1170 : mstch_base(generators, cache, pos),
1171 field_(field),
1172 value_(value),
1173 depth_(depth),
1174 options_(options) {
1175 register_methods(
1176 this,
1177 {
1178 {"field:rust_name", &mstch_rust_struct_field::rust_name},
1179 {"field:optional?", &mstch_rust_struct_field::is_optional},
1180 {"field:value", &mstch_rust_struct_field::value},
1181 {"field:type", &mstch_rust_struct_field::type},
1182 {"field:box?", &mstch_rust_struct_field::is_boxed},
1183 {"field:arc?", &mstch_rust_struct_field::is_arc},
1184 {"field:docs?", &mstch_rust_struct_field::rust_has_docs},
1185 {"field:docs", &mstch_rust_struct_field::rust_docs},
1186 });
1187 }
rust_name()1188 mstch::node rust_name() {
1189 if (!field_->has_annotation("rust.name")) {
1190 return mangle(field_->get_name());
1191 }
1192 return field_->get_annotation("rust.name");
1193 }
is_optional()1194 mstch::node is_optional() {
1195 return field_->get_req() == t_field::e_req::optional;
1196 }
value()1197 mstch::node value() {
1198 if (value_) {
1199 auto type = field_->get_type();
1200 return std::make_shared<mstch_rust_value>(
1201 value_, type, depth_, generators_, cache_, pos_, options_);
1202 }
1203 return mstch::node();
1204 }
type()1205 mstch::node type() {
1206 auto type = field_->get_type();
1207 return std::make_shared<mstch_rust_type>(
1208 type, generators_, cache_, pos_, options_);
1209 }
is_boxed()1210 mstch::node is_boxed() { return field_kind(*field_) == FieldKind::Box; }
is_arc()1211 mstch::node is_arc() { return field_kind(*field_) == FieldKind::Arc; }
rust_has_docs()1212 mstch::node rust_has_docs() { return field_->has_doc(); }
rust_docs()1213 mstch::node rust_docs() { return quoted_rust_doc(field_); }
1214
1215 private:
1216 const t_field* field_;
1217 const t_const_value* value_;
1218 unsigned depth_;
1219 const rust_codegen_options& options_;
1220 };
1221
map_entries()1222 mstch::node mstch_rust_value::map_entries() {
1223 auto map_type = dynamic_cast<const t_map*>(type_);
1224 if (!map_type) {
1225 return mstch::node();
1226 }
1227 auto key_type = map_type->get_key_type();
1228 auto value_type = map_type->get_val_type();
1229
1230 mstch::array entries;
1231 for (auto entry : const_value_->get_map()) {
1232 entries.push_back(std::make_shared<mstch_rust_map_entry>(
1233 entry.first,
1234 key_type,
1235 entry.second,
1236 value_type,
1237 depth_ + 1,
1238 generators_,
1239 cache_,
1240 pos_,
1241 options_));
1242 }
1243 return entries;
1244 }
1245
struct_fields()1246 mstch::node mstch_rust_value::struct_fields() {
1247 auto struct_type = dynamic_cast<const t_struct*>(type_);
1248 if (!struct_type) {
1249 return mstch::node();
1250 }
1251
1252 std::map<std::string, const t_const_value*> map_entries;
1253 for (auto entry : const_value_->get_map()) {
1254 auto key = entry.first;
1255 if (key->get_type() == value_type::CV_STRING) {
1256 map_entries[key->get_string()] = entry.second;
1257 }
1258 }
1259
1260 mstch::array fields;
1261 for (auto&& field : struct_type->fields()) {
1262 auto value = map_entries[field.name()];
1263 if (!value) {
1264 value = field.default_value();
1265 }
1266 fields.push_back(std::make_shared<mstch_rust_struct_field>(
1267 &field, value, depth_ + 1, generators_, cache_, pos_, options_));
1268 }
1269 return fields;
1270 }
1271
is_exhaustive()1272 mstch::node mstch_rust_value::is_exhaustive() {
1273 auto struct_type = dynamic_cast<const t_struct*>(type_);
1274 return struct_type && struct_type->has_annotation("rust.exhaustive");
1275 }
1276
1277 class mstch_rust_const : public mstch_const {
1278 public:
mstch_rust_const(const t_const * cnst,const t_const * current_const,const t_type * expected_type,std::shared_ptr<mstch_generators const> generators,std::shared_ptr<mstch_cache> cache,ELEMENT_POSITION const pos,int32_t index,t_field const * field,const rust_codegen_options & options)1279 mstch_rust_const(
1280 const t_const* cnst,
1281 const t_const* current_const,
1282 const t_type* expected_type,
1283 std::shared_ptr<mstch_generators const> generators,
1284 std::shared_ptr<mstch_cache> cache,
1285 ELEMENT_POSITION const pos,
1286 int32_t index,
1287 t_field const* field,
1288 const rust_codegen_options& options)
1289 : mstch_const(
1290 cnst,
1291 current_const,
1292 expected_type,
1293 generators,
1294 cache,
1295 pos,
1296 index,
1297 field),
1298 options_(options) {
1299 register_methods(
1300 this,
1301 {
1302 {"constant:package", &mstch_rust_const::rust_package},
1303 {"constant:lazy?", &mstch_rust_const::rust_lazy},
1304 {"constant:rust", &mstch_rust_const::rust_typed_value},
1305 {"constant:docs?", &mstch_rust_const::rust_has_docs},
1306 {"constant:docs", &mstch_rust_const::rust_docs},
1307 });
1308 }
rust_package()1309 mstch::node rust_package() {
1310 return get_import_name(cnst_->get_program(), options_);
1311 }
rust_lazy()1312 mstch::node rust_lazy() {
1313 auto type = cnst_->get_type()->get_true_type();
1314 return type->is_list() || type->is_map() || type->is_set() ||
1315 type->is_struct();
1316 }
rust_typed_value()1317 mstch::node rust_typed_value() {
1318 unsigned depth = 0;
1319 return std::make_shared<mstch_rust_value>(
1320 cnst_->get_value(),
1321 cnst_->get_type(),
1322 depth,
1323 generators_,
1324 cache_,
1325 pos_,
1326 options_);
1327 }
rust_has_docs()1328 mstch::node rust_has_docs() { return cnst_->has_doc(); }
rust_docs()1329 mstch::node rust_docs() { return quoted_rust_doc(cnst_); }
1330
1331 private:
1332 const rust_codegen_options& options_;
1333 };
1334
1335 class mstch_rust_field : public mstch_field {
1336 public:
mstch_rust_field(const t_field * field,std::shared_ptr<mstch_generators const> generators,std::shared_ptr<mstch_cache> cache,ELEMENT_POSITION const pos,int32_t index,field_generator_context const * field_context,const rust_codegen_options & options)1337 mstch_rust_field(
1338 const t_field* field,
1339 std::shared_ptr<mstch_generators const> generators,
1340 std::shared_ptr<mstch_cache> cache,
1341 ELEMENT_POSITION const pos,
1342 int32_t index,
1343 field_generator_context const* field_context,
1344 const rust_codegen_options& options)
1345 : mstch_field(field, generators, cache, pos, index, field_context),
1346 options_(options) {
1347 register_methods(
1348 this,
1349 {
1350 {"field:rust_name", &mstch_rust_field::rust_name},
1351 {"field:primitive?", &mstch_rust_field::rust_primitive},
1352 {"field:rename?", &mstch_rust_field::rust_rename},
1353 {"field:default", &mstch_rust_field::rust_default},
1354 {"field:box?", &mstch_rust_field::rust_is_boxed},
1355 {"field:arc?", &mstch_rust_field::rust_is_arc},
1356 {"field:docs?", &mstch_rust_field::rust_has_docs},
1357 {"field:docs", &mstch_rust_field::rust_docs},
1358 });
1359 }
rust_name()1360 mstch::node rust_name() {
1361 if (!field_->has_annotation("rust.name")) {
1362 return mangle(field_->get_name());
1363 }
1364 return field_->get_annotation("rust.name");
1365 }
rust_primitive()1366 mstch::node rust_primitive() {
1367 auto type = field_->get_type();
1368 return type->is_bool() || type->is_any_int() || type->is_floating_point();
1369 }
rust_rename()1370 mstch::node rust_rename() {
1371 return field_->get_name() != mangle(field_->get_name());
1372 }
rust_default()1373 mstch::node rust_default() {
1374 auto value = field_->get_value();
1375 if (value) {
1376 unsigned depth = 2; // impl Default + fn default
1377 auto type = field_->get_type();
1378 return std::make_shared<mstch_rust_value>(
1379 value, type, depth, generators_, cache_, pos_, options_);
1380 }
1381 return mstch::node();
1382 }
rust_is_boxed()1383 mstch::node rust_is_boxed() { return field_kind(*field_) == FieldKind::Box; }
rust_is_arc()1384 mstch::node rust_is_arc() { return field_kind(*field_) == FieldKind::Arc; }
rust_has_docs()1385 mstch::node rust_has_docs() { return field_->has_doc(); }
rust_docs()1386 mstch::node rust_docs() { return quoted_rust_doc(field_); }
1387
1388 private:
1389 const rust_codegen_options& options_;
1390 };
1391
1392 class mstch_rust_typedef : public mstch_typedef {
1393 public:
mstch_rust_typedef(const t_typedef * typedf,std::shared_ptr<mstch_generators const> generators,std::shared_ptr<mstch_cache> cache,ELEMENT_POSITION pos)1394 mstch_rust_typedef(
1395 const t_typedef* typedf,
1396 std::shared_ptr<mstch_generators const> generators,
1397 std::shared_ptr<mstch_cache> cache,
1398 ELEMENT_POSITION pos)
1399 : mstch_typedef(typedf, generators, cache, pos) {
1400 register_methods(
1401 this,
1402 {
1403 {"typedef:rust_name", &mstch_rust_typedef::rust_name},
1404 {"typedef:newtype?", &mstch_rust_typedef::rust_newtype},
1405 {"typedef:ord?", &mstch_rust_typedef::rust_ord},
1406 {"typedef:copy?", &mstch_rust_typedef::rust_copy},
1407 {"typedef:rust_type", &mstch_rust_typedef::rust_type},
1408 {"typedef:nonstandard?", &mstch_rust_typedef::rust_nonstandard},
1409 {"typedef:docs?", &mstch_rust_typedef::rust_has_docs},
1410 {"typedef:docs", &mstch_rust_typedef::rust_docs},
1411 });
1412 }
rust_name()1413 mstch::node rust_name() {
1414 if (!typedf_->has_annotation("rust.name")) {
1415 return mangle_type(typedf_->name());
1416 }
1417 return typedf_->get_annotation("rust.name");
1418 }
rust_newtype()1419 mstch::node rust_newtype() { return typedf_->has_annotation("rust.newtype"); }
rust_type()1420 mstch::node rust_type() {
1421 const std::string& rust_type = typedf_->get_annotation("rust.type");
1422 if (!rust_type.empty() && rust_type.find("::") == std::string::npos) {
1423 return "fbthrift::builtin_types::" + rust_type;
1424 }
1425 return rust_type;
1426 }
rust_ord()1427 mstch::node rust_ord() {
1428 return typedf_->has_annotation("rust.ord") ||
1429 can_derive_ord(typedf_->get_type());
1430 }
rust_copy()1431 mstch::node rust_copy() {
1432 auto inner = typedf_->get_true_type();
1433 if (inner->is_bool() || inner->is_byte() || inner->is_i16() ||
1434 inner->is_i32() || inner->is_i64() || inner->is_enum() ||
1435 inner->is_void()) {
1436 return true;
1437 }
1438 if (typedf_->has_annotation("rust.copy")) {
1439 return true;
1440 }
1441 return false;
1442 }
rust_nonstandard()1443 mstch::node rust_nonstandard() {
1444 return typedf_->get_annotation("rust.type").find("::") != string::npos;
1445 }
rust_has_docs()1446 mstch::node rust_has_docs() { return typedf_->has_doc(); }
rust_docs()1447 mstch::node rust_docs() { return quoted_rust_doc(typedf_); }
1448 };
1449
1450 class mstch_rust_annotation : public mstch_annotation {
1451 public:
mstch_rust_annotation(const t_annotation & annotation,std::shared_ptr<const mstch_generators> generators,std::shared_ptr<mstch_cache> cache,ELEMENT_POSITION pos,int32_t index)1452 mstch_rust_annotation(
1453 const t_annotation& annotation,
1454 std::shared_ptr<const mstch_generators> generators,
1455 std::shared_ptr<mstch_cache> cache,
1456 ELEMENT_POSITION pos,
1457 int32_t index)
1458 : mstch_annotation(
1459 annotation.first,
1460 annotation.second,
1461 generators,
1462 cache,
1463 pos,
1464 index) {
1465 register_methods(
1466 this,
1467 {
1468 {"annotation:value?", &mstch_rust_annotation::rust_has_value},
1469 {"annotation:rust_name", &mstch_rust_annotation::rust_name},
1470 {"annotation:rust_value", &mstch_rust_annotation::rust_value},
1471 });
1472 }
rust_has_value()1473 mstch::node rust_has_value() { return !val_.value.empty(); }
rust_name()1474 mstch::node rust_name() {
1475 return boost::algorithm::replace_all_copy(key_, ".", "_");
1476 }
rust_value()1477 mstch::node rust_value() { return quote(val_.value); }
1478 };
1479
1480 class program_rust_generator : public program_generator {
1481 public:
program_rust_generator(const rust_codegen_options & options)1482 explicit program_rust_generator(const rust_codegen_options& options)
1483 : options_(options) {}
1484 ~program_rust_generator() override = default;
generate(t_program const * program,std::shared_ptr<mstch_generators const> generators,std::shared_ptr<mstch_cache> cache,ELEMENT_POSITION pos,int32_t) const1485 std::shared_ptr<mstch_base> generate(
1486 t_program const* program,
1487 std::shared_ptr<mstch_generators const> generators,
1488 std::shared_ptr<mstch_cache> cache,
1489 ELEMENT_POSITION pos,
1490 int32_t /*index*/) const override {
1491 return std::make_shared<mstch_rust_program>(
1492 program, generators, cache, pos, options_);
1493 }
1494
1495 private:
1496 const rust_codegen_options& options_;
1497 };
1498
1499 class struct_rust_generator : public struct_generator {
1500 public:
struct_rust_generator(const rust_codegen_options & options)1501 explicit struct_rust_generator(const rust_codegen_options& options)
1502 : options_(options) {}
1503 ~struct_rust_generator() override = default;
generate(const t_struct * strct,std::shared_ptr<mstch_generators const> generators,std::shared_ptr<mstch_cache> cache,ELEMENT_POSITION pos,int32_t) const1504 std::shared_ptr<mstch_base> generate(
1505 const t_struct* strct,
1506 std::shared_ptr<mstch_generators const> generators,
1507 std::shared_ptr<mstch_cache> cache,
1508 ELEMENT_POSITION pos,
1509 int32_t /*index*/) const override {
1510 return std::make_shared<mstch_rust_struct>(
1511 strct, generators, cache, pos, options_);
1512 }
1513
1514 private:
1515 const rust_codegen_options& options_;
1516 };
1517
1518 class service_rust_generator : public service_generator {
1519 public:
service_rust_generator(const rust_codegen_options & options)1520 explicit service_rust_generator(const rust_codegen_options& options)
1521 : options_(options) {}
1522 ~service_rust_generator() override = default;
generate(const t_service * service,std::shared_ptr<mstch_generators const> generators,std::shared_ptr<mstch_cache> cache,ELEMENT_POSITION pos,int32_t) const1523 std::shared_ptr<mstch_base> generate(
1524 const t_service* service,
1525 std::shared_ptr<mstch_generators const> generators,
1526 std::shared_ptr<mstch_cache> cache,
1527 ELEMENT_POSITION pos,
1528 int32_t /*index*/) const override {
1529 return std::make_shared<mstch_rust_service>(
1530 service, generators, cache, pos, options_);
1531 }
1532
1533 private:
1534 const rust_codegen_options& options_;
1535 };
1536
1537 class function_rust_generator {
1538 public:
generate(const t_function * function,std::shared_ptr<mstch_generators const> generators,std::shared_ptr<mstch_cache> cache,ELEMENT_POSITION pos,int32_t index,const std::unordered_multiset<std::string> & function_upcamel_names) const1539 std::shared_ptr<mstch_base> generate(
1540 const t_function* function,
1541 std::shared_ptr<mstch_generators const> generators,
1542 std::shared_ptr<mstch_cache> cache,
1543 ELEMENT_POSITION pos,
1544 int32_t index,
1545 const std::unordered_multiset<std::string>& function_upcamel_names)
1546 const {
1547 return std::make_shared<mstch_rust_function>(
1548 function, generators, cache, pos, index, function_upcamel_names);
1549 }
1550 };
1551
rust_functions()1552 mstch::node mstch_rust_service::rust_functions() {
1553 function_rust_generator function_generator;
1554 return generate_elements(
1555 service_->get_functions(), &function_generator, function_upcamel_names_);
1556 }
1557
1558 class field_rust_generator : public field_generator {
1559 public:
field_rust_generator(const rust_codegen_options & options)1560 explicit field_rust_generator(const rust_codegen_options& options)
1561 : options_(options) {}
1562
generate(const t_field * field,std::shared_ptr<mstch_generators const> generators,std::shared_ptr<mstch_cache> cache,ELEMENT_POSITION pos,int32_t index,field_generator_context const * field_context=nullptr) const1563 std::shared_ptr<mstch_base> generate(
1564 const t_field* field,
1565 std::shared_ptr<mstch_generators const> generators,
1566 std::shared_ptr<mstch_cache> cache,
1567 ELEMENT_POSITION pos,
1568 int32_t index,
1569 field_generator_context const* field_context = nullptr) const override {
1570 return std::make_shared<mstch_rust_field>(
1571 field, generators, cache, pos, index, field_context, options_);
1572 }
1573
1574 private:
1575 const rust_codegen_options& options_;
1576 };
1577
1578 class enum_value_rust_generator : public enum_value_generator {
1579 public:
1580 enum_value_rust_generator() = default;
1581 ~enum_value_rust_generator() override = default;
generate(const t_enum_value * enm_value,std::shared_ptr<mstch_generators const> generators,std::shared_ptr<mstch_cache> cache,ELEMENT_POSITION pos,int32_t) const1582 std::shared_ptr<mstch_base> generate(
1583 const t_enum_value* enm_value,
1584 std::shared_ptr<mstch_generators const> generators,
1585 std::shared_ptr<mstch_cache> cache,
1586 ELEMENT_POSITION pos,
1587 int32_t /*index*/) const override {
1588 return std::make_shared<mstch_rust_enum_value>(
1589 enm_value, generators, cache, pos);
1590 }
1591 };
1592
1593 class enum_rust_generator : public enum_generator {
1594 public:
enum_rust_generator(const rust_codegen_options & options)1595 explicit enum_rust_generator(const rust_codegen_options& options)
1596 : options_(options) {}
1597 ~enum_rust_generator() override = default;
generate(const t_enum * enm,std::shared_ptr<mstch_generators const> generators,std::shared_ptr<mstch_cache> cache,ELEMENT_POSITION pos,int32_t) const1598 std::shared_ptr<mstch_base> generate(
1599 const t_enum* enm,
1600 std::shared_ptr<mstch_generators const> generators,
1601 std::shared_ptr<mstch_cache> cache,
1602 ELEMENT_POSITION pos,
1603 int32_t /*index*/) const override {
1604 return std::make_shared<mstch_rust_enum>(
1605 enm, generators, cache, pos, options_);
1606 }
1607
1608 private:
1609 const rust_codegen_options& options_;
1610 };
1611
1612 class type_rust_generator : public type_generator {
1613 public:
type_rust_generator(const rust_codegen_options & options)1614 explicit type_rust_generator(const rust_codegen_options& options)
1615 : options_(options) {}
1616 ~type_rust_generator() override = default;
generate(const t_type * type,std::shared_ptr<mstch_generators const> generators,std::shared_ptr<mstch_cache> cache,ELEMENT_POSITION pos,int32_t) const1617 std::shared_ptr<mstch_base> generate(
1618 const t_type* type,
1619 std::shared_ptr<mstch_generators const> generators,
1620 std::shared_ptr<mstch_cache> cache,
1621 ELEMENT_POSITION pos,
1622 int32_t /*index*/) const override {
1623 return std::make_shared<mstch_rust_type>(
1624 type, generators, cache, pos, options_);
1625 }
1626
1627 private:
1628 const rust_codegen_options& options_;
1629 };
1630
rust_all_exceptions()1631 mstch::node mstch_rust_service::rust_all_exceptions() {
1632 std::map<const t_type*, std::vector<const t_function*>> function_map;
1633 std::map<const t_type*, std::vector<const t_field*>> field_map;
1634 for (const auto& fun : service_->functions()) {
1635 for (const auto& fld : fun.get_xceptions()->fields()) {
1636 function_map[fld.type()->get_true_type()].push_back(&fun);
1637 field_map[fld.type()->get_true_type()].push_back(&fld);
1638 }
1639 }
1640
1641 mstch::array output;
1642 for (const auto& funcs : function_map) {
1643 mstch::map data;
1644 type_rust_generator gen(options_);
1645 data["rust_exception:type"] = gen.generate(
1646 funcs.first, generators_, cache_, ELEMENT_POSITION::NONE, 0);
1647
1648 function_rust_generator function_generator;
1649
1650 auto functions = generate_elements(
1651 funcs.second, &function_generator, function_upcamel_names_);
1652 auto fields = generate_fields(field_map[funcs.first]);
1653
1654 mstch::array function_data;
1655 for (size_t i = 0; i < fields.size(); i++) {
1656 mstch::map inner;
1657 inner["rust_exception_function:function"] = std::move(functions[i]);
1658 inner["rust_exception_function:field"] = std::move(fields[i]);
1659 function_data.push_back(std::move(inner));
1660 }
1661
1662 data["rust_exception:functions"] = std::move(function_data);
1663 output.push_back(data);
1664 }
1665
1666 return output;
1667 }
1668
1669 class const_rust_generator : public const_generator {
1670 public:
const_rust_generator(const rust_codegen_options & options)1671 explicit const_rust_generator(const rust_codegen_options& options)
1672 : options_(options) {}
1673 ~const_rust_generator() override = default;
generate(const t_const * cnst,std::shared_ptr<mstch_generators const> generators,std::shared_ptr<mstch_cache> cache,ELEMENT_POSITION pos,int32_t index,const t_const * current_const,const t_type * expected_type,t_field const * field) const1674 std::shared_ptr<mstch_base> generate(
1675 const t_const* cnst,
1676 std::shared_ptr<mstch_generators const> generators,
1677 std::shared_ptr<mstch_cache> cache,
1678 ELEMENT_POSITION pos,
1679 int32_t index,
1680 const t_const* current_const,
1681 const t_type* expected_type,
1682 t_field const* field) const override {
1683 return std::make_shared<mstch_rust_const>(
1684 cnst,
1685 current_const,
1686 expected_type,
1687 generators,
1688 cache,
1689 pos,
1690 index,
1691 field,
1692 options_);
1693 }
1694
1695 private:
1696 const rust_codegen_options& options_;
1697 };
1698
1699 class typedef_rust_generator : public typedef_generator {
1700 public:
1701 typedef_rust_generator() = default;
1702 ~typedef_rust_generator() override = default;
generate(const t_typedef * typedf,std::shared_ptr<mstch_generators const> generators,std::shared_ptr<mstch_cache> cache,ELEMENT_POSITION pos,int32_t) const1703 std::shared_ptr<mstch_base> generate(
1704 const t_typedef* typedf,
1705 std::shared_ptr<mstch_generators const> generators,
1706 std::shared_ptr<mstch_cache> cache,
1707 ELEMENT_POSITION pos,
1708 int32_t /*index*/) const override {
1709 return std::make_shared<mstch_rust_typedef>(typedf, generators, cache, pos);
1710 }
1711 };
1712
1713 class annotation_rust_generator : public annotation_generator {
1714 public:
generate(const t_annotation & annotation,std::shared_ptr<const mstch_generators> generators,std::shared_ptr<mstch_cache> cache,ELEMENT_POSITION pos,int32_t index) const1715 std::shared_ptr<mstch_base> generate(
1716 const t_annotation& annotation,
1717 std::shared_ptr<const mstch_generators> generators,
1718 std::shared_ptr<mstch_cache> cache,
1719 ELEMENT_POSITION pos,
1720 int32_t index) const override {
1721 return std::make_shared<mstch_rust_annotation>(
1722 annotation, generators, cache, pos, index);
1723 }
1724 };
1725
generate_program()1726 void t_mstch_rust_generator::generate_program() {
1727 set_mstch_generators();
1728
1729 const auto* program = get_program();
1730 const auto& prog = cached_program(program);
1731
1732 render_to_file(prog, "lib.rs", "lib.rs");
1733 }
1734
set_mstch_generators()1735 void t_mstch_rust_generator::set_mstch_generators() {
1736 generators_->set_program_generator(
1737 std::make_unique<program_rust_generator>(options_));
1738 generators_->set_struct_generator(
1739 std::make_unique<struct_rust_generator>(options_));
1740 generators_->set_service_generator(
1741 std::make_unique<service_rust_generator>(options_));
1742 generators_->set_field_generator(
1743 std::make_unique<field_rust_generator>(options_));
1744 generators_->set_enum_value_generator(
1745 std::make_unique<enum_value_rust_generator>());
1746 generators_->set_enum_generator(
1747 std::make_unique<enum_rust_generator>(options_));
1748 generators_->set_type_generator(
1749 std::make_unique<type_rust_generator>(options_));
1750 generators_->set_const_generator(
1751 std::make_unique<const_rust_generator>(options_));
1752 generators_->set_typedef_generator(
1753 std::make_unique<typedef_rust_generator>());
1754 generators_->set_annotation_generator(
1755 std::make_unique<annotation_rust_generator>());
1756 }
1757
load_crate_map(const std::string & path)1758 void t_mstch_rust_generator::load_crate_map(const std::string& path) {
1759 // Each line of the file is:
1760 // thrift_name crate_name
1761 //
1762 // As an example of each value, we might have:
1763 // - thrift_name: demo
1764 // (this is the name by which the dependency is referred to in thrift)
1765 // - crate_name: demo_api
1766 // (the Rust code will refer to demo_api::types::WhateverType)
1767 auto in = std::ifstream(path);
1768
1769 // Map from crate_name to list of thrift_names. Most Thrift crates consist of
1770 // a single *.thrift file but some may have multiple.
1771 std::map<std::string, std::vector<std::string>> sources;
1772
1773 std::string line;
1774 while (std::getline(in, line)) {
1775 std::istringstream iss(line);
1776 std::string thrift_name, crate_name;
1777 iss >> thrift_name >> crate_name;
1778 sources[crate_name].push_back(thrift_name);
1779 }
1780
1781 for (const auto& source : sources) {
1782 std::string crate_name;
1783 auto thrift_names = source.second;
1784 auto multifile = thrift_names.size() > 1;
1785
1786 // Look out for our own crate in the cratemap. It will require paths that
1787 // begin with `crate::module` rather than `::depenency::module`.
1788 if (source.first == "crate") {
1789 crate_name = "crate";
1790 options_.multifile_mode = multifile;
1791 } else {
1792 crate_name = "::" + mangle(source.first);
1793 }
1794
1795 if (multifile) {
1796 for (const auto& thrift_name : thrift_names) {
1797 options_.cratemap[thrift_name] =
1798 crate_name + "::" + mangle(thrift_name);
1799 }
1800 } else if (crate_name != "crate") {
1801 options_.cratemap[thrift_names[0]] = crate_name;
1802 }
1803 }
1804 }
1805
1806 namespace {
1807 class annotation_validator : public validator {
1808 public:
1809 using validator::visit;
1810 bool visit(t_struct* s) override;
1811 };
1812
visit(t_struct * s)1813 bool annotation_validator::visit(t_struct* s) {
1814 for (auto& field : s->fields()) {
1815 bool box = field.has_annotation("rust.box");
1816 bool arc = field.has_annotation("rust.arc");
1817 if (box && arc) {
1818 add_error(
1819 field.lineno(),
1820 "Field `" + field.name() + "` cannot be both Box'ed and Arc'ed");
1821 }
1822 }
1823 return true;
1824 }
1825 } // namespace
1826
fill_validator_list(validator_list & l) const1827 void t_mstch_rust_generator::fill_validator_list(validator_list& l) const {
1828 l.add<annotation_validator>();
1829 }
1830
1831 THRIFT_REGISTER_GENERATOR(
1832 mstch_rust,
1833 "Rust",
1834 " serde: Derive serde Serialize/Deserialize traits for types\n"
1835 " noserver: Don't emit server code\n"
1836 " include_prefix=: Set program:include_prefix.\n"
1837 " include_srcs=: Additional Rust source file to include in output, `:` separated\n"
1838 " cratemap=map: Mapping file from services to crate names\n");
1839 } // namespace compiler
1840 } // namespace thrift
1841 } // namespace apache
1842