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