1 /*
2 * Copyright 2018 Google Inc. All rights reserved.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 // independent from idl_parser, since this code is not needed for most clients
18
19 #include "flatbuffers/code_generators.h"
20 #include "flatbuffers/flatbuffers.h"
21 #include "flatbuffers/idl.h"
22 #include "flatbuffers/util.h"
23
24 namespace flatbuffers {
25
26 // Convert a camelCaseIdentifier or CamelCaseIdentifier to a
27 // snake_case_indentifier.
MakeSnakeCase(const std::string & in)28 std::string MakeSnakeCase(const std::string &in) {
29 std::string s;
30 for (size_t i = 0; i < in.length(); i++) {
31 if (i == 0) {
32 s += static_cast<char>(tolower(in[0]));
33 } else if (in[i] == '_') {
34 s += '_';
35 } else if (!islower(in[i])) {
36 // Prevent duplicate underscores for Upper_Snake_Case strings
37 // and UPPERCASE strings.
38 if (islower(in[i - 1])) { s += '_'; }
39 s += static_cast<char>(tolower(in[i]));
40 } else {
41 s += in[i];
42 }
43 }
44 return s;
45 }
46
47 // Convert a string to all uppercase.
MakeUpper(const std::string & in)48 std::string MakeUpper(const std::string &in) {
49 std::string s;
50 for (size_t i = 0; i < in.length(); i++) {
51 s += static_cast<char>(toupper(in[i]));
52 }
53 return s;
54 }
55
56 // Encapsulate all logical field types in this enum. This allows us to write
57 // field logic based on type switches, instead of branches on the properties
58 // set on the Type.
59 // TODO(rw): for backwards compatibility, we can't use a strict `enum class`
60 // declaration here. could we use the `-Wswitch-enum` warning to
61 // achieve the same effect?
62 enum FullType {
63 ftInteger = 0,
64 ftFloat = 1,
65 ftBool = 2,
66
67 ftStruct = 3,
68 ftTable = 4,
69
70 ftEnumKey = 5,
71 ftUnionKey = 6,
72
73 ftUnionValue = 7,
74
75 // TODO(rw): bytestring?
76 ftString = 8,
77
78 ftVectorOfInteger = 9,
79 ftVectorOfFloat = 10,
80 ftVectorOfBool = 11,
81 ftVectorOfEnumKey = 12,
82 ftVectorOfStruct = 13,
83 ftVectorOfTable = 14,
84 ftVectorOfString = 15,
85 ftVectorOfUnionValue = 16,
86 };
87
88 // Convert a Type to a FullType (exhaustive).
GetFullType(const Type & type)89 FullType GetFullType(const Type &type) {
90 // N.B. The order of these conditionals matters for some types.
91
92 if (type.base_type == BASE_TYPE_STRING) {
93 return ftString;
94 } else if (type.base_type == BASE_TYPE_STRUCT) {
95 if (type.struct_def->fixed) {
96 return ftStruct;
97 } else {
98 return ftTable;
99 }
100 } else if (type.base_type == BASE_TYPE_VECTOR) {
101 switch (GetFullType(type.VectorType())) {
102 case ftInteger: {
103 return ftVectorOfInteger;
104 }
105 case ftFloat: {
106 return ftVectorOfFloat;
107 }
108 case ftBool: {
109 return ftVectorOfBool;
110 }
111 case ftStruct: {
112 return ftVectorOfStruct;
113 }
114 case ftTable: {
115 return ftVectorOfTable;
116 }
117 case ftString: {
118 return ftVectorOfString;
119 }
120 case ftEnumKey: {
121 return ftVectorOfEnumKey;
122 }
123 case ftUnionKey:
124 case ftUnionValue: {
125 FLATBUFFERS_ASSERT(false && "vectors of unions are unsupported");
126 break;
127 }
128 default: {
129 FLATBUFFERS_ASSERT(false && "vector of vectors are unsupported");
130 }
131 }
132 } else if (type.enum_def != nullptr) {
133 if (type.enum_def->is_union) {
134 if (type.base_type == BASE_TYPE_UNION) {
135 return ftUnionValue;
136 } else if (IsInteger(type.base_type)) {
137 return ftUnionKey;
138 } else {
139 FLATBUFFERS_ASSERT(false && "unknown union field type");
140 }
141 } else {
142 return ftEnumKey;
143 }
144 } else if (IsScalar(type.base_type)) {
145 if (IsBool(type.base_type)) {
146 return ftBool;
147 } else if (IsInteger(type.base_type)) {
148 return ftInteger;
149 } else if (IsFloat(type.base_type)) {
150 return ftFloat;
151 } else {
152 FLATBUFFERS_ASSERT(false && "unknown number type");
153 }
154 }
155
156 FLATBUFFERS_ASSERT(false && "completely unknown type");
157
158 // this is only to satisfy the compiler's return analysis.
159 return ftBool;
160 }
161
162 // If the second parameter is false then wrap the first with Option<...>
WrapInOptionIfNotRequired(std::string s,bool required)163 std::string WrapInOptionIfNotRequired(std::string s, bool required) {
164 if (required) {
165 return s;
166 } else {
167 return "Option<" + s + ">";
168 }
169 }
170
171 // If the second parameter is false then add .unwrap()
AddUnwrapIfRequired(std::string s,bool required)172 std::string AddUnwrapIfRequired(std::string s, bool required) {
173 if (required) {
174 return s + ".unwrap()";
175 } else {
176 return s;
177 }
178 }
179
180 namespace rust {
181
182 class RustGenerator : public BaseGenerator {
183 public:
RustGenerator(const Parser & parser,const std::string & path,const std::string & file_name)184 RustGenerator(const Parser &parser, const std::string &path,
185 const std::string &file_name)
186 : BaseGenerator(parser, path, file_name, "", "::", "rs"),
187 cur_name_space_(nullptr) {
188 const char *keywords[] = {
189 // list taken from:
190 // https://doc.rust-lang.org/book/second-edition/appendix-01-keywords.html
191 //
192 // we write keywords one per line so that we can easily compare them with
193 // changes to that webpage in the future.
194
195 // currently-used keywords
196 "as", "break", "const", "continue", "crate", "else", "enum", "extern",
197 "false", "fn", "for", "if", "impl", "in", "let", "loop", "match", "mod",
198 "move", "mut", "pub", "ref", "return", "Self", "self", "static", "struct",
199 "super", "trait", "true", "type", "unsafe", "use", "where", "while",
200
201 // future possible keywords
202 "abstract", "alignof", "become", "box", "do", "final", "macro",
203 "offsetof", "override", "priv", "proc", "pure", "sizeof", "typeof",
204 "unsized", "virtual", "yield",
205
206 // other rust terms we should not use
207 "std", "usize", "isize", "u8", "i8", "u16", "i16", "u32", "i32", "u64",
208 "i64", "u128", "i128", "f32", "f64",
209
210 // These are terms the code generator can implement on types.
211 //
212 // In Rust, the trait resolution rules (as described at
213 // https://github.com/rust-lang/rust/issues/26007) mean that, as long
214 // as we impl table accessors as inherent methods, we'll never create
215 // conflicts with these keywords. However, that's a fairly nuanced
216 // implementation detail, and how we implement methods could change in
217 // the future. as a result, we proactively block these out as reserved
218 // words.
219 "follow", "push", "size", "alignment", "to_little_endian",
220 "from_little_endian", nullptr
221 };
222 for (auto kw = keywords; *kw; kw++) keywords_.insert(*kw);
223 }
224
225 // Iterate through all definitions we haven't generated code for (enums,
226 // structs, and tables) and output them to a single file.
generate()227 bool generate() {
228 code_.Clear();
229 code_ += "// " + std::string(FlatBuffersGeneratedWarning()) + "\n\n";
230
231 assert(!cur_name_space_);
232
233 // Generate imports for the global scope in case no namespace is used
234 // in the schema file.
235 GenNamespaceImports(0);
236 code_ += "";
237
238 // Generate all code in their namespaces, once, because Rust does not
239 // permit re-opening modules.
240 //
241 // TODO(rw): Use a set data structure to reduce namespace evaluations from
242 // O(n**2) to O(n).
243 for (auto ns_it = parser_.namespaces_.begin();
244 ns_it != parser_.namespaces_.end(); ++ns_it) {
245 const auto &ns = *ns_it;
246
247 // Generate code for all the enum declarations.
248 for (auto it = parser_.enums_.vec.begin(); it != parser_.enums_.vec.end();
249 ++it) {
250 const auto &enum_def = **it;
251 if (enum_def.defined_namespace != ns) { continue; }
252 if (!enum_def.generated) {
253 SetNameSpace(enum_def.defined_namespace);
254 GenEnum(enum_def);
255 }
256 }
257
258 // Generate code for all structs.
259 for (auto it = parser_.structs_.vec.begin();
260 it != parser_.structs_.vec.end(); ++it) {
261 const auto &struct_def = **it;
262 if (struct_def.defined_namespace != ns) { continue; }
263 if (struct_def.fixed && !struct_def.generated) {
264 SetNameSpace(struct_def.defined_namespace);
265 GenStruct(struct_def);
266 }
267 }
268
269 // Generate code for all tables.
270 for (auto it = parser_.structs_.vec.begin();
271 it != parser_.structs_.vec.end(); ++it) {
272 const auto &struct_def = **it;
273 if (struct_def.defined_namespace != ns) { continue; }
274 if (!struct_def.fixed && !struct_def.generated) {
275 SetNameSpace(struct_def.defined_namespace);
276 GenTable(struct_def);
277 }
278 }
279
280 // Generate global helper functions.
281 if (parser_.root_struct_def_) {
282 auto &struct_def = *parser_.root_struct_def_;
283 if (struct_def.defined_namespace != ns) { continue; }
284 SetNameSpace(struct_def.defined_namespace);
285 GenRootTableFuncs(struct_def);
286 }
287 }
288 if (cur_name_space_) SetNameSpace(nullptr);
289
290 const auto file_path = GeneratedFileName(path_, file_name_, parser_.opts);
291 const auto final_code = code_.ToString();
292 return SaveFile(file_path.c_str(), final_code, false);
293 }
294
295 private:
296 CodeWriter code_;
297
298 std::set<std::string> keywords_;
299
300 // This tracks the current namespace so we can insert namespace declarations.
301 const Namespace *cur_name_space_;
302
CurrentNameSpace() const303 const Namespace *CurrentNameSpace() const { return cur_name_space_; }
304
305 // Determine if a Type needs a lifetime template parameter when used in the
306 // Rust builder args.
TableBuilderTypeNeedsLifetime(const Type & type) const307 bool TableBuilderTypeNeedsLifetime(const Type &type) const {
308 switch (GetFullType(type)) {
309 case ftInteger:
310 case ftFloat:
311 case ftBool:
312 case ftEnumKey:
313 case ftUnionKey:
314 case ftUnionValue: {
315 return false;
316 }
317 default: {
318 return true;
319 }
320 }
321 }
322
323 // Determine if a table args rust type needs a lifetime template parameter.
TableBuilderArgsNeedsLifetime(const StructDef & struct_def) const324 bool TableBuilderArgsNeedsLifetime(const StructDef &struct_def) const {
325 FLATBUFFERS_ASSERT(!struct_def.fixed);
326
327 for (auto it = struct_def.fields.vec.begin();
328 it != struct_def.fields.vec.end(); ++it) {
329 const auto &field = **it;
330 if (field.deprecated) { continue; }
331
332 if (TableBuilderTypeNeedsLifetime(field.value.type)) { return true; }
333 }
334
335 return false;
336 }
337
338 // Determine if a Type needs to be copied (for endian safety) when used in a
339 // Struct.
StructMemberAccessNeedsCopy(const Type & type) const340 bool StructMemberAccessNeedsCopy(const Type &type) const {
341 switch (GetFullType(type)) {
342 case ftInteger: // requires endian swap
343 case ftFloat: // requires endian swap
344 case ftBool: // no endian-swap, but do the copy for UX consistency
345 case ftEnumKey: {
346 return true;
347 } // requires endian swap
348 case ftStruct: {
349 return false;
350 } // no endian swap
351 default: {
352 // logic error: no other types can be struct members.
353 FLATBUFFERS_ASSERT(false && "invalid struct member type");
354 return false; // only to satisfy compiler's return analysis
355 }
356 }
357 }
358
EscapeKeyword(const std::string & name) const359 std::string EscapeKeyword(const std::string &name) const {
360 return keywords_.find(name) == keywords_.end() ? name : name + "_";
361 }
362
Name(const Definition & def) const363 std::string Name(const Definition &def) const {
364 return EscapeKeyword(def.name);
365 }
366
Name(const EnumVal & ev) const367 std::string Name(const EnumVal &ev) const { return EscapeKeyword(ev.name); }
368
WrapInNameSpace(const Definition & def) const369 std::string WrapInNameSpace(const Definition &def) const {
370 return WrapInNameSpace(def.defined_namespace, Name(def));
371 }
WrapInNameSpace(const Namespace * ns,const std::string & name) const372 std::string WrapInNameSpace(const Namespace *ns,
373 const std::string &name) const {
374 if (CurrentNameSpace() == ns) return name;
375 std::string prefix = GetRelativeNamespaceTraversal(CurrentNameSpace(), ns);
376 return prefix + name;
377 }
378
379 // Determine the namespace traversal needed from the Rust crate root.
380 // This may be useful in the future for referring to included files, but is
381 // currently unused.
GetAbsoluteNamespaceTraversal(const Namespace * dst) const382 std::string GetAbsoluteNamespaceTraversal(const Namespace *dst) const {
383 std::stringstream stream;
384
385 stream << "::";
386 for (auto d = dst->components.begin(); d != dst->components.end(); ++d) {
387 stream << MakeSnakeCase(*d) + "::";
388 }
389 return stream.str();
390 }
391
392 // Determine the relative namespace traversal needed to reference one
393 // namespace from another namespace. This is useful because it does not force
394 // the user to have a particular file layout. (If we output absolute
395 // namespace paths, that may require users to organize their Rust crates in a
396 // particular way.)
GetRelativeNamespaceTraversal(const Namespace * src,const Namespace * dst) const397 std::string GetRelativeNamespaceTraversal(const Namespace *src,
398 const Namespace *dst) const {
399 // calculate the path needed to reference dst from src.
400 // example: f(A::B::C, A::B::C) -> (none)
401 // example: f(A::B::C, A::B) -> super::
402 // example: f(A::B::C, A::B::D) -> super::D
403 // example: f(A::B::C, A) -> super::super::
404 // example: f(A::B::C, D) -> super::super::super::D
405 // example: f(A::B::C, D::E) -> super::super::super::D::E
406 // example: f(A, D::E) -> super::D::E
407 // does not include leaf object (typically a struct type).
408
409 size_t i = 0;
410 std::stringstream stream;
411
412 auto s = src->components.begin();
413 auto d = dst->components.begin();
414 for (;;) {
415 if (s == src->components.end()) { break; }
416 if (d == dst->components.end()) { break; }
417 if (*s != *d) { break; }
418 ++s;
419 ++d;
420 ++i;
421 }
422
423 for (; s != src->components.end(); ++s) { stream << "super::"; }
424 for (; d != dst->components.end(); ++d) {
425 stream << MakeSnakeCase(*d) + "::";
426 }
427 return stream.str();
428 }
429
430 // Generate a comment from the schema.
GenComment(const std::vector<std::string> & dc,const char * prefix="")431 void GenComment(const std::vector<std::string> &dc, const char *prefix = "") {
432 std::string text;
433 ::flatbuffers::GenComment(dc, &text, nullptr, prefix);
434 code_ += text + "\\";
435 }
436
437 // Return a Rust type from the table in idl.h.
GetTypeBasic(const Type & type) const438 std::string GetTypeBasic(const Type &type) const {
439 switch (GetFullType(type)) {
440 case ftInteger:
441 case ftFloat:
442 case ftBool:
443 case ftEnumKey:
444 case ftUnionKey: {
445 break;
446 }
447 default: {
448 FLATBUFFERS_ASSERT(false && "incorrect type given");
449 }
450 }
451
452 // clang-format off
453 static const char * const ctypename[] = {
454 #define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, \
455 RTYPE, ...) \
456 #RTYPE,
457 FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
458 #undef FLATBUFFERS_TD
459 };
460 // clang-format on
461
462 if (type.enum_def) { return WrapInNameSpace(*type.enum_def); }
463 return ctypename[type.base_type];
464 }
465
466 // Look up the native type for an enum. This will always be an integer like
467 // u8, i32, etc.
GetEnumTypeForDecl(const Type & type)468 std::string GetEnumTypeForDecl(const Type &type) {
469 const auto ft = GetFullType(type);
470 if (!(ft == ftEnumKey || ft == ftUnionKey)) {
471 FLATBUFFERS_ASSERT(false && "precondition failed in GetEnumTypeForDecl");
472 }
473
474 // clang-format off
475 static const char *ctypename[] = {
476 #define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, \
477 RTYPE, ...) \
478 #RTYPE,
479 FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
480 #undef FLATBUFFERS_TD
481 };
482 // clang-format on
483
484 // Enums can be bools, but their Rust representation must be a u8, as used
485 // in the repr attribute (#[repr(bool)] is an invalid attribute).
486 if (type.base_type == BASE_TYPE_BOOL) return "u8";
487 return ctypename[type.base_type];
488 }
489
490 // Return a Rust type for any type (scalar, table, struct) specifically for
491 // using a FlatBuffer.
GetTypeGet(const Type & type) const492 std::string GetTypeGet(const Type &type) const {
493 switch (GetFullType(type)) {
494 case ftInteger:
495 case ftFloat:
496 case ftBool:
497 case ftEnumKey:
498 case ftUnionKey: {
499 return GetTypeBasic(type);
500 }
501 case ftTable: {
502 return WrapInNameSpace(type.struct_def->defined_namespace,
503 type.struct_def->name) +
504 "<'a>";
505 }
506 default: {
507 return WrapInNameSpace(type.struct_def->defined_namespace,
508 type.struct_def->name);
509 }
510 }
511 }
512
GetEnumValUse(const EnumDef & enum_def,const EnumVal & enum_val) const513 std::string GetEnumValUse(const EnumDef &enum_def,
514 const EnumVal &enum_val) const {
515 return Name(enum_def) + "::" + Name(enum_val);
516 }
517
518 // Generate an enum declaration,
519 // an enum string lookup table,
520 // an enum match function,
521 // and an enum array of values
GenEnum(const EnumDef & enum_def)522 void GenEnum(const EnumDef &enum_def) {
523 code_.SetValue("ENUM_NAME", Name(enum_def));
524 code_.SetValue("BASE_TYPE", GetEnumTypeForDecl(enum_def.underlying_type));
525
526 GenComment(enum_def.doc_comment);
527 code_ += "#[allow(non_camel_case_types)]";
528 code_ += "#[repr({{BASE_TYPE}})]";
529 code_ +=
530 "#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]";
531 code_ += "pub enum " + Name(enum_def) + " {";
532
533 for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); ++it) {
534 const auto &ev = **it;
535
536 GenComment(ev.doc_comment, " ");
537 code_.SetValue("KEY", Name(ev));
538 code_.SetValue("VALUE", enum_def.ToString(ev));
539 code_ += " {{KEY}} = {{VALUE}},";
540 }
541 const EnumVal *minv = enum_def.MinValue();
542 const EnumVal *maxv = enum_def.MaxValue();
543 FLATBUFFERS_ASSERT(minv && maxv);
544
545 code_ += "";
546 code_ += "}";
547 code_ += "";
548
549 code_.SetValue("ENUM_NAME", Name(enum_def));
550 code_.SetValue("ENUM_NAME_SNAKE", MakeSnakeCase(Name(enum_def)));
551 code_.SetValue("ENUM_NAME_CAPS", MakeUpper(MakeSnakeCase(Name(enum_def))));
552 code_.SetValue("ENUM_MIN_BASE_VALUE", enum_def.ToString(*minv));
553 code_.SetValue("ENUM_MAX_BASE_VALUE", enum_def.ToString(*maxv));
554
555 // Generate enum constants, and impls for Follow, EndianScalar, and Push.
556 code_ += "pub const ENUM_MIN_{{ENUM_NAME_CAPS}}: {{BASE_TYPE}} = \\";
557 code_ += "{{ENUM_MIN_BASE_VALUE}};";
558 code_ += "pub const ENUM_MAX_{{ENUM_NAME_CAPS}}: {{BASE_TYPE}} = \\";
559 code_ += "{{ENUM_MAX_BASE_VALUE}};";
560 code_ += "";
561 code_ += "impl<'a> flatbuffers::Follow<'a> for {{ENUM_NAME}} {";
562 code_ += " type Inner = Self;";
563 code_ += " #[inline]";
564 code_ += " fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {";
565 code_ += " flatbuffers::read_scalar_at::<Self>(buf, loc)";
566 code_ += " }";
567 code_ += "}";
568 code_ += "";
569 code_ += "impl flatbuffers::EndianScalar for {{ENUM_NAME}} {";
570 code_ += " #[inline]";
571 code_ += " fn to_little_endian(self) -> Self {";
572 code_ += " let n = {{BASE_TYPE}}::to_le(self as {{BASE_TYPE}});";
573 code_ += " let p = &n as *const {{BASE_TYPE}} as *const {{ENUM_NAME}};";
574 code_ += " unsafe { *p }";
575 code_ += " }";
576 code_ += " #[inline]";
577 code_ += " fn from_little_endian(self) -> Self {";
578 code_ += " let n = {{BASE_TYPE}}::from_le(self as {{BASE_TYPE}});";
579 code_ += " let p = &n as *const {{BASE_TYPE}} as *const {{ENUM_NAME}};";
580 code_ += " unsafe { *p }";
581 code_ += " }";
582 code_ += "}";
583 code_ += "";
584 code_ += "impl flatbuffers::Push for {{ENUM_NAME}} {";
585 code_ += " type Output = {{ENUM_NAME}};";
586 code_ += " #[inline]";
587 code_ += " fn push(&self, dst: &mut [u8], _rest: &[u8]) {";
588 code_ +=
589 " flatbuffers::emplace_scalar::<{{ENUM_NAME}}>"
590 "(dst, *self);";
591 code_ += " }";
592 code_ += "}";
593 code_ += "";
594
595 // Generate an array of all enumeration values.
596 auto num_fields = NumToString(enum_def.size());
597 code_ += "#[allow(non_camel_case_types)]";
598 code_ += "pub const ENUM_VALUES_{{ENUM_NAME_CAPS}}:[{{ENUM_NAME}}; " +
599 num_fields + "] = [";
600 for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); ++it) {
601 const auto &ev = **it;
602 auto value = GetEnumValUse(enum_def, ev);
603 auto suffix = *it != enum_def.Vals().back() ? "," : "";
604 code_ += " " + value + suffix;
605 }
606 code_ += "];";
607 code_ += "";
608
609 // Generate a string table for enum values.
610 // Problem is, if values are very sparse that could generate really big
611 // tables. Ideally in that case we generate a map lookup instead, but for
612 // the moment we simply don't output a table at all.
613 auto range = enum_def.Distance();
614 // Average distance between values above which we consider a table
615 // "too sparse". Change at will.
616 static const uint64_t kMaxSparseness = 5;
617 if (range / static_cast<uint64_t>(enum_def.size()) < kMaxSparseness) {
618 code_ += "#[allow(non_camel_case_types)]";
619 code_ += "pub const ENUM_NAMES_{{ENUM_NAME_CAPS}}:[&'static str; " +
620 NumToString(range + 1) + "] = [";
621
622 auto val = enum_def.Vals().front();
623 for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end();
624 ++it) {
625 auto ev = *it;
626 for (auto k = enum_def.Distance(val, ev); k > 1; --k) {
627 code_ += " \"\",";
628 }
629 val = ev;
630 auto suffix = *it != enum_def.Vals().back() ? "," : "";
631 code_ += " \"" + Name(*ev) + "\"" + suffix;
632 }
633 code_ += "];";
634 code_ += "";
635
636 code_ +=
637 "pub fn enum_name_{{ENUM_NAME_SNAKE}}(e: {{ENUM_NAME}}) -> "
638 "&'static str {";
639
640 code_ += " let index = e as {{BASE_TYPE}}\\";
641 if (enum_def.MinValue()->IsNonZero()) {
642 auto vals = GetEnumValUse(enum_def, *enum_def.MinValue());
643 code_ += " - " + vals + " as {{BASE_TYPE}}\\";
644 }
645 code_ += ";";
646
647 code_ += " ENUM_NAMES_{{ENUM_NAME_CAPS}}[index as usize]";
648 code_ += "}";
649 code_ += "";
650 }
651
652 if (enum_def.is_union) {
653 // Generate tyoesafe offset(s) for unions
654 code_.SetValue("NAME", Name(enum_def));
655 code_.SetValue("UNION_OFFSET_NAME", Name(enum_def) + "UnionTableOffset");
656 code_ += "pub struct {{UNION_OFFSET_NAME}} {}";
657 }
658 }
659
GetFieldOffsetName(const FieldDef & field)660 std::string GetFieldOffsetName(const FieldDef &field) {
661 return "VT_" + MakeUpper(Name(field));
662 }
663
GetDefaultConstant(const FieldDef & field)664 std::string GetDefaultConstant(const FieldDef &field) {
665 return field.value.type.base_type == BASE_TYPE_FLOAT
666 ? field.value.constant + ""
667 : field.value.constant;
668 }
669
GetDefaultScalarValue(const FieldDef & field)670 std::string GetDefaultScalarValue(const FieldDef &field) {
671 switch (GetFullType(field.value.type)) {
672 case ftInteger: {
673 return GetDefaultConstant(field);
674 }
675 case ftFloat: {
676 return GetDefaultConstant(field);
677 }
678 case ftBool: {
679 return field.value.constant == "0" ? "false" : "true";
680 }
681 case ftUnionKey:
682 case ftEnumKey: {
683 auto ev = field.value.type.enum_def->FindByValue(field.value.constant);
684 assert(ev);
685 return WrapInNameSpace(field.value.type.enum_def->defined_namespace,
686 GetEnumValUse(*field.value.type.enum_def, *ev));
687 }
688
689 // All pointer-ish types have a default value of None, because they are
690 // wrapped in Option.
691 default: {
692 return "None";
693 }
694 }
695 }
696
697 // Create the return type for fields in the *BuilderArgs structs that are
698 // used to create Tables.
699 //
700 // Note: we could make all inputs to the BuilderArgs be an Option, as well
701 // as all outputs. But, the UX of Flatbuffers is that the user doesn't get to
702 // know if the value is default or not, because there are three ways to
703 // return a default value:
704 // 1) return a stored value that happens to be the default,
705 // 2) return a hardcoded value because the relevant vtable field is not in
706 // the vtable, or
707 // 3) return a hardcoded value because the vtable field value is set to zero.
TableBuilderArgsDefnType(const FieldDef & field,const std::string & lifetime)708 std::string TableBuilderArgsDefnType(const FieldDef &field,
709 const std::string &lifetime) {
710 const Type &type = field.value.type;
711
712 switch (GetFullType(type)) {
713 case ftInteger:
714 case ftFloat:
715 case ftBool: {
716 const auto typname = GetTypeBasic(type);
717 return typname;
718 }
719 case ftStruct: {
720 const auto typname = WrapInNameSpace(*type.struct_def);
721 return "Option<&" + lifetime + " " + typname + ">";
722 }
723 case ftTable: {
724 const auto typname = WrapInNameSpace(*type.struct_def);
725 return "Option<flatbuffers::WIPOffset<" + typname + "<" + lifetime +
726 ">>>";
727 }
728 case ftString: {
729 return "Option<flatbuffers::WIPOffset<&" + lifetime + " str>>";
730 }
731 case ftEnumKey:
732 case ftUnionKey: {
733 const auto typname = WrapInNameSpace(*type.enum_def);
734 return typname;
735 }
736 case ftUnionValue: {
737 return "Option<flatbuffers::WIPOffset<flatbuffers::UnionWIPOffset>>";
738 }
739
740 case ftVectorOfInteger:
741 case ftVectorOfFloat: {
742 const auto typname = GetTypeBasic(type.VectorType());
743 return "Option<flatbuffers::WIPOffset<flatbuffers::Vector<" + lifetime +
744 ", " + typname + ">>>";
745 }
746 case ftVectorOfBool: {
747 return "Option<flatbuffers::WIPOffset<flatbuffers::Vector<" + lifetime +
748 ", bool>>>";
749 }
750 case ftVectorOfEnumKey: {
751 const auto typname = WrapInNameSpace(*type.enum_def);
752 return "Option<flatbuffers::WIPOffset<flatbuffers::Vector<" + lifetime +
753 ", " + typname + ">>>";
754 }
755 case ftVectorOfStruct: {
756 const auto typname = WrapInNameSpace(*type.struct_def);
757 return "Option<flatbuffers::WIPOffset<flatbuffers::Vector<" + lifetime +
758 ", " + typname + ">>>";
759 }
760 case ftVectorOfTable: {
761 const auto typname = WrapInNameSpace(*type.struct_def);
762 return "Option<flatbuffers::WIPOffset<flatbuffers::Vector<" + lifetime +
763 ", flatbuffers::ForwardsUOffset<" + typname + "<" + lifetime +
764 ">>>>>";
765 }
766 case ftVectorOfString: {
767 return "Option<flatbuffers::WIPOffset<flatbuffers::Vector<" + lifetime +
768 ", flatbuffers::ForwardsUOffset<&" + lifetime + " str>>>>";
769 }
770 case ftVectorOfUnionValue: {
771 const auto typname =
772 WrapInNameSpace(*type.enum_def) + "UnionTableOffset";
773 return "Option<flatbuffers::WIPOffset<flatbuffers::Vector<" + lifetime +
774 ", flatbuffers::ForwardsUOffset<"
775 "flatbuffers::Table<" +
776 lifetime + ">>>>";
777 }
778 }
779 return "INVALID_CODE_GENERATION"; // for return analysis
780 }
781
TableBuilderArgsDefaultValue(const FieldDef & field)782 std::string TableBuilderArgsDefaultValue(const FieldDef &field) {
783 return GetDefaultScalarValue(field);
784 }
TableBuilderAddFuncDefaultValue(const FieldDef & field)785 std::string TableBuilderAddFuncDefaultValue(const FieldDef &field) {
786 // All branches of switch do the same action!
787 switch (GetFullType(field.value.type)) {
788 case ftUnionKey:
789 case ftEnumKey: {
790 const std::string basetype =
791 GetTypeBasic(field.value.type); //<- never used
792 return GetDefaultScalarValue(field);
793 }
794
795 default: {
796 return GetDefaultScalarValue(field);
797 }
798 }
799 }
800
TableBuilderArgsAddFuncType(const FieldDef & field,const std::string & lifetime)801 std::string TableBuilderArgsAddFuncType(const FieldDef &field,
802 const std::string &lifetime) {
803 const Type &type = field.value.type;
804
805 switch (GetFullType(field.value.type)) {
806 case ftVectorOfStruct: {
807 const auto typname = WrapInNameSpace(*type.struct_def);
808 return "flatbuffers::WIPOffset<flatbuffers::Vector<" + lifetime + ", " +
809 typname + ">>";
810 }
811 case ftVectorOfTable: {
812 const auto typname = WrapInNameSpace(*type.struct_def);
813 return "flatbuffers::WIPOffset<flatbuffers::Vector<" + lifetime +
814 ", flatbuffers::ForwardsUOffset<" + typname + "<" + lifetime +
815 ">>>>";
816 }
817 case ftVectorOfInteger:
818 case ftVectorOfFloat: {
819 const auto typname = GetTypeBasic(type.VectorType());
820 return "flatbuffers::WIPOffset<flatbuffers::Vector<" + lifetime + ", " +
821 typname + ">>";
822 }
823 case ftVectorOfBool: {
824 return "flatbuffers::WIPOffset<flatbuffers::Vector<" + lifetime +
825 ", bool>>";
826 }
827 case ftVectorOfString: {
828 return "flatbuffers::WIPOffset<flatbuffers::Vector<" + lifetime +
829 ", flatbuffers::ForwardsUOffset<&" + lifetime + " str>>>";
830 }
831 case ftVectorOfEnumKey: {
832 const auto typname = WrapInNameSpace(*type.enum_def);
833 return "flatbuffers::WIPOffset<flatbuffers::Vector<" + lifetime + ", " +
834 typname + ">>";
835 }
836 case ftVectorOfUnionValue: {
837 return "flatbuffers::WIPOffset<flatbuffers::Vector<" + lifetime +
838 ", flatbuffers::ForwardsUOffset<flatbuffers::Table<" + lifetime +
839 ">>>";
840 }
841 case ftEnumKey: {
842 const auto typname = WrapInNameSpace(*type.enum_def);
843 return typname;
844 }
845 case ftStruct: {
846 const auto typname = WrapInNameSpace(*type.struct_def);
847 return "&" + lifetime + " " + typname + "";
848 }
849 case ftTable: {
850 const auto typname = WrapInNameSpace(*type.struct_def);
851 return "flatbuffers::WIPOffset<" + typname + "<" + lifetime + ">>";
852 }
853 case ftInteger:
854 case ftFloat: {
855 const auto typname = GetTypeBasic(type);
856 return typname;
857 }
858 case ftBool: {
859 return "bool";
860 }
861 case ftString: {
862 return "flatbuffers::WIPOffset<&" + lifetime + " str>";
863 }
864 case ftUnionKey: {
865 const auto typname = WrapInNameSpace(*type.enum_def);
866 return typname;
867 }
868 case ftUnionValue: {
869 return "flatbuffers::WIPOffset<flatbuffers::UnionWIPOffset>";
870 }
871 }
872
873 return "INVALID_CODE_GENERATION"; // for return analysis
874 }
875
TableBuilderArgsAddFuncBody(const FieldDef & field)876 std::string TableBuilderArgsAddFuncBody(const FieldDef &field) {
877 const Type &type = field.value.type;
878
879 switch (GetFullType(field.value.type)) {
880 case ftInteger:
881 case ftFloat: {
882 const auto typname = GetTypeBasic(field.value.type);
883 return "self.fbb_.push_slot::<" + typname + ">";
884 }
885 case ftBool: {
886 return "self.fbb_.push_slot::<bool>";
887 }
888
889 case ftEnumKey:
890 case ftUnionKey: {
891 const auto underlying_typname = GetTypeBasic(type);
892 return "self.fbb_.push_slot::<" + underlying_typname + ">";
893 }
894
895 case ftStruct: {
896 const std::string typname = WrapInNameSpace(*type.struct_def);
897 return "self.fbb_.push_slot_always::<&" + typname + ">";
898 }
899 case ftTable: {
900 const auto typname = WrapInNameSpace(*type.struct_def);
901 return "self.fbb_.push_slot_always::<flatbuffers::WIPOffset<" +
902 typname + ">>";
903 }
904
905 case ftUnionValue:
906 case ftString:
907 case ftVectorOfInteger:
908 case ftVectorOfFloat:
909 case ftVectorOfBool:
910 case ftVectorOfEnumKey:
911 case ftVectorOfStruct:
912 case ftVectorOfTable:
913 case ftVectorOfString:
914 case ftVectorOfUnionValue: {
915 return "self.fbb_.push_slot_always::<flatbuffers::WIPOffset<_>>";
916 }
917 }
918 return "INVALID_CODE_GENERATION"; // for return analysis
919 }
920
GenTableAccessorFuncReturnType(const FieldDef & field,const std::string & lifetime)921 std::string GenTableAccessorFuncReturnType(const FieldDef &field,
922 const std::string &lifetime) {
923 const Type &type = field.value.type;
924
925 switch (GetFullType(field.value.type)) {
926 case ftInteger:
927 case ftFloat: {
928 const auto typname = GetTypeBasic(type);
929 return typname;
930 }
931 case ftBool: {
932 return "bool";
933 }
934 case ftStruct: {
935 const auto typname = WrapInNameSpace(*type.struct_def);
936 return WrapInOptionIfNotRequired("&" + lifetime + " " + typname,
937 field.required);
938 }
939 case ftTable: {
940 const auto typname = WrapInNameSpace(*type.struct_def);
941 return WrapInOptionIfNotRequired(typname + "<" + lifetime + ">",
942 field.required);
943 }
944 case ftEnumKey:
945 case ftUnionKey: {
946 const auto typname = WrapInNameSpace(*type.enum_def);
947 return typname;
948 }
949
950 case ftUnionValue: {
951 return WrapInOptionIfNotRequired("flatbuffers::Table<" + lifetime + ">",
952 field.required);
953 }
954 case ftString: {
955 return WrapInOptionIfNotRequired("&" + lifetime + " str",
956 field.required);
957 }
958 case ftVectorOfInteger:
959 case ftVectorOfFloat: {
960 const auto typname = GetTypeBasic(type.VectorType());
961 if (IsOneByte(type.VectorType().base_type)) {
962 return WrapInOptionIfNotRequired(
963 "&" + lifetime + " [" + typname + "]", field.required);
964 }
965 return WrapInOptionIfNotRequired(
966 "flatbuffers::Vector<" + lifetime + ", " + typname + ">",
967 field.required);
968 }
969 case ftVectorOfBool: {
970 return WrapInOptionIfNotRequired("&" + lifetime + " [bool]",
971 field.required);
972 }
973 case ftVectorOfEnumKey: {
974 const auto typname = WrapInNameSpace(*type.enum_def);
975 return WrapInOptionIfNotRequired(
976 "flatbuffers::Vector<" + lifetime + ", " + typname + ">",
977 field.required);
978 }
979 case ftVectorOfStruct: {
980 const auto typname = WrapInNameSpace(*type.struct_def);
981 return WrapInOptionIfNotRequired("&" + lifetime + " [" + typname + "]",
982 field.required);
983 }
984 case ftVectorOfTable: {
985 const auto typname = WrapInNameSpace(*type.struct_def);
986 return WrapInOptionIfNotRequired("flatbuffers::Vector<" + lifetime +
987 ", flatbuffers::ForwardsUOffset<" +
988 typname + "<" + lifetime + ">>>",
989 field.required);
990 }
991 case ftVectorOfString: {
992 return WrapInOptionIfNotRequired(
993 "flatbuffers::Vector<" + lifetime +
994 ", flatbuffers::ForwardsUOffset<&" + lifetime + " str>>",
995 field.required);
996 }
997 case ftVectorOfUnionValue: {
998 FLATBUFFERS_ASSERT(false && "vectors of unions are not yet supported");
999 // TODO(rw): when we do support these, we should consider using the
1000 // Into trait to convert tables to typesafe union values.
1001 return "INVALID_CODE_GENERATION"; // for return analysis
1002 }
1003 }
1004 return "INVALID_CODE_GENERATION"; // for return analysis
1005 }
1006
GenTableAccessorFuncBody(const FieldDef & field,const std::string & lifetime,const std::string & offset_prefix)1007 std::string GenTableAccessorFuncBody(const FieldDef &field,
1008 const std::string &lifetime,
1009 const std::string &offset_prefix) {
1010 const std::string offset_name =
1011 offset_prefix + "::" + GetFieldOffsetName(field);
1012 const Type &type = field.value.type;
1013
1014 switch (GetFullType(field.value.type)) {
1015 case ftInteger:
1016 case ftFloat:
1017 case ftBool: {
1018 const auto typname = GetTypeBasic(type);
1019 const auto default_value = GetDefaultScalarValue(field);
1020 return "self._tab.get::<" + typname + ">(" + offset_name + ", Some(" +
1021 default_value + ")).unwrap()";
1022 }
1023 case ftStruct: {
1024 const auto typname = WrapInNameSpace(*type.struct_def);
1025 return AddUnwrapIfRequired(
1026 "self._tab.get::<" + typname + ">(" + offset_name + ", None)",
1027 field.required);
1028 }
1029 case ftTable: {
1030 const auto typname = WrapInNameSpace(*type.struct_def);
1031 return AddUnwrapIfRequired(
1032 "self._tab.get::<flatbuffers::ForwardsUOffset<" + typname + "<" +
1033 lifetime + ">>>(" + offset_name + ", None)",
1034 field.required);
1035 }
1036 case ftUnionValue: {
1037 return AddUnwrapIfRequired(
1038 "self._tab.get::<flatbuffers::ForwardsUOffset<"
1039 "flatbuffers::Table<" +
1040 lifetime + ">>>(" + offset_name + ", None)",
1041 field.required);
1042 }
1043 case ftUnionKey:
1044 case ftEnumKey: {
1045 const auto underlying_typname = GetTypeBasic(type); //<- never used
1046 const auto typname = WrapInNameSpace(*type.enum_def);
1047 const auto default_value = GetDefaultScalarValue(field);
1048 return "self._tab.get::<" + typname + ">(" + offset_name + ", Some(" +
1049 default_value + ")).unwrap()";
1050 }
1051 case ftString: {
1052 return AddUnwrapIfRequired(
1053 "self._tab.get::<flatbuffers::ForwardsUOffset<&str>>(" +
1054 offset_name + ", None)",
1055 field.required);
1056 }
1057
1058 case ftVectorOfInteger:
1059 case ftVectorOfFloat: {
1060 const auto typname = GetTypeBasic(type.VectorType());
1061 std::string s =
1062 "self._tab.get::<flatbuffers::ForwardsUOffset<"
1063 "flatbuffers::Vector<" +
1064 lifetime + ", " + typname + ">>>(" + offset_name + ", None)";
1065 // single-byte values are safe to slice
1066 if (IsOneByte(type.VectorType().base_type)) {
1067 s += ".map(|v| v.safe_slice())";
1068 }
1069 return AddUnwrapIfRequired(s, field.required);
1070 }
1071 case ftVectorOfBool: {
1072 return AddUnwrapIfRequired(
1073 "self._tab.get::<flatbuffers::ForwardsUOffset<"
1074 "flatbuffers::Vector<" +
1075 lifetime + ", bool>>>(" + offset_name +
1076 ", None).map(|v| v.safe_slice())",
1077 field.required);
1078 }
1079 case ftVectorOfEnumKey: {
1080 const auto typname = WrapInNameSpace(*type.enum_def);
1081 return AddUnwrapIfRequired(
1082 "self._tab.get::<flatbuffers::ForwardsUOffset<"
1083 "flatbuffers::Vector<" +
1084 lifetime + ", " + typname + ">>>(" + offset_name + ", None)",
1085 field.required);
1086 }
1087 case ftVectorOfStruct: {
1088 const auto typname = WrapInNameSpace(*type.struct_def);
1089 return AddUnwrapIfRequired(
1090 "self._tab.get::<flatbuffers::ForwardsUOffset<"
1091 "flatbuffers::Vector<" +
1092 typname + ">>>(" + offset_name +
1093 ", None).map(|v| v.safe_slice() )",
1094 field.required);
1095 }
1096 case ftVectorOfTable: {
1097 const auto typname = WrapInNameSpace(*type.struct_def);
1098 return AddUnwrapIfRequired(
1099 "self._tab.get::<flatbuffers::ForwardsUOffset<"
1100 "flatbuffers::Vector<flatbuffers::ForwardsUOffset<" +
1101 typname + "<" + lifetime + ">>>>>(" + offset_name + ", None)",
1102 field.required);
1103 }
1104 case ftVectorOfString: {
1105 return AddUnwrapIfRequired(
1106 "self._tab.get::<flatbuffers::ForwardsUOffset<"
1107 "flatbuffers::Vector<flatbuffers::ForwardsUOffset<&" +
1108 lifetime + " str>>>>(" + offset_name + ", None)",
1109 field.required);
1110 }
1111 case ftVectorOfUnionValue: {
1112 FLATBUFFERS_ASSERT(false && "vectors of unions are not yet supported");
1113 return "INVALID_CODE_GENERATION"; // for return analysis
1114 }
1115 }
1116 return "INVALID_CODE_GENERATION"; // for return analysis
1117 }
1118
TableFieldReturnsOption(const Type & type)1119 bool TableFieldReturnsOption(const Type &type) {
1120 switch (GetFullType(type)) {
1121 case ftInteger:
1122 case ftFloat:
1123 case ftBool:
1124 case ftEnumKey:
1125 case ftUnionKey: return false;
1126 default: return true;
1127 }
1128 }
1129
1130 // Generates a fully-qualified name getter for use with --gen-name-strings
GenFullyQualifiedNameGetter(const StructDef & struct_def,const std::string & name)1131 void GenFullyQualifiedNameGetter(const StructDef &struct_def,
1132 const std::string &name) {
1133 code_ += " pub const fn get_fully_qualified_name() -> &'static str {";
1134 code_ += " \"" +
1135 struct_def.defined_namespace->GetFullyQualifiedName(name) + "\"";
1136 code_ += " }";
1137 code_ += "";
1138 }
1139
1140 // Generate an accessor struct, builder struct, and create function for a
1141 // table.
GenTable(const StructDef & struct_def)1142 void GenTable(const StructDef &struct_def) {
1143 code_.SetValue("STRUCT_NAME", Name(struct_def));
1144 code_.SetValue("OFFSET_TYPELABEL", Name(struct_def) + "Offset");
1145 code_.SetValue("STRUCT_NAME_SNAKECASE", MakeSnakeCase(Name(struct_def)));
1146
1147 // Generate an offset type, the base type, the Follow impl, and the
1148 // init_from_table impl.
1149 code_ += "pub enum {{OFFSET_TYPELABEL}} {}";
1150 code_ += "#[derive(Copy, Clone, Debug, PartialEq)]";
1151 code_ += "";
1152
1153 GenComment(struct_def.doc_comment);
1154
1155 code_ += "pub struct {{STRUCT_NAME}}<'a> {";
1156 code_ += " pub _tab: flatbuffers::Table<'a>,";
1157 code_ += "}";
1158 code_ += "";
1159 code_ += "impl<'a> flatbuffers::Follow<'a> for {{STRUCT_NAME}}<'a> {";
1160 code_ += " type Inner = {{STRUCT_NAME}}<'a>;";
1161 code_ += " #[inline]";
1162 code_ += " fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {";
1163 code_ += " Self {";
1164 code_ += " _tab: flatbuffers::Table { buf: buf, loc: loc },";
1165 code_ += " }";
1166 code_ += " }";
1167 code_ += "}";
1168 code_ += "";
1169 code_ += "impl<'a> {{STRUCT_NAME}}<'a> {";
1170
1171 if (parser_.opts.generate_name_strings) {
1172 GenFullyQualifiedNameGetter(struct_def, struct_def.name);
1173 }
1174
1175 code_ += " #[inline]";
1176 code_ +=
1177 " pub fn init_from_table(table: flatbuffers::Table<'a>) -> "
1178 "Self {";
1179 code_ += " {{STRUCT_NAME}} {";
1180 code_ += " _tab: table,";
1181 code_ += " }";
1182 code_ += " }";
1183
1184 // Generate a convenient create* function that uses the above builder
1185 // to create a table in one function call.
1186 code_.SetValue("MAYBE_US", struct_def.fields.vec.size() == 0 ? "_" : "");
1187 code_.SetValue("MAYBE_LT",
1188 TableBuilderArgsNeedsLifetime(struct_def) ? "<'args>" : "");
1189 code_ += " #[allow(unused_mut)]";
1190 code_ += " pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr>(";
1191 code_ +=
1192 " _fbb: "
1193 "&'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr>,";
1194 code_ +=
1195 " {{MAYBE_US}}args: &'args {{STRUCT_NAME}}Args{{MAYBE_LT}})"
1196 " -> flatbuffers::WIPOffset<{{STRUCT_NAME}}<'bldr>> {";
1197
1198 code_ += " let mut builder = {{STRUCT_NAME}}Builder::new(_fbb);";
1199 for (size_t size = struct_def.sortbysize ? sizeof(largest_scalar_t) : 1;
1200 size; size /= 2) {
1201 for (auto it = struct_def.fields.vec.rbegin();
1202 it != struct_def.fields.vec.rend(); ++it) {
1203 const auto &field = **it;
1204 // TODO(rw): fully understand this sortbysize usage
1205 if (!field.deprecated && (!struct_def.sortbysize ||
1206 size == SizeOf(field.value.type.base_type))) {
1207 code_.SetValue("FIELD_NAME", Name(field));
1208 if (TableFieldReturnsOption(field.value.type)) {
1209 code_ +=
1210 " if let Some(x) = args.{{FIELD_NAME}} "
1211 "{ builder.add_{{FIELD_NAME}}(x); }";
1212 } else {
1213 code_ += " builder.add_{{FIELD_NAME}}(args.{{FIELD_NAME}});";
1214 }
1215 }
1216 }
1217 }
1218 code_ += " builder.finish()";
1219 code_ += " }";
1220 code_ += "";
1221
1222 // Generate field id constants.
1223 if (struct_def.fields.vec.size() > 0) {
1224 for (auto it = struct_def.fields.vec.begin();
1225 it != struct_def.fields.vec.end(); ++it) {
1226 const auto &field = **it;
1227 if (field.deprecated) {
1228 // Deprecated fields won't be accessible.
1229 continue;
1230 }
1231
1232 code_.SetValue("OFFSET_NAME", GetFieldOffsetName(field));
1233 code_.SetValue("OFFSET_VALUE", NumToString(field.value.offset));
1234 code_ +=
1235 " pub const {{OFFSET_NAME}}: flatbuffers::VOffsetT = "
1236 "{{OFFSET_VALUE}};";
1237 }
1238 code_ += "";
1239 }
1240
1241 // Generate the accessors. Each has one of two forms:
1242 //
1243 // If a value can be None:
1244 // pub fn name(&'a self) -> Option<user_facing_type> {
1245 // self._tab.get::<internal_type>(offset, defaultval)
1246 // }
1247 //
1248 // If a value is always Some:
1249 // pub fn name(&'a self) -> user_facing_type {
1250 // self._tab.get::<internal_type>(offset, defaultval).unwrap()
1251 // }
1252 const auto offset_prefix = Name(struct_def);
1253 for (auto it = struct_def.fields.vec.begin();
1254 it != struct_def.fields.vec.end(); ++it) {
1255 const auto &field = **it;
1256 if (field.deprecated) {
1257 // Deprecated fields won't be accessible.
1258 continue;
1259 }
1260
1261 code_.SetValue("FIELD_NAME", Name(field));
1262 code_.SetValue("RETURN_TYPE",
1263 GenTableAccessorFuncReturnType(field, "'a"));
1264 code_.SetValue("FUNC_BODY",
1265 GenTableAccessorFuncBody(field, "'a", offset_prefix));
1266
1267 GenComment(field.doc_comment, " ");
1268 code_ += " #[inline]";
1269 code_ += " pub fn {{FIELD_NAME}}(&self) -> {{RETURN_TYPE}} {";
1270 code_ += " {{FUNC_BODY}}";
1271 code_ += " }";
1272
1273 // Generate a comparison function for this field if it is a key.
1274 if (field.key) { GenKeyFieldMethods(field); }
1275
1276 // Generate a nested flatbuffer field, if applicable.
1277 auto nested = field.attributes.Lookup("nested_flatbuffer");
1278 if (nested) {
1279 std::string qualified_name = nested->constant;
1280 auto nested_root = parser_.LookupStruct(nested->constant);
1281 if (nested_root == nullptr) {
1282 qualified_name = parser_.current_namespace_->GetFullyQualifiedName(
1283 nested->constant);
1284 nested_root = parser_.LookupStruct(qualified_name);
1285 }
1286 FLATBUFFERS_ASSERT(nested_root); // Guaranteed to exist by parser.
1287 (void)nested_root;
1288
1289 code_.SetValue("OFFSET_NAME",
1290 offset_prefix + "::" + GetFieldOffsetName(field));
1291 code_ +=
1292 " pub fn {{FIELD_NAME}}_nested_flatbuffer(&'a self) -> "
1293 " Option<{{STRUCT_NAME}}<'a>> {";
1294 code_ += " match self.{{FIELD_NAME}}() {";
1295 code_ += " None => { None }";
1296 code_ += " Some(data) => {";
1297 code_ += " use self::flatbuffers::Follow;";
1298 code_ +=
1299 " Some(<flatbuffers::ForwardsUOffset"
1300 "<{{STRUCT_NAME}}<'a>>>::follow(data, 0))";
1301 code_ += " },";
1302 code_ += " }";
1303 code_ += " }";
1304 }
1305 }
1306
1307 // Explicit specializations for union accessors
1308 for (auto it = struct_def.fields.vec.begin();
1309 it != struct_def.fields.vec.end(); ++it) {
1310 const auto &field = **it;
1311 if (field.deprecated || field.value.type.base_type != BASE_TYPE_UNION) {
1312 continue;
1313 }
1314
1315 auto u = field.value.type.enum_def;
1316
1317 code_.SetValue("FIELD_NAME", Name(field));
1318 code_.SetValue("FIELD_TYPE_FIELD_NAME", field.name);
1319
1320 for (auto u_it = u->Vals().begin(); u_it != u->Vals().end(); ++u_it) {
1321 auto &ev = **u_it;
1322 if (ev.union_type.base_type == BASE_TYPE_NONE) { continue; }
1323
1324 auto table_init_type =
1325 WrapInNameSpace(ev.union_type.struct_def->defined_namespace,
1326 ev.union_type.struct_def->name);
1327
1328 code_.SetValue(
1329 "U_ELEMENT_ENUM_TYPE",
1330 WrapInNameSpace(u->defined_namespace, GetEnumValUse(*u, ev)));
1331 code_.SetValue("U_ELEMENT_TABLE_TYPE", table_init_type);
1332 code_.SetValue("U_ELEMENT_NAME", MakeSnakeCase(Name(ev)));
1333
1334 code_ += " #[inline]";
1335 code_ += " #[allow(non_snake_case)]";
1336 code_ +=
1337 " pub fn {{FIELD_NAME}}_as_{{U_ELEMENT_NAME}}(&self) -> "
1338 "Option<{{U_ELEMENT_TABLE_TYPE}}<'a>> {";
1339 // If the user defined schemas name a field that clashes with a
1340 // language reserved word, flatc will try to escape the field name by
1341 // appending an underscore. This works well for most cases, except
1342 // one. When generating union accessors (and referring to them
1343 // internally within the code generated here), an extra underscore
1344 // will be appended to the name, causing build failures.
1345 //
1346 // This only happens when unions have members that overlap with
1347 // language reserved words.
1348 //
1349 // To avoid this problem the type field name is used unescaped here:
1350 code_ +=
1351 " if self.{{FIELD_TYPE_FIELD_NAME}}_type() == "
1352 "{{U_ELEMENT_ENUM_TYPE}} {";
1353 code_ +=
1354 " self.{{FIELD_NAME}}().map(|u| "
1355 "{{U_ELEMENT_TABLE_TYPE}}::init_from_table(u))";
1356 code_ += " } else {";
1357 code_ += " None";
1358 code_ += " }";
1359 code_ += " }";
1360 code_ += "";
1361 }
1362 }
1363
1364 code_ += "}"; // End of table impl.
1365 code_ += "";
1366
1367 // Generate an args struct:
1368 code_.SetValue("MAYBE_LT",
1369 TableBuilderArgsNeedsLifetime(struct_def) ? "<'a>" : "");
1370 code_ += "pub struct {{STRUCT_NAME}}Args{{MAYBE_LT}} {";
1371 for (auto it = struct_def.fields.vec.begin();
1372 it != struct_def.fields.vec.end(); ++it) {
1373 const auto &field = **it;
1374 if (!field.deprecated) {
1375 code_.SetValue("PARAM_NAME", Name(field));
1376 code_.SetValue("PARAM_TYPE", TableBuilderArgsDefnType(field, "'a "));
1377 code_ += " pub {{PARAM_NAME}}: {{PARAM_TYPE}},";
1378 }
1379 }
1380 code_ += "}";
1381
1382 // Generate an impl of Default for the *Args type:
1383 code_ += "impl<'a> Default for {{STRUCT_NAME}}Args{{MAYBE_LT}} {";
1384 code_ += " #[inline]";
1385 code_ += " fn default() -> Self {";
1386 code_ += " {{STRUCT_NAME}}Args {";
1387 for (auto it = struct_def.fields.vec.begin();
1388 it != struct_def.fields.vec.end(); ++it) {
1389 const auto &field = **it;
1390 if (!field.deprecated) {
1391 code_.SetValue("PARAM_VALUE", TableBuilderArgsDefaultValue(field));
1392 code_.SetValue("REQ", field.required ? " // required field" : "");
1393 code_.SetValue("PARAM_NAME", Name(field));
1394 code_ += " {{PARAM_NAME}}: {{PARAM_VALUE}},{{REQ}}";
1395 }
1396 }
1397 code_ += " }";
1398 code_ += " }";
1399 code_ += "}";
1400
1401 // Generate a builder struct:
1402 code_ += "pub struct {{STRUCT_NAME}}Builder<'a: 'b, 'b> {";
1403 code_ += " fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a>,";
1404 code_ +=
1405 " start_: flatbuffers::WIPOffset<"
1406 "flatbuffers::TableUnfinishedWIPOffset>,";
1407 code_ += "}";
1408
1409 // Generate builder functions:
1410 code_ += "impl<'a: 'b, 'b> {{STRUCT_NAME}}Builder<'a, 'b> {";
1411 for (auto it = struct_def.fields.vec.begin();
1412 it != struct_def.fields.vec.end(); ++it) {
1413 const auto &field = **it;
1414 if (!field.deprecated) {
1415 const bool is_scalar = IsScalar(field.value.type.base_type);
1416
1417 std::string offset = GetFieldOffsetName(field);
1418
1419 // Generate functions to add data, which take one of two forms.
1420 //
1421 // If a value has a default:
1422 // fn add_x(x_: type) {
1423 // fbb_.push_slot::<type>(offset, x_, Some(default));
1424 // }
1425 //
1426 // If a value does not have a default:
1427 // fn add_x(x_: type) {
1428 // fbb_.push_slot_always::<type>(offset, x_);
1429 // }
1430 code_.SetValue("FIELD_NAME", Name(field));
1431 code_.SetValue("FIELD_OFFSET", Name(struct_def) + "::" + offset);
1432 code_.SetValue("FIELD_TYPE", TableBuilderArgsAddFuncType(field, "'b "));
1433 code_.SetValue("FUNC_BODY", TableBuilderArgsAddFuncBody(field));
1434 code_ += " #[inline]";
1435 code_ +=
1436 " pub fn add_{{FIELD_NAME}}(&mut self, {{FIELD_NAME}}: "
1437 "{{FIELD_TYPE}}) {";
1438 if (is_scalar) {
1439 code_.SetValue("FIELD_DEFAULT_VALUE",
1440 TableBuilderAddFuncDefaultValue(field));
1441 code_ +=
1442 " {{FUNC_BODY}}({{FIELD_OFFSET}}, {{FIELD_NAME}}, "
1443 "{{FIELD_DEFAULT_VALUE}});";
1444 } else {
1445 code_ += " {{FUNC_BODY}}({{FIELD_OFFSET}}, {{FIELD_NAME}});";
1446 }
1447 code_ += " }";
1448 }
1449 }
1450
1451 // Struct initializer (all fields required);
1452 code_ += " #[inline]";
1453 code_ +=
1454 " pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a>) -> "
1455 "{{STRUCT_NAME}}Builder<'a, 'b> {";
1456 code_.SetValue("NUM_FIELDS", NumToString(struct_def.fields.vec.size()));
1457 code_ += " let start = _fbb.start_table();";
1458 code_ += " {{STRUCT_NAME}}Builder {";
1459 code_ += " fbb_: _fbb,";
1460 code_ += " start_: start,";
1461 code_ += " }";
1462 code_ += " }";
1463
1464 // finish() function.
1465 code_ += " #[inline]";
1466 code_ +=
1467 " pub fn finish(self) -> "
1468 "flatbuffers::WIPOffset<{{STRUCT_NAME}}<'a>> {";
1469 code_ += " let o = self.fbb_.end_table(self.start_);";
1470
1471 for (auto it = struct_def.fields.vec.begin();
1472 it != struct_def.fields.vec.end(); ++it) {
1473 const auto &field = **it;
1474 if (!field.deprecated && field.required) {
1475 code_.SetValue("FIELD_NAME", MakeSnakeCase(Name(field)));
1476 code_.SetValue("OFFSET_NAME", GetFieldOffsetName(field));
1477 code_ +=
1478 " self.fbb_.required(o, {{STRUCT_NAME}}::{{OFFSET_NAME}},"
1479 "\"{{FIELD_NAME}}\");";
1480 }
1481 }
1482 code_ += " flatbuffers::WIPOffset::new(o.value())";
1483 code_ += " }";
1484 code_ += "}";
1485 code_ += "";
1486 }
1487
1488 // Generate functions to compare tables and structs by key. This function
1489 // must only be called if the field key is defined.
GenKeyFieldMethods(const FieldDef & field)1490 void GenKeyFieldMethods(const FieldDef &field) {
1491 FLATBUFFERS_ASSERT(field.key);
1492
1493 code_.SetValue("KEY_TYPE", GenTableAccessorFuncReturnType(field, ""));
1494
1495 code_ += " #[inline]";
1496 code_ +=
1497 " pub fn key_compare_less_than(&self, o: &{{STRUCT_NAME}}) -> "
1498 " bool {";
1499 code_ += " self.{{FIELD_NAME}}() < o.{{FIELD_NAME}}()";
1500 code_ += " }";
1501 code_ += "";
1502 code_ += " #[inline]";
1503 code_ +=
1504 " pub fn key_compare_with_value(&self, val: {{KEY_TYPE}}) -> "
1505 " ::std::cmp::Ordering {";
1506 code_ += " let key = self.{{FIELD_NAME}}();";
1507 code_ += " key.cmp(&val)";
1508 code_ += " }";
1509 }
1510
1511 // Generate functions for accessing the root table object. This function
1512 // must only be called if the root table is defined.
GenRootTableFuncs(const StructDef & struct_def)1513 void GenRootTableFuncs(const StructDef &struct_def) {
1514 FLATBUFFERS_ASSERT(parser_.root_struct_def_ && "root table not defined");
1515 auto name = Name(struct_def);
1516
1517 code_.SetValue("STRUCT_NAME", name);
1518 code_.SetValue("STRUCT_NAME_SNAKECASE", MakeSnakeCase(name));
1519 code_.SetValue("STRUCT_NAME_CAPS", MakeUpper(MakeSnakeCase(name)));
1520
1521 // The root datatype accessors:
1522 code_ += "#[inline]";
1523 code_ +=
1524 "pub fn get_root_as_{{STRUCT_NAME_SNAKECASE}}<'a>(buf: &'a [u8])"
1525 " -> {{STRUCT_NAME}}<'a> {";
1526 code_ += " flatbuffers::get_root::<{{STRUCT_NAME}}<'a>>(buf)";
1527 code_ += "}";
1528 code_ += "";
1529
1530 code_ += "#[inline]";
1531 code_ +=
1532 "pub fn get_size_prefixed_root_as_{{STRUCT_NAME_SNAKECASE}}"
1533 "<'a>(buf: &'a [u8]) -> {{STRUCT_NAME}}<'a> {";
1534 code_ +=
1535 " flatbuffers::get_size_prefixed_root::<{{STRUCT_NAME}}<'a>>"
1536 "(buf)";
1537 code_ += "}";
1538 code_ += "";
1539
1540 if (parser_.file_identifier_.length()) {
1541 // Declare the identifier
1542 code_ += "pub const {{STRUCT_NAME_CAPS}}_IDENTIFIER: &'static str\\";
1543 code_ += " = \"" + parser_.file_identifier_ + "\";";
1544 code_ += "";
1545
1546 // Check if a buffer has the identifier.
1547 code_ += "#[inline]";
1548 code_ += "pub fn {{STRUCT_NAME_SNAKECASE}}_buffer_has_identifier\\";
1549 code_ += "(buf: &[u8]) -> bool {";
1550 code_ += " return flatbuffers::buffer_has_identifier(buf, \\";
1551 code_ += "{{STRUCT_NAME_CAPS}}_IDENTIFIER, false);";
1552 code_ += "}";
1553 code_ += "";
1554 code_ += "#[inline]";
1555 code_ += "pub fn {{STRUCT_NAME_SNAKECASE}}_size_prefixed\\";
1556 code_ += "_buffer_has_identifier(buf: &[u8]) -> bool {";
1557 code_ += " return flatbuffers::buffer_has_identifier(buf, \\";
1558 code_ += "{{STRUCT_NAME_CAPS}}_IDENTIFIER, true);";
1559 code_ += "}";
1560 code_ += "";
1561 }
1562
1563 if (parser_.file_extension_.length()) {
1564 // Return the extension
1565 code_ += "pub const {{STRUCT_NAME_CAPS}}_EXTENSION: &'static str = \\";
1566 code_ += "\"" + parser_.file_extension_ + "\";";
1567 code_ += "";
1568 }
1569
1570 // Finish a buffer with a given root object:
1571 code_.SetValue("OFFSET_TYPELABEL", Name(struct_def) + "Offset");
1572 code_ += "#[inline]";
1573 code_ += "pub fn finish_{{STRUCT_NAME_SNAKECASE}}_buffer<'a, 'b>(";
1574 code_ += " fbb: &'b mut flatbuffers::FlatBufferBuilder<'a>,";
1575 code_ += " root: flatbuffers::WIPOffset<{{STRUCT_NAME}}<'a>>) {";
1576 if (parser_.file_identifier_.length()) {
1577 code_ += " fbb.finish(root, Some({{STRUCT_NAME_CAPS}}_IDENTIFIER));";
1578 } else {
1579 code_ += " fbb.finish(root, None);";
1580 }
1581 code_ += "}";
1582 code_ += "";
1583 code_ += "#[inline]";
1584 code_ +=
1585 "pub fn finish_size_prefixed_{{STRUCT_NAME_SNAKECASE}}_buffer"
1586 "<'a, 'b>("
1587 "fbb: &'b mut flatbuffers::FlatBufferBuilder<'a>, "
1588 "root: flatbuffers::WIPOffset<{{STRUCT_NAME}}<'a>>) {";
1589 if (parser_.file_identifier_.length()) {
1590 code_ +=
1591 " fbb.finish_size_prefixed(root, "
1592 "Some({{STRUCT_NAME_CAPS}}_IDENTIFIER));";
1593 } else {
1594 code_ += " fbb.finish_size_prefixed(root, None);";
1595 }
1596 code_ += "}";
1597 }
1598
GenPadding(const FieldDef & field,std::string * code_ptr,int * id,const std::function<void (int bits,std::string * code_ptr,int * id)> & f)1599 static void GenPadding(
1600 const FieldDef &field, std::string *code_ptr, int *id,
1601 const std::function<void(int bits, std::string *code_ptr, int *id)> &f) {
1602 if (field.padding) {
1603 for (int i = 0; i < 4; i++) {
1604 if (static_cast<int>(field.padding) & (1 << i)) {
1605 f((1 << i) * 8, code_ptr, id);
1606 }
1607 }
1608 assert(!(field.padding & ~0xF));
1609 }
1610 }
1611
PaddingDefinition(int bits,std::string * code_ptr,int * id)1612 static void PaddingDefinition(int bits, std::string *code_ptr, int *id) {
1613 *code_ptr +=
1614 " padding" + NumToString((*id)++) + "__: u" + NumToString(bits) + ",";
1615 }
1616
PaddingInitializer(int bits,std::string * code_ptr,int * id)1617 static void PaddingInitializer(int bits, std::string *code_ptr, int *id) {
1618 (void)bits;
1619 *code_ptr += "padding" + NumToString((*id)++) + "__: 0,";
1620 }
1621
1622 // Generate an accessor struct with constructor for a flatbuffers struct.
GenStruct(const StructDef & struct_def)1623 void GenStruct(const StructDef &struct_def) {
1624 // Generates manual padding and alignment.
1625 // Variables are private because they contain little endian data on all
1626 // platforms.
1627 GenComment(struct_def.doc_comment);
1628 code_.SetValue("ALIGN", NumToString(struct_def.minalign));
1629 code_.SetValue("STRUCT_NAME", Name(struct_def));
1630
1631 code_ += "// struct {{STRUCT_NAME}}, aligned to {{ALIGN}}";
1632 code_ += "#[repr(C, align({{ALIGN}}))]";
1633
1634 // PartialEq is useful to derive because we can correctly compare structs
1635 // for equality by just comparing their underlying byte data. This doesn't
1636 // hold for PartialOrd/Ord.
1637 code_ += "#[derive(Clone, Copy, Debug, PartialEq)]";
1638 code_ += "pub struct {{STRUCT_NAME}} {";
1639
1640 int padding_id = 0;
1641 for (auto it = struct_def.fields.vec.begin();
1642 it != struct_def.fields.vec.end(); ++it) {
1643 const auto &field = **it;
1644 code_.SetValue("FIELD_TYPE", GetTypeGet(field.value.type));
1645 code_.SetValue("FIELD_NAME", Name(field));
1646 code_ += " {{FIELD_NAME}}_: {{FIELD_TYPE}},";
1647
1648 if (field.padding) {
1649 std::string padding;
1650 GenPadding(field, &padding, &padding_id, PaddingDefinition);
1651 code_ += padding;
1652 }
1653 }
1654
1655 code_ += "} // pub struct {{STRUCT_NAME}}";
1656
1657 // Generate impls for SafeSliceAccess (because all structs are endian-safe),
1658 // Follow for the value type, Follow for the reference type, Push for the
1659 // value type, and Push for the reference type.
1660 code_ += "impl flatbuffers::SafeSliceAccess for {{STRUCT_NAME}} {}";
1661 code_ += "impl<'a> flatbuffers::Follow<'a> for {{STRUCT_NAME}} {";
1662 code_ += " type Inner = &'a {{STRUCT_NAME}};";
1663 code_ += " #[inline]";
1664 code_ += " fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {";
1665 code_ += " <&'a {{STRUCT_NAME}}>::follow(buf, loc)";
1666 code_ += " }";
1667 code_ += "}";
1668 code_ += "impl<'a> flatbuffers::Follow<'a> for &'a {{STRUCT_NAME}} {";
1669 code_ += " type Inner = &'a {{STRUCT_NAME}};";
1670 code_ += " #[inline]";
1671 code_ += " fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {";
1672 code_ += " flatbuffers::follow_cast_ref::<{{STRUCT_NAME}}>(buf, loc)";
1673 code_ += " }";
1674 code_ += "}";
1675 code_ += "impl<'b> flatbuffers::Push for {{STRUCT_NAME}} {";
1676 code_ += " type Output = {{STRUCT_NAME}};";
1677 code_ += " #[inline]";
1678 code_ += " fn push(&self, dst: &mut [u8], _rest: &[u8]) {";
1679 code_ += " let src = unsafe {";
1680 code_ +=
1681 " ::std::slice::from_raw_parts("
1682 "self as *const {{STRUCT_NAME}} as *const u8, Self::size())";
1683 code_ += " };";
1684 code_ += " dst.copy_from_slice(src);";
1685 code_ += " }";
1686 code_ += "}";
1687 code_ += "impl<'b> flatbuffers::Push for &'b {{STRUCT_NAME}} {";
1688 code_ += " type Output = {{STRUCT_NAME}};";
1689 code_ += "";
1690 code_ += " #[inline]";
1691 code_ += " fn push(&self, dst: &mut [u8], _rest: &[u8]) {";
1692 code_ += " let src = unsafe {";
1693 code_ +=
1694 " ::std::slice::from_raw_parts("
1695 "*self as *const {{STRUCT_NAME}} as *const u8, Self::size())";
1696 code_ += " };";
1697 code_ += " dst.copy_from_slice(src);";
1698 code_ += " }";
1699 code_ += "}";
1700 code_ += "";
1701 code_ += "";
1702
1703 // Generate a constructor that takes all fields as arguments.
1704 code_ += "impl {{STRUCT_NAME}} {";
1705 std::string arg_list;
1706 std::string init_list;
1707 padding_id = 0;
1708 for (auto it = struct_def.fields.vec.begin();
1709 it != struct_def.fields.vec.end(); ++it) {
1710 const auto &field = **it;
1711 const auto member_name = Name(field) + "_";
1712 const auto reference =
1713 StructMemberAccessNeedsCopy(field.value.type) ? "" : "&'a ";
1714 const auto arg_name = "_" + Name(field);
1715 const auto arg_type = reference + GetTypeGet(field.value.type);
1716
1717 if (it != struct_def.fields.vec.begin()) { arg_list += ", "; }
1718 arg_list += arg_name + ": ";
1719 arg_list += arg_type;
1720 init_list += " " + member_name;
1721 if (StructMemberAccessNeedsCopy(field.value.type)) {
1722 init_list += ": " + arg_name + ".to_little_endian(),\n";
1723 } else {
1724 init_list += ": *" + arg_name + ",\n";
1725 }
1726 }
1727
1728 code_.SetValue("ARG_LIST", arg_list);
1729 code_.SetValue("INIT_LIST", init_list);
1730 code_ += " pub fn new<'a>({{ARG_LIST}}) -> Self {";
1731 code_ += " {{STRUCT_NAME}} {";
1732 code_ += "{{INIT_LIST}}";
1733 padding_id = 0;
1734 for (auto it = struct_def.fields.vec.begin();
1735 it != struct_def.fields.vec.end(); ++it) {
1736 const auto &field = **it;
1737 if (field.padding) {
1738 std::string padding;
1739 GenPadding(field, &padding, &padding_id, PaddingInitializer);
1740 code_ += " " + padding;
1741 }
1742 }
1743 code_ += " }";
1744 code_ += " }";
1745
1746 if (parser_.opts.generate_name_strings) {
1747 GenFullyQualifiedNameGetter(struct_def, struct_def.name);
1748 }
1749
1750 // Generate accessor methods for the struct.
1751 for (auto it = struct_def.fields.vec.begin();
1752 it != struct_def.fields.vec.end(); ++it) {
1753 const auto &field = **it;
1754
1755 auto field_type = TableBuilderArgsAddFuncType(field, "'a");
1756 auto member = "self." + Name(field) + "_";
1757 auto value = StructMemberAccessNeedsCopy(field.value.type)
1758 ? member + ".from_little_endian()"
1759 : member;
1760
1761 code_.SetValue("FIELD_NAME", Name(field));
1762 code_.SetValue("FIELD_TYPE", field_type);
1763 code_.SetValue("FIELD_VALUE", value);
1764 code_.SetValue("REF", IsStruct(field.value.type) ? "&" : "");
1765
1766 GenComment(field.doc_comment, " ");
1767 code_ += " pub fn {{FIELD_NAME}}<'a>(&'a self) -> {{FIELD_TYPE}} {";
1768 code_ += " {{REF}}{{FIELD_VALUE}}";
1769 code_ += " }";
1770
1771 // Generate a comparison function for this field if it is a key.
1772 if (field.key) { GenKeyFieldMethods(field); }
1773 }
1774 code_ += "}";
1775 code_ += "";
1776 }
1777
GenNamespaceImports(const int white_spaces)1778 void GenNamespaceImports(const int white_spaces) {
1779 std::string indent = std::string(white_spaces, ' ');
1780 code_ += "";
1781 for (auto it = parser_.included_files_.begin();
1782 it != parser_.included_files_.end(); ++it) {
1783 if (it->second.empty()) continue;
1784 auto noext = flatbuffers::StripExtension(it->second);
1785 auto basename = flatbuffers::StripPath(noext);
1786
1787 code_ += indent + "use crate::" + basename + "_generated::*;";
1788 }
1789 code_ += indent + "use std::mem;";
1790 code_ += indent + "use std::cmp::Ordering;";
1791 code_ += "";
1792 code_ += indent + "extern crate flatbuffers;";
1793 code_ += indent + "use self::flatbuffers::EndianScalar;";
1794 }
1795
1796 // Set up the correct namespace. This opens a namespace if the current
1797 // namespace is different from the target namespace. This function
1798 // closes and opens the namespaces only as necessary.
1799 //
1800 // The file must start and end with an empty (or null) namespace so that
1801 // namespaces are properly opened and closed.
SetNameSpace(const Namespace * ns)1802 void SetNameSpace(const Namespace *ns) {
1803 if (cur_name_space_ == ns) { return; }
1804
1805 // Compute the size of the longest common namespace prefix.
1806 // If cur_name_space is A::B::C::D and ns is A::B::E::F::G,
1807 // the common prefix is A::B:: and we have old_size = 4, new_size = 5
1808 // and common_prefix_size = 2
1809 size_t old_size = cur_name_space_ ? cur_name_space_->components.size() : 0;
1810 size_t new_size = ns ? ns->components.size() : 0;
1811
1812 size_t common_prefix_size = 0;
1813 while (common_prefix_size < old_size && common_prefix_size < new_size &&
1814 ns->components[common_prefix_size] ==
1815 cur_name_space_->components[common_prefix_size]) {
1816 common_prefix_size++;
1817 }
1818
1819 // Close cur_name_space in reverse order to reach the common prefix.
1820 // In the previous example, D then C are closed.
1821 for (size_t j = old_size; j > common_prefix_size; --j) {
1822 code_ += "} // pub mod " + cur_name_space_->components[j - 1];
1823 }
1824 if (old_size != common_prefix_size) { code_ += ""; }
1825
1826 // open namespace parts to reach the ns namespace
1827 // in the previous example, E, then F, then G are opened
1828 for (auto j = common_prefix_size; j != new_size; ++j) {
1829 code_ += "#[allow(unused_imports, dead_code)]";
1830 code_ += "pub mod " + MakeSnakeCase(ns->components[j]) + " {";
1831 // Generate local namespace imports.
1832 GenNamespaceImports(2);
1833 }
1834 if (new_size != common_prefix_size) { code_ += ""; }
1835
1836 cur_name_space_ = ns;
1837 }
1838 };
1839
1840 } // namespace rust
1841
GenerateRust(const Parser & parser,const std::string & path,const std::string & file_name)1842 bool GenerateRust(const Parser &parser, const std::string &path,
1843 const std::string &file_name) {
1844 rust::RustGenerator generator(parser, path, file_name);
1845 return generator.generate();
1846 }
1847
RustMakeRule(const Parser & parser,const std::string & path,const std::string & file_name)1848 std::string RustMakeRule(const Parser &parser, const std::string &path,
1849 const std::string &file_name) {
1850 std::string filebase =
1851 flatbuffers::StripPath(flatbuffers::StripExtension(file_name));
1852 rust::RustGenerator generator(parser, path, file_name);
1853 std::string make_rule =
1854 generator.GeneratedFileName(path, filebase, parser.opts) + ": ";
1855
1856 auto included_files = parser.GetIncludedFilesRecursive(file_name);
1857 for (auto it = included_files.begin(); it != included_files.end(); ++it) {
1858 make_rule += " " + *it;
1859 }
1860 return make_rule;
1861 }
1862
1863 } // namespace flatbuffers
1864
1865 // TODO(rw): Generated code should import other generated files.
1866 // TODO(rw): Generated code should refer to namespaces in included files in a
1867 // way that makes them referrable.
1868 // TODO(rw): Generated code should indent according to nesting level.
1869 // TODO(rw): Generated code should generate endian-safe Debug impls.
1870 // TODO(rw): Generated code could use a Rust-only enum type to access unions,
1871 // instead of making the user use _type() to manually switch.
1872 // TODO(maxburke): There should be test schemas added that use language
1873 // keywords as fields of structs, tables, unions, enums, to make sure
1874 // that internal code generated references escaped names correctly.
1875 // TODO(maxburke): We should see if there is a more flexible way of resolving
1876 // module paths for use declarations. Right now if schemas refer to
1877 // other flatbuffer files, the include paths in emitted Rust bindings
1878 // are crate-relative which may undesirable.
1879