Squashed 'third_party/flatbuffers/' content from commit acc9990ab
Change-Id: I48550d40d78fea996ebe74e9723a5d1f910de491
git-subtree-dir: third_party/flatbuffers
git-subtree-split: acc9990abd2206491480291b0f85f925110102ea
diff --git a/src/idl_gen_rust.cpp b/src/idl_gen_rust.cpp
new file mode 100644
index 0000000..936ac83
--- /dev/null
+++ b/src/idl_gen_rust.cpp
@@ -0,0 +1,1825 @@
+/*
+ * Copyright 2018 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// independent from idl_parser, since this code is not needed for most clients
+
+#include "flatbuffers/code_generators.h"
+#include "flatbuffers/flatbuffers.h"
+#include "flatbuffers/idl.h"
+#include "flatbuffers/util.h"
+
+namespace flatbuffers {
+
+static std::string GeneratedFileName(const std::string &path,
+ const std::string &file_name) {
+ return path + file_name + "_generated.rs";
+}
+
+// Convert a camelCaseIdentifier or CamelCaseIdentifier to a
+// snake_case_indentifier.
+std::string MakeSnakeCase(const std::string &in) {
+ std::string s;
+ for (size_t i = 0; i < in.length(); i++) {
+ if (i == 0) {
+ s += static_cast<char>(tolower(in[0]));
+ } else if (in[i] == '_') {
+ s += '_';
+ } else if (!islower(in[i])) {
+ // Prevent duplicate underscores for Upper_Snake_Case strings
+ // and UPPERCASE strings.
+ if (islower(in[i - 1])) {
+ s += '_';
+ }
+ s += static_cast<char>(tolower(in[i]));
+ } else {
+ s += in[i];
+ }
+ }
+ return s;
+}
+
+// Convert a string to all uppercase.
+std::string MakeUpper(const std::string &in) {
+ std::string s;
+ for (size_t i = 0; i < in.length(); i++) {
+ s += static_cast<char>(toupper(in[i]));
+ }
+ return s;
+}
+
+// Encapsulate all logical field types in this enum. This allows us to write
+// field logic based on type switches, instead of branches on the properties
+// set on the Type.
+// TODO(rw): for backwards compatibility, we can't use a strict `enum class`
+// declaration here. could we use the `-Wswitch-enum` warning to
+// achieve the same effect?
+enum FullType {
+ ftInteger = 0,
+ ftFloat = 1,
+ ftBool = 2,
+
+ ftStruct = 3,
+ ftTable = 4,
+
+ ftEnumKey = 5,
+ ftUnionKey = 6,
+
+ ftUnionValue = 7,
+
+ // TODO(rw): bytestring?
+ ftString = 8,
+
+ ftVectorOfInteger = 9,
+ ftVectorOfFloat = 10,
+ ftVectorOfBool = 11,
+ ftVectorOfEnumKey = 12,
+ ftVectorOfStruct = 13,
+ ftVectorOfTable = 14,
+ ftVectorOfString = 15,
+ ftVectorOfUnionValue = 16,
+};
+
+// Convert a Type to a FullType (exhaustive).
+FullType GetFullType(const Type &type) {
+ // N.B. The order of these conditionals matters for some types.
+
+ if (type.base_type == BASE_TYPE_STRING) {
+ return ftString;
+ } else if (type.base_type == BASE_TYPE_STRUCT) {
+ if (type.struct_def->fixed) {
+ return ftStruct;
+ } else {
+ return ftTable;
+ }
+ } else if (type.base_type == BASE_TYPE_VECTOR) {
+ switch (GetFullType(type.VectorType())) {
+ case ftInteger: {
+ return ftVectorOfInteger;
+ }
+ case ftFloat: {
+ return ftVectorOfFloat;
+ }
+ case ftBool: {
+ return ftVectorOfBool;
+ }
+ case ftStruct: {
+ return ftVectorOfStruct;
+ }
+ case ftTable: {
+ return ftVectorOfTable;
+ }
+ case ftString: {
+ return ftVectorOfString;
+ }
+ case ftEnumKey: {
+ return ftVectorOfEnumKey;
+ }
+ case ftUnionKey:
+ case ftUnionValue: {
+ FLATBUFFERS_ASSERT(false && "vectors of unions are unsupported");
+ break;
+ }
+ default: {
+ FLATBUFFERS_ASSERT(false && "vector of vectors are unsupported");
+ }
+ }
+ } else if (type.enum_def != nullptr) {
+ if (type.enum_def->is_union) {
+ if (type.base_type == BASE_TYPE_UNION) {
+ return ftUnionValue;
+ } else if (IsInteger(type.base_type)) {
+ return ftUnionKey;
+ } else {
+ FLATBUFFERS_ASSERT(false && "unknown union field type");
+ }
+ } else {
+ return ftEnumKey;
+ }
+ } else if (IsScalar(type.base_type)) {
+ if (IsBool(type.base_type)) {
+ return ftBool;
+ } else if (IsInteger(type.base_type)) {
+ return ftInteger;
+ } else if (IsFloat(type.base_type)) {
+ return ftFloat;
+ } else {
+ FLATBUFFERS_ASSERT(false && "unknown number type");
+ }
+ }
+
+ FLATBUFFERS_ASSERT(false && "completely unknown type");
+
+ // this is only to satisfy the compiler's return analysis.
+ return ftBool;
+}
+
+// If the second parameter is false then wrap the first with Option<...>
+std::string WrapInOptionIfNotRequired(std::string s, bool required) {
+ if (required) {
+ return s;
+ } else {
+ return "Option<" + s + ">";
+ }
+}
+
+// If the second parameter is false then add .unwrap()
+std::string AddUnwrapIfRequired(std::string s, bool required) {
+ if (required) {
+ return s + ".unwrap()";
+ } else {
+ return s;
+ }
+}
+
+namespace rust {
+
+class RustGenerator : public BaseGenerator {
+ public:
+ RustGenerator(const Parser &parser, const std::string &path,
+ const std::string &file_name)
+ : BaseGenerator(parser, path, file_name, "", "::"),
+ cur_name_space_(nullptr) {
+ const char *keywords[] = {
+ // list taken from:
+ // https://doc.rust-lang.org/book/second-edition/appendix-01-keywords.html
+ //
+ // we write keywords one per line so that we can easily compare them with
+ // changes to that webpage in the future.
+
+ // currently-used keywords
+ "as",
+ "break",
+ "const",
+ "continue",
+ "crate",
+ "else",
+ "enum",
+ "extern",
+ "false",
+ "fn",
+ "for",
+ "if",
+ "impl",
+ "in",
+ "let",
+ "loop",
+ "match",
+ "mod",
+ "move",
+ "mut",
+ "pub",
+ "ref",
+ "return",
+ "Self",
+ "self",
+ "static",
+ "struct",
+ "super",
+ "trait",
+ "true",
+ "type",
+ "unsafe",
+ "use",
+ "where",
+ "while",
+
+ // future possible keywords
+ "abstract",
+ "alignof",
+ "become",
+ "box",
+ "do",
+ "final",
+ "macro",
+ "offsetof",
+ "override",
+ "priv",
+ "proc",
+ "pure",
+ "sizeof",
+ "typeof",
+ "unsized",
+ "virtual",
+ "yield",
+
+ // other rust terms we should not use
+ "std",
+ "usize",
+ "isize",
+ "u8",
+ "i8",
+ "u16",
+ "i16",
+ "u32",
+ "i32",
+ "u64",
+ "i64",
+ "u128",
+ "i128",
+ "f32",
+ "f64",
+
+ // These are terms the code generator can implement on types.
+ //
+ // In Rust, the trait resolution rules (as described at
+ // https://github.com/rust-lang/rust/issues/26007) mean that, as long
+ // as we impl table accessors as inherent methods, we'll never create
+ // conflicts with these keywords. However, that's a fairly nuanced
+ // implementation detail, and how we implement methods could change in
+ // the future. as a result, we proactively block these out as reserved
+ // words.
+ "follow",
+ "push",
+ "size",
+ "alignment",
+ "to_little_endian",
+ "from_little_endian",
+ nullptr };
+ for (auto kw = keywords; *kw; kw++) keywords_.insert(*kw);
+ }
+
+ // Iterate through all definitions we haven't generated code for (enums,
+ // structs, and tables) and output them to a single file.
+ bool generate() {
+ code_.Clear();
+ code_ += "// " + std::string(FlatBuffersGeneratedWarning()) + "\n\n";
+
+ assert(!cur_name_space_);
+
+ // Generate imports for the global scope in case no namespace is used
+ // in the schema file.
+ GenNamespaceImports(0);
+ code_ += "";
+
+ // Generate all code in their namespaces, once, because Rust does not
+ // permit re-opening modules.
+ //
+ // TODO(rw): Use a set data structure to reduce namespace evaluations from
+ // O(n**2) to O(n).
+ for (auto ns_it = parser_.namespaces_.begin();
+ ns_it != parser_.namespaces_.end();
+ ++ns_it) {
+ const auto &ns = *ns_it;
+
+ // Generate code for all the enum declarations.
+ for (auto it = parser_.enums_.vec.begin(); it != parser_.enums_.vec.end();
+ ++it) {
+ const auto &enum_def = **it;
+ if (enum_def.defined_namespace != ns) { continue; }
+ if (!enum_def.generated) {
+ SetNameSpace(enum_def.defined_namespace);
+ GenEnum(enum_def);
+ }
+ }
+
+ // Generate code for all structs.
+ for (auto it = parser_.structs_.vec.begin();
+ it != parser_.structs_.vec.end(); ++it) {
+ const auto &struct_def = **it;
+ if (struct_def.defined_namespace != ns) { continue; }
+ if (struct_def.fixed && !struct_def.generated) {
+ SetNameSpace(struct_def.defined_namespace);
+ GenStruct(struct_def);
+ }
+ }
+
+ // Generate code for all tables.
+ for (auto it = parser_.structs_.vec.begin();
+ it != parser_.structs_.vec.end(); ++it) {
+ const auto &struct_def = **it;
+ if (struct_def.defined_namespace != ns) { continue; }
+ if (!struct_def.fixed && !struct_def.generated) {
+ SetNameSpace(struct_def.defined_namespace);
+ GenTable(struct_def);
+ }
+ }
+
+ // Generate global helper functions.
+ if (parser_.root_struct_def_) {
+ auto &struct_def = *parser_.root_struct_def_;
+ if (struct_def.defined_namespace != ns) { continue; }
+ SetNameSpace(struct_def.defined_namespace);
+ GenRootTableFuncs(struct_def);
+ }
+ }
+ if (cur_name_space_) SetNameSpace(nullptr);
+
+ const auto file_path = GeneratedFileName(path_, file_name_);
+ const auto final_code = code_.ToString();
+ return SaveFile(file_path.c_str(), final_code, false);
+ }
+
+ private:
+ CodeWriter code_;
+
+ std::set<std::string> keywords_;
+
+ // This tracks the current namespace so we can insert namespace declarations.
+ const Namespace *cur_name_space_;
+
+ const Namespace *CurrentNameSpace() const { return cur_name_space_; }
+
+ // Determine if a Type needs a lifetime template parameter when used in the
+ // Rust builder args.
+ bool TableBuilderTypeNeedsLifetime(const Type &type) const {
+ switch (GetFullType(type)) {
+ case ftInteger:
+ case ftFloat:
+ case ftBool:
+ case ftEnumKey:
+ case ftUnionKey:
+ case ftUnionValue: { return false; }
+ default: { return true; }
+ }
+ }
+
+ // Determine if a table args rust type needs a lifetime template parameter.
+ bool TableBuilderArgsNeedsLifetime(const StructDef &struct_def) const {
+ FLATBUFFERS_ASSERT(!struct_def.fixed);
+
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end(); ++it) {
+ const auto &field = **it;
+ if (field.deprecated) {
+ continue;
+ }
+
+ if (TableBuilderTypeNeedsLifetime(field.value.type)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ // Determine if a Type needs to be copied (for endian safety) when used in a
+ // Struct.
+ bool StructMemberAccessNeedsCopy(const Type &type) const {
+ switch (GetFullType(type)) {
+ case ftInteger: // requires endian swap
+ case ftFloat: // requires endian swap
+ case ftBool: // no endian-swap, but do the copy for UX consistency
+ case ftEnumKey: { return true; } // requires endian swap
+ case ftStruct: { return false; } // no endian swap
+ default: {
+ // logic error: no other types can be struct members.
+ FLATBUFFERS_ASSERT(false && "invalid struct member type");
+ return false; // only to satisfy compiler's return analysis
+ }
+ }
+ }
+
+ std::string EscapeKeyword(const std::string &name) const {
+ return keywords_.find(name) == keywords_.end() ? name : name + "_";
+ }
+
+ std::string Name(const Definition &def) const {
+ return EscapeKeyword(def.name);
+ }
+
+ std::string Name(const EnumVal &ev) const { return EscapeKeyword(ev.name); }
+
+ std::string WrapInNameSpace(const Definition &def) const {
+ return WrapInNameSpace(def.defined_namespace, Name(def));
+ }
+ std::string WrapInNameSpace(const Namespace *ns,
+ const std::string &name) const {
+ if (CurrentNameSpace() == ns) return name;
+ std::string prefix = GetRelativeNamespaceTraversal(CurrentNameSpace(), ns);
+ return prefix + name;
+ }
+
+ // Determine the namespace traversal needed from the Rust crate root.
+ // This may be useful in the future for referring to included files, but is
+ // currently unused.
+ std::string GetAbsoluteNamespaceTraversal(const Namespace *dst) const {
+ std::stringstream stream;
+
+ stream << "::";
+ for (auto d = dst->components.begin(); d != dst->components.end(); ++d) {
+ stream << MakeSnakeCase(*d) + "::";
+ }
+ return stream.str();
+ }
+
+ // Determine the relative namespace traversal needed to reference one
+ // namespace from another namespace. This is useful because it does not force
+ // the user to have a particular file layout. (If we output absolute
+ // namespace paths, that may require users to organize their Rust crates in a
+ // particular way.)
+ std::string GetRelativeNamespaceTraversal(const Namespace *src,
+ const Namespace *dst) const {
+ // calculate the path needed to reference dst from src.
+ // example: f(A::B::C, A::B::C) -> (none)
+ // example: f(A::B::C, A::B) -> super::
+ // example: f(A::B::C, A::B::D) -> super::D
+ // example: f(A::B::C, A) -> super::super::
+ // example: f(A::B::C, D) -> super::super::super::D
+ // example: f(A::B::C, D::E) -> super::super::super::D::E
+ // example: f(A, D::E) -> super::D::E
+ // does not include leaf object (typically a struct type).
+
+ size_t i = 0;
+ std::stringstream stream;
+
+ auto s = src->components.begin();
+ auto d = dst->components.begin();
+ for(;;) {
+ if (s == src->components.end()) { break; }
+ if (d == dst->components.end()) { break; }
+ if (*s != *d) { break; }
+ ++s;
+ ++d;
+ ++i;
+ }
+
+ for (; s != src->components.end(); ++s) {
+ stream << "super::";
+ }
+ for (; d != dst->components.end(); ++d) {
+ stream << MakeSnakeCase(*d) + "::";
+ }
+ return stream.str();
+ }
+
+ // Generate a comment from the schema.
+ void GenComment(const std::vector<std::string> &dc, const char *prefix = "") {
+ std::string text;
+ ::flatbuffers::GenComment(dc, &text, nullptr, prefix);
+ code_ += text + "\\";
+ }
+
+ // Return a Rust type from the table in idl.h.
+ std::string GetTypeBasic(const Type &type) const {
+ switch (GetFullType(type)) {
+ case ftInteger:
+ case ftFloat:
+ case ftBool:
+ case ftEnumKey:
+ case ftUnionKey: { break; }
+ default: { FLATBUFFERS_ASSERT(false && "incorrect type given");}
+ }
+
+ // clang-format off
+ static const char * const ctypename[] = {
+ #define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, \
+ RTYPE, KTYPE) \
+ #RTYPE,
+ FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
+ #undef FLATBUFFERS_TD
+ // clang-format on
+ };
+
+ if (type.enum_def) { return WrapInNameSpace(*type.enum_def); }
+ return ctypename[type.base_type];
+ }
+
+ // Look up the native type for an enum. This will always be an integer like
+ // u8, i32, etc.
+ std::string GetEnumTypeForDecl(const Type &type) {
+ const auto ft = GetFullType(type);
+ if (!(ft == ftEnumKey || ft == ftUnionKey)) {
+ FLATBUFFERS_ASSERT(false && "precondition failed in GetEnumTypeForDecl");
+ }
+
+ static const char *ctypename[] = {
+ // clang-format off
+ #define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, \
+ RTYPE, KTYPE) \
+ #RTYPE,
+ FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
+ #undef FLATBUFFERS_TD
+ // clang-format on
+ };
+
+ // Enums can be bools, but their Rust representation must be a u8, as used
+ // in the repr attribute (#[repr(bool)] is an invalid attribute).
+ if (type.base_type == BASE_TYPE_BOOL) return "u8";
+ return ctypename[type.base_type];
+ }
+
+ // Return a Rust type for any type (scalar, table, struct) specifically for
+ // using a FlatBuffer.
+ std::string GetTypeGet(const Type &type) const {
+ switch (GetFullType(type)) {
+ case ftInteger:
+ case ftFloat:
+ case ftBool:
+ case ftEnumKey:
+ case ftUnionKey: {
+ return GetTypeBasic(type); }
+ case ftTable: {
+ return WrapInNameSpace(type.struct_def->defined_namespace,
+ type.struct_def->name) + "<'a>"; }
+ default: {
+ return WrapInNameSpace(type.struct_def->defined_namespace,
+ type.struct_def->name); }
+ }
+ }
+
+ std::string GetEnumValUse(const EnumDef &enum_def,
+ const EnumVal &enum_val) const {
+ return Name(enum_def) + "::" + Name(enum_val);
+ }
+
+ // Generate an enum declaration,
+ // an enum string lookup table,
+ // an enum match function,
+ // and an enum array of values
+ void GenEnum(const EnumDef &enum_def) {
+ code_.SetValue("ENUM_NAME", Name(enum_def));
+ code_.SetValue("BASE_TYPE", GetEnumTypeForDecl(enum_def.underlying_type));
+
+ GenComment(enum_def.doc_comment);
+ code_ += "#[allow(non_camel_case_types)]";
+ code_ += "#[repr({{BASE_TYPE}})]";
+ code_ += "#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]";
+ code_ += "pub enum " + Name(enum_def) + " {";
+
+ for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); ++it) {
+ const auto &ev = **it;
+
+ GenComment(ev.doc_comment, " ");
+ code_.SetValue("KEY", Name(ev));
+ code_.SetValue("VALUE", enum_def.ToString(ev));
+ code_ += " {{KEY}} = {{VALUE}},";
+ }
+ const EnumVal *minv = enum_def.MinValue();
+ const EnumVal *maxv = enum_def.MaxValue();
+ FLATBUFFERS_ASSERT(minv && maxv);
+
+ code_ += "";
+ code_ += "}";
+ code_ += "";
+
+ code_.SetValue("ENUM_NAME", Name(enum_def));
+ code_.SetValue("ENUM_NAME_SNAKE", MakeSnakeCase(Name(enum_def)));
+ code_.SetValue("ENUM_NAME_CAPS", MakeUpper(MakeSnakeCase(Name(enum_def))));
+ code_.SetValue("ENUM_MIN_BASE_VALUE", enum_def.ToString(*minv));
+ code_.SetValue("ENUM_MAX_BASE_VALUE", enum_def.ToString(*maxv));
+
+ // Generate enum constants, and impls for Follow, EndianScalar, and Push.
+ code_ += "const ENUM_MIN_{{ENUM_NAME_CAPS}}: {{BASE_TYPE}} = \\";
+ code_ += "{{ENUM_MIN_BASE_VALUE}};";
+ code_ += "const ENUM_MAX_{{ENUM_NAME_CAPS}}: {{BASE_TYPE}} = \\";
+ code_ += "{{ENUM_MAX_BASE_VALUE}};";
+ code_ += "";
+ code_ += "impl<'a> flatbuffers::Follow<'a> for {{ENUM_NAME}} {";
+ code_ += " type Inner = Self;";
+ code_ += " #[inline]";
+ code_ += " fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {";
+ code_ += " flatbuffers::read_scalar_at::<Self>(buf, loc)";
+ code_ += " }";
+ code_ += "}";
+ code_ += "";
+ code_ += "impl flatbuffers::EndianScalar for {{ENUM_NAME}} {";
+ code_ += " #[inline]";
+ code_ += " fn to_little_endian(self) -> Self {";
+ code_ += " let n = {{BASE_TYPE}}::to_le(self as {{BASE_TYPE}});";
+ code_ += " let p = &n as *const {{BASE_TYPE}} as *const {{ENUM_NAME}};";
+ code_ += " unsafe { *p }";
+ code_ += " }";
+ code_ += " #[inline]";
+ code_ += " fn from_little_endian(self) -> Self {";
+ code_ += " let n = {{BASE_TYPE}}::from_le(self as {{BASE_TYPE}});";
+ code_ += " let p = &n as *const {{BASE_TYPE}} as *const {{ENUM_NAME}};";
+ code_ += " unsafe { *p }";
+ code_ += " }";
+ code_ += "}";
+ code_ += "";
+ code_ += "impl flatbuffers::Push for {{ENUM_NAME}} {";
+ code_ += " type Output = {{ENUM_NAME}};";
+ code_ += " #[inline]";
+ code_ += " fn push(&self, dst: &mut [u8], _rest: &[u8]) {";
+ code_ += " flatbuffers::emplace_scalar::<{{ENUM_NAME}}>"
+ "(dst, *self);";
+ code_ += " }";
+ code_ += "}";
+ code_ += "";
+
+ // Generate an array of all enumeration values.
+ auto num_fields = NumToString(enum_def.size());
+ code_ += "#[allow(non_camel_case_types)]";
+ code_ += "const ENUM_VALUES_{{ENUM_NAME_CAPS}}:[{{ENUM_NAME}}; " +
+ num_fields + "] = [";
+ for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); ++it) {
+ const auto &ev = **it;
+ auto value = GetEnumValUse(enum_def, ev);
+ auto suffix = *it != enum_def.Vals().back() ? "," : "";
+ code_ += " " + value + suffix;
+ }
+ code_ += "];";
+ code_ += "";
+
+ // Generate a string table for enum values.
+ // Problem is, if values are very sparse that could generate really big
+ // tables. Ideally in that case we generate a map lookup instead, but for
+ // the moment we simply don't output a table at all.
+ auto range = enum_def.Distance();
+ // Average distance between values above which we consider a table
+ // "too sparse". Change at will.
+ static const uint64_t kMaxSparseness = 5;
+ if (range / static_cast<uint64_t>(enum_def.size()) < kMaxSparseness) {
+ code_ += "#[allow(non_camel_case_types)]";
+ code_ += "const ENUM_NAMES_{{ENUM_NAME_CAPS}}:[&'static str; " +
+ NumToString(range + 1) + "] = [";
+
+ auto val = enum_def.Vals().front();
+ for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end();
+ ++it) {
+ auto ev = *it;
+ for (auto k = enum_def.Distance(val, ev); k > 1; --k) {
+ code_ += " \"\",";
+ }
+ val = ev;
+ auto suffix = *it != enum_def.Vals().back() ? "," : "";
+ code_ += " \"" + Name(*ev) + "\"" + suffix;
+ }
+ code_ += "];";
+ code_ += "";
+
+ code_ +=
+ "pub fn enum_name_{{ENUM_NAME_SNAKE}}(e: {{ENUM_NAME}}) -> "
+ "&'static str {";
+
+ code_ += " let index = e as {{BASE_TYPE}}\\";
+ if (enum_def.MinValue()->IsNonZero()) {
+ auto vals = GetEnumValUse(enum_def, *enum_def.MinValue());
+ code_ += " - " + vals + " as {{BASE_TYPE}}\\";
+ }
+ code_ += ";";
+
+ code_ += " ENUM_NAMES_{{ENUM_NAME_CAPS}}[index as usize]";
+ code_ += "}";
+ code_ += "";
+ }
+
+ if (enum_def.is_union) {
+ // Generate tyoesafe offset(s) for unions
+ code_.SetValue("NAME", Name(enum_def));
+ code_.SetValue("UNION_OFFSET_NAME", Name(enum_def) + "UnionTableOffset");
+ code_ += "pub struct {{UNION_OFFSET_NAME}} {}";
+ }
+ }
+
+ std::string GetFieldOffsetName(const FieldDef &field) {
+ return "VT_" + MakeUpper(Name(field));
+ }
+
+ std::string GetDefaultConstant(const FieldDef &field) {
+ return field.value.type.base_type == BASE_TYPE_FLOAT
+ ? field.value.constant + ""
+ : field.value.constant;
+ }
+
+ std::string GetDefaultScalarValue(const FieldDef &field) {
+ switch (GetFullType(field.value.type)) {
+ case ftInteger: { return GetDefaultConstant(field); }
+ case ftFloat: { return GetDefaultConstant(field); }
+ case ftBool: {
+ return field.value.constant == "0" ? "false" : "true";
+ }
+ case ftUnionKey:
+ case ftEnumKey: {
+ auto ev = field.value.type.enum_def->FindByValue(field.value.constant);
+ assert(ev);
+ return WrapInNameSpace(field.value.type.enum_def->defined_namespace,
+ GetEnumValUse(*field.value.type.enum_def, *ev));
+ }
+
+ // All pointer-ish types have a default value of None, because they are
+ // wrapped in Option.
+ default: { return "None"; }
+ }
+ }
+
+ // Create the return type for fields in the *BuilderArgs structs that are
+ // used to create Tables.
+ //
+ // Note: we could make all inputs to the BuilderArgs be an Option, as well
+ // as all outputs. But, the UX of Flatbuffers is that the user doesn't get to
+ // know if the value is default or not, because there are three ways to
+ // return a default value:
+ // 1) return a stored value that happens to be the default,
+ // 2) return a hardcoded value because the relevant vtable field is not in
+ // the vtable, or
+ // 3) return a hardcoded value because the vtable field value is set to zero.
+ std::string TableBuilderArgsDefnType(const FieldDef &field,
+ const std::string &lifetime) {
+ const Type& type = field.value.type;
+
+ switch (GetFullType(type)) {
+ case ftInteger:
+ case ftFloat:
+ case ftBool: {
+ const auto typname = GetTypeBasic(type);
+ return typname;
+ }
+ case ftStruct: {
+ const auto typname = WrapInNameSpace(*type.struct_def);
+ return "Option<&" + lifetime + " " + typname + ">";
+ }
+ case ftTable: {
+ const auto typname = WrapInNameSpace(*type.struct_def);
+ return "Option<flatbuffers::WIPOffset<" + typname + "<" + lifetime + \
+ ">>>";
+ }
+ case ftString: {
+ return "Option<flatbuffers::WIPOffset<&" + lifetime + " str>>";
+ }
+ case ftEnumKey:
+ case ftUnionKey: {
+ const auto typname = WrapInNameSpace(*type.enum_def);
+ return typname;
+ }
+ case ftUnionValue: {
+ return "Option<flatbuffers::WIPOffset<flatbuffers::UnionWIPOffset>>";
+ }
+
+ case ftVectorOfInteger:
+ case ftVectorOfFloat: {
+ const auto typname = GetTypeBasic(type.VectorType());
+ return "Option<flatbuffers::WIPOffset<flatbuffers::Vector<" + \
+ lifetime + ", " + typname + ">>>";
+ }
+ case ftVectorOfBool: {
+ return "Option<flatbuffers::WIPOffset<flatbuffers::Vector<" + \
+ lifetime + ", bool>>>";
+ }
+ case ftVectorOfEnumKey: {
+ const auto typname = WrapInNameSpace(*type.enum_def);
+ return "Option<flatbuffers::WIPOffset<flatbuffers::Vector<" + \
+ lifetime + ", " + typname + ">>>";
+ }
+ case ftVectorOfStruct: {
+ const auto typname = WrapInNameSpace(*type.struct_def);
+ return "Option<flatbuffers::WIPOffset<flatbuffers::Vector<" + \
+ lifetime + ", " + typname + ">>>";
+ }
+ case ftVectorOfTable: {
+ const auto typname = WrapInNameSpace(*type.struct_def);
+ return "Option<flatbuffers::WIPOffset<flatbuffers::Vector<" + \
+ lifetime + ", flatbuffers::ForwardsUOffset<" + typname + \
+ "<" + lifetime + ">>>>>";
+ }
+ case ftVectorOfString: {
+ return "Option<flatbuffers::WIPOffset<flatbuffers::Vector<" + \
+ lifetime + ", flatbuffers::ForwardsUOffset<&" + lifetime + \
+ " str>>>>";
+ }
+ case ftVectorOfUnionValue: {
+ const auto typname = WrapInNameSpace(*type.enum_def) + \
+ "UnionTableOffset";
+ return "Option<flatbuffers::WIPOffset<flatbuffers::Vector<" + \
+ lifetime + ", flatbuffers::ForwardsUOffset<"
+ "flatbuffers::Table<" + lifetime + ">>>>";
+ }
+ }
+ return "INVALID_CODE_GENERATION"; // for return analysis
+ }
+
+ std::string TableBuilderArgsDefaultValue(const FieldDef &field) {
+ return GetDefaultScalarValue(field);
+ }
+ std::string TableBuilderAddFuncDefaultValue(const FieldDef &field) {
+ // All branches of switch do the same action!
+ switch (GetFullType(field.value.type)) {
+ case ftUnionKey:
+ case ftEnumKey: {
+ const std::string basetype = GetTypeBasic(field.value.type); //<- never used
+ return GetDefaultScalarValue(field);
+ }
+
+ default: { return GetDefaultScalarValue(field); }
+ }
+ }
+
+ std::string TableBuilderArgsAddFuncType(const FieldDef &field,
+ const std::string &lifetime) {
+ const Type& type = field.value.type;
+
+ switch (GetFullType(field.value.type)) {
+ case ftVectorOfStruct: {
+ const auto typname = WrapInNameSpace(*type.struct_def);
+ return "flatbuffers::WIPOffset<flatbuffers::Vector<" + lifetime + \
+ ", " + typname + ">>";
+ }
+ case ftVectorOfTable: {
+ const auto typname = WrapInNameSpace(*type.struct_def);
+ return "flatbuffers::WIPOffset<flatbuffers::Vector<" + lifetime + \
+ ", flatbuffers::ForwardsUOffset<" + typname + \
+ "<" + lifetime + ">>>>";
+ }
+ case ftVectorOfInteger:
+ case ftVectorOfFloat: {
+ const auto typname = GetTypeBasic(type.VectorType());
+ return "flatbuffers::WIPOffset<flatbuffers::Vector<" + lifetime + \
+ ", " + typname + ">>";
+ }
+ case ftVectorOfBool: {
+ return "flatbuffers::WIPOffset<flatbuffers::Vector<" + lifetime + \
+ ", bool>>";
+ }
+ case ftVectorOfString: {
+ return "flatbuffers::WIPOffset<flatbuffers::Vector<" + lifetime + \
+ ", flatbuffers::ForwardsUOffset<&" + lifetime + " str>>>";
+ }
+ case ftVectorOfEnumKey: {
+ const auto typname = WrapInNameSpace(*type.enum_def);
+ return "flatbuffers::WIPOffset<flatbuffers::Vector<" + lifetime + \
+ ", " + typname + ">>";
+ }
+ case ftVectorOfUnionValue: {
+ return "flatbuffers::WIPOffset<flatbuffers::Vector<" + lifetime + \
+ ", flatbuffers::ForwardsUOffset<flatbuffers::Table<" + \
+ lifetime + ">>>";
+ }
+ case ftEnumKey: {
+ const auto typname = WrapInNameSpace(*type.enum_def);
+ return typname;
+ }
+ case ftStruct: {
+ const auto typname = WrapInNameSpace(*type.struct_def);
+ return "&" + lifetime + " " + typname + "";
+ }
+ case ftTable: {
+ const auto typname = WrapInNameSpace(*type.struct_def);
+ return "flatbuffers::WIPOffset<" + typname + "<" + lifetime + ">>";
+ }
+ case ftInteger:
+ case ftFloat: {
+ const auto typname = GetTypeBasic(type);
+ return typname;
+ }
+ case ftBool: {
+ return "bool";
+ }
+ case ftString: {
+ return "flatbuffers::WIPOffset<&" + lifetime + " str>";
+ }
+ case ftUnionKey: {
+ const auto typname = WrapInNameSpace(*type.enum_def);
+ return typname;
+ }
+ case ftUnionValue: {
+ return "flatbuffers::WIPOffset<flatbuffers::UnionWIPOffset>";
+ }
+ }
+
+ return "INVALID_CODE_GENERATION"; // for return analysis
+ }
+
+ std::string TableBuilderArgsAddFuncBody(const FieldDef &field) {
+ const Type& type = field.value.type;
+
+ switch (GetFullType(field.value.type)) {
+ case ftInteger:
+ case ftFloat: {
+ const auto typname = GetTypeBasic(field.value.type);
+ return "self.fbb_.push_slot::<" + typname + ">";
+ }
+ case ftBool: {
+ return "self.fbb_.push_slot::<bool>";
+ }
+
+ case ftEnumKey:
+ case ftUnionKey: {
+ const auto underlying_typname = GetTypeBasic(type);
+ return "self.fbb_.push_slot::<" + underlying_typname + ">";
+ }
+
+ case ftStruct: {
+ const std::string typname = WrapInNameSpace(*type.struct_def);
+ return "self.fbb_.push_slot_always::<&" + typname + ">";
+ }
+ case ftTable: {
+ const auto typname = WrapInNameSpace(*type.struct_def);
+ return "self.fbb_.push_slot_always::<flatbuffers::WIPOffset<" + \
+ typname + ">>";
+ }
+
+ case ftUnionValue:
+ case ftString:
+ case ftVectorOfInteger:
+ case ftVectorOfFloat:
+ case ftVectorOfBool:
+ case ftVectorOfEnumKey:
+ case ftVectorOfStruct:
+ case ftVectorOfTable:
+ case ftVectorOfString:
+ case ftVectorOfUnionValue: {
+ return "self.fbb_.push_slot_always::<flatbuffers::WIPOffset<_>>";
+ }
+ }
+ return "INVALID_CODE_GENERATION"; // for return analysis
+ }
+
+ std::string GenTableAccessorFuncReturnType(const FieldDef &field,
+ const std::string &lifetime) {
+ const Type& type = field.value.type;
+
+ switch (GetFullType(field.value.type)) {
+ case ftInteger:
+ case ftFloat: {
+ const auto typname = GetTypeBasic(type);
+ return typname;
+ }
+ case ftBool: {
+ return "bool";
+ }
+ case ftStruct: {
+ const auto typname = WrapInNameSpace(*type.struct_def);
+ return WrapInOptionIfNotRequired("&" + lifetime + " " + typname, field.required);
+ }
+ case ftTable: {
+ const auto typname = WrapInNameSpace(*type.struct_def);
+ return WrapInOptionIfNotRequired(typname + "<" + lifetime + ">", field.required);
+ }
+ case ftEnumKey:
+ case ftUnionKey: {
+ const auto typname = WrapInNameSpace(*type.enum_def);
+ return typname;
+ }
+
+ case ftUnionValue: {
+ return WrapInOptionIfNotRequired("flatbuffers::Table<" + lifetime + ">", field.required);
+ }
+ case ftString: {
+ return WrapInOptionIfNotRequired("&" + lifetime + " str", field.required);
+ }
+ case ftVectorOfInteger:
+ case ftVectorOfFloat: {
+ const auto typname = GetTypeBasic(type.VectorType());
+ if (IsOneByte(type.VectorType().base_type)) {
+ return WrapInOptionIfNotRequired("&" + lifetime + " [" + typname + "]", field.required);
+ }
+ return WrapInOptionIfNotRequired("flatbuffers::Vector<" + lifetime + ", " + typname + ">", field.required);
+ }
+ case ftVectorOfBool: {
+ return WrapInOptionIfNotRequired("&" + lifetime + " [bool]", field.required);
+ }
+ case ftVectorOfEnumKey: {
+ const auto typname = WrapInNameSpace(*type.enum_def);
+ return WrapInOptionIfNotRequired("flatbuffers::Vector<" + lifetime + ", " + typname + ">", field.required);
+ }
+ case ftVectorOfStruct: {
+ const auto typname = WrapInNameSpace(*type.struct_def);
+ return WrapInOptionIfNotRequired("&" + lifetime + " [" + typname + "]", field.required);
+ }
+ case ftVectorOfTable: {
+ const auto typname = WrapInNameSpace(*type.struct_def);
+ return WrapInOptionIfNotRequired("flatbuffers::Vector<" + lifetime + ", flatbuffers::ForwardsUOffset<" + \
+ typname + "<" + lifetime + ">>>", field.required);
+ }
+ case ftVectorOfString: {
+ return WrapInOptionIfNotRequired("flatbuffers::Vector<" + lifetime + ", flatbuffers::ForwardsUOffset<&" + \
+ lifetime + " str>>", field.required);
+ }
+ case ftVectorOfUnionValue: {
+ FLATBUFFERS_ASSERT(false && "vectors of unions are not yet supported");
+ // TODO(rw): when we do support these, we should consider using the
+ // Into trait to convert tables to typesafe union values.
+ return "INVALID_CODE_GENERATION"; // for return analysis
+ }
+ }
+ return "INVALID_CODE_GENERATION"; // for return analysis
+ }
+
+ std::string GenTableAccessorFuncBody(const FieldDef &field,
+ const std::string &lifetime,
+ const std::string &offset_prefix) {
+ const std::string offset_name = offset_prefix + "::" + \
+ GetFieldOffsetName(field);
+ const Type& type = field.value.type;
+
+ switch (GetFullType(field.value.type)) {
+ case ftInteger:
+ case ftFloat:
+ case ftBool: {
+ const auto typname = GetTypeBasic(type);
+ const auto default_value = GetDefaultScalarValue(field);
+ return "self._tab.get::<" + typname + ">(" + offset_name + ", Some(" + \
+ default_value + ")).unwrap()";
+ }
+ case ftStruct: {
+ const auto typname = WrapInNameSpace(*type.struct_def);
+ return AddUnwrapIfRequired("self._tab.get::<" + typname + ">(" + offset_name + ", None)", field.required);
+ }
+ case ftTable: {
+ const auto typname = WrapInNameSpace(*type.struct_def);
+ return AddUnwrapIfRequired("self._tab.get::<flatbuffers::ForwardsUOffset<" + \
+ typname + "<" + lifetime + ">>>(" + offset_name + ", None)", field.required);
+ }
+ case ftUnionValue: {
+ return AddUnwrapIfRequired("self._tab.get::<flatbuffers::ForwardsUOffset<"
+ "flatbuffers::Table<" + lifetime + ">>>(" + offset_name + \
+ ", None)", field.required);
+ }
+ case ftUnionKey:
+ case ftEnumKey: {
+ const auto underlying_typname = GetTypeBasic(type); //<- never used
+ const auto typname = WrapInNameSpace(*type.enum_def);
+ const auto default_value = GetDefaultScalarValue(field);
+ return "self._tab.get::<" + typname + ">(" + offset_name + \
+ ", Some(" + default_value + ")).unwrap()";
+ }
+ case ftString: {
+ return AddUnwrapIfRequired("self._tab.get::<flatbuffers::ForwardsUOffset<&str>>(" + \
+ offset_name + ", None)", field.required);
+ }
+
+ case ftVectorOfInteger:
+ case ftVectorOfFloat: {
+ const auto typname = GetTypeBasic(type.VectorType());
+ std::string s = "self._tab.get::<flatbuffers::ForwardsUOffset<"
+ "flatbuffers::Vector<" + lifetime + ", " + typname + \
+ ">>>(" + offset_name + ", None)";
+ // single-byte values are safe to slice
+ if (IsOneByte(type.VectorType().base_type)) {
+ s += ".map(|v| v.safe_slice())";
+ }
+ return AddUnwrapIfRequired(s, field.required);
+ }
+ case ftVectorOfBool: {
+ return AddUnwrapIfRequired("self._tab.get::<flatbuffers::ForwardsUOffset<"
+ "flatbuffers::Vector<" + lifetime + ", bool>>>(" + \
+ offset_name + ", None).map(|v| v.safe_slice())", field.required);
+ }
+ case ftVectorOfEnumKey: {
+ const auto typname = WrapInNameSpace(*type.enum_def);
+ return AddUnwrapIfRequired("self._tab.get::<flatbuffers::ForwardsUOffset<"
+ "flatbuffers::Vector<" + lifetime + ", " + typname + ">>>(" + \
+ offset_name + ", None)", field.required);
+ }
+ case ftVectorOfStruct: {
+ const auto typname = WrapInNameSpace(*type.struct_def);
+ return AddUnwrapIfRequired("self._tab.get::<flatbuffers::ForwardsUOffset<"
+ "flatbuffers::Vector<" + typname + ">>>(" + \
+ offset_name + ", None).map(|v| v.safe_slice() )", field.required);
+ }
+ case ftVectorOfTable: {
+ const auto typname = WrapInNameSpace(*type.struct_def);
+ return AddUnwrapIfRequired("self._tab.get::<flatbuffers::ForwardsUOffset<"
+ "flatbuffers::Vector<flatbuffers::ForwardsUOffset<" + typname + \
+ "<" + lifetime + ">>>>>(" + offset_name + ", None)", field.required);
+ }
+ case ftVectorOfString: {
+ return AddUnwrapIfRequired("self._tab.get::<flatbuffers::ForwardsUOffset<"
+ "flatbuffers::Vector<flatbuffers::ForwardsUOffset<&" + \
+ lifetime + " str>>>>(" + offset_name + ", None)", field.required);
+ }
+ case ftVectorOfUnionValue: {
+ FLATBUFFERS_ASSERT(false && "vectors of unions are not yet supported");
+ return "INVALID_CODE_GENERATION"; // for return analysis
+ }
+ }
+ return "INVALID_CODE_GENERATION"; // for return analysis
+ }
+
+ bool TableFieldReturnsOption(const Type& type) {
+ switch (GetFullType(type)) {
+ case ftInteger:
+ case ftFloat:
+ case ftBool:
+ case ftEnumKey:
+ case ftUnionKey:
+ return false;
+ default: return true;
+ }
+ }
+
+ // Generate an accessor struct, builder struct, and create function for a
+ // table.
+ void GenTable(const StructDef &struct_def) {
+ code_.SetValue("STRUCT_NAME", Name(struct_def));
+ code_.SetValue("OFFSET_TYPELABEL", Name(struct_def) + "Offset");
+ code_.SetValue("STRUCT_NAME_SNAKECASE", MakeSnakeCase(Name(struct_def)));
+
+ // Generate an offset type, the base type, the Follow impl, and the
+ // init_from_table impl.
+ code_ += "pub enum {{OFFSET_TYPELABEL}} {}";
+ code_ += "#[derive(Copy, Clone, Debug, PartialEq)]";
+ code_ += "";
+
+ GenComment(struct_def.doc_comment);
+
+ code_ += "pub struct {{STRUCT_NAME}}<'a> {";
+ code_ += " pub _tab: flatbuffers::Table<'a>,";
+ code_ += "}";
+ code_ += "";
+ code_ += "impl<'a> flatbuffers::Follow<'a> for {{STRUCT_NAME}}<'a> {";
+ code_ += " type Inner = {{STRUCT_NAME}}<'a>;";
+ code_ += " #[inline]";
+ code_ += " fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {";
+ code_ += " Self {";
+ code_ += " _tab: flatbuffers::Table { buf: buf, loc: loc },";
+ code_ += " }";
+ code_ += " }";
+ code_ += "}";
+ code_ += "";
+ code_ += "impl<'a> {{STRUCT_NAME}}<'a> {";
+ code_ += " #[inline]";
+ code_ += " pub fn init_from_table(table: flatbuffers::Table<'a>) -> "
+ "Self {";
+ code_ += " {{STRUCT_NAME}} {";
+ code_ += " _tab: table,";
+ code_ += " }";
+ code_ += " }";
+
+ // Generate a convenient create* function that uses the above builder
+ // to create a table in one function call.
+ code_.SetValue("MAYBE_US",
+ struct_def.fields.vec.size() == 0 ? "_" : "");
+ code_.SetValue("MAYBE_LT",
+ TableBuilderArgsNeedsLifetime(struct_def) ? "<'args>" : "");
+ code_ += " #[allow(unused_mut)]";
+ code_ += " pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr>(";
+ code_ += " _fbb: "
+ "&'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr>,";
+ code_ += " {{MAYBE_US}}args: &'args {{STRUCT_NAME}}Args{{MAYBE_LT}})"
+ " -> flatbuffers::WIPOffset<{{STRUCT_NAME}}<'bldr>> {";
+
+ code_ += " let mut builder = {{STRUCT_NAME}}Builder::new(_fbb);";
+ for (size_t size = struct_def.sortbysize ? sizeof(largest_scalar_t) : 1;
+ size; size /= 2) {
+ for (auto it = struct_def.fields.vec.rbegin();
+ it != struct_def.fields.vec.rend(); ++it) {
+ const auto &field = **it;
+ // TODO(rw): fully understand this sortbysize usage
+ if (!field.deprecated && (!struct_def.sortbysize ||
+ size == SizeOf(field.value.type.base_type))) {
+ code_.SetValue("FIELD_NAME", Name(field));
+ if (TableFieldReturnsOption(field.value.type)) {
+ code_ += " if let Some(x) = args.{{FIELD_NAME}} "
+ "{ builder.add_{{FIELD_NAME}}(x); }";
+ } else {
+ code_ += " builder.add_{{FIELD_NAME}}(args.{{FIELD_NAME}});";
+ }
+ }
+ }
+ }
+ code_ += " builder.finish()";
+ code_ += " }";
+ code_ += "";
+
+ // Generate field id constants.
+ if (struct_def.fields.vec.size() > 0) {
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end(); ++it) {
+ const auto &field = **it;
+ if (field.deprecated) {
+ // Deprecated fields won't be accessible.
+ continue;
+ }
+
+ code_.SetValue("OFFSET_NAME", GetFieldOffsetName(field));
+ code_.SetValue("OFFSET_VALUE", NumToString(field.value.offset));
+ code_ += " pub const {{OFFSET_NAME}}: flatbuffers::VOffsetT = "
+ "{{OFFSET_VALUE}};";
+ }
+ code_ += "";
+ }
+
+ // Generate the accessors. Each has one of two forms:
+ //
+ // If a value can be None:
+ // pub fn name(&'a self) -> Option<user_facing_type> {
+ // self._tab.get::<internal_type>(offset, defaultval)
+ // }
+ //
+ // If a value is always Some:
+ // pub fn name(&'a self) -> user_facing_type {
+ // self._tab.get::<internal_type>(offset, defaultval).unwrap()
+ // }
+ const auto offset_prefix = Name(struct_def);
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end(); ++it) {
+ const auto &field = **it;
+ if (field.deprecated) {
+ // Deprecated fields won't be accessible.
+ continue;
+ }
+
+ code_.SetValue("FIELD_NAME", Name(field));
+ code_.SetValue("RETURN_TYPE",
+ GenTableAccessorFuncReturnType(field, "'a"));
+ code_.SetValue("FUNC_BODY",
+ GenTableAccessorFuncBody(field, "'a", offset_prefix));
+
+ GenComment(field.doc_comment, " ");
+ code_ += " #[inline]";
+ code_ += " pub fn {{FIELD_NAME}}(&self) -> {{RETURN_TYPE}} {";
+ code_ += " {{FUNC_BODY}}";
+ code_ += " }";
+
+ // Generate a comparison function for this field if it is a key.
+ if (field.key) {
+ GenKeyFieldMethods(field);
+ }
+
+ // Generate a nested flatbuffer field, if applicable.
+ auto nested = field.attributes.Lookup("nested_flatbuffer");
+ if (nested) {
+ std::string qualified_name = nested->constant;
+ auto nested_root = parser_.LookupStruct(nested->constant);
+ if (nested_root == nullptr) {
+ qualified_name = parser_.current_namespace_->GetFullyQualifiedName(
+ nested->constant);
+ nested_root = parser_.LookupStruct(qualified_name);
+ }
+ FLATBUFFERS_ASSERT(nested_root); // Guaranteed to exist by parser.
+ (void)nested_root;
+
+ code_.SetValue("OFFSET_NAME",
+ offset_prefix + "::" + GetFieldOffsetName(field));
+ code_ += " pub fn {{FIELD_NAME}}_nested_flatbuffer(&'a self) -> "
+ " Option<{{STRUCT_NAME}}<'a>> {";
+ code_ += " match self.{{FIELD_NAME}}() {";
+ code_ += " None => { None }";
+ code_ += " Some(data) => {";
+ code_ += " use self::flatbuffers::Follow;";
+ code_ += " Some(<flatbuffers::ForwardsUOffset"
+ "<{{STRUCT_NAME}}<'a>>>::follow(data, 0))";
+ code_ += " },";
+ code_ += " }";
+ code_ += " }";
+ }
+ }
+
+ // Explicit specializations for union accessors
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end(); ++it) {
+ const auto &field = **it;
+ if (field.deprecated || field.value.type.base_type != BASE_TYPE_UNION) {
+ continue;
+ }
+
+ auto u = field.value.type.enum_def;
+
+ code_.SetValue("FIELD_NAME", Name(field));
+
+ for (auto u_it = u->Vals().begin(); u_it != u->Vals().end(); ++u_it) {
+ auto &ev = **u_it;
+ if (ev.union_type.base_type == BASE_TYPE_NONE) { continue; }
+
+ auto table_init_type = WrapInNameSpace(
+ ev.union_type.struct_def->defined_namespace,
+ ev.union_type.struct_def->name);
+
+ code_.SetValue("U_ELEMENT_ENUM_TYPE",
+ WrapInNameSpace(u->defined_namespace, GetEnumValUse(*u, ev)));
+ code_.SetValue("U_ELEMENT_TABLE_TYPE", table_init_type);
+ code_.SetValue("U_ELEMENT_NAME", MakeSnakeCase(Name(ev)));
+
+ code_ += " #[inline]";
+ code_ += " #[allow(non_snake_case)]";
+ code_ += " pub fn {{FIELD_NAME}}_as_{{U_ELEMENT_NAME}}(&self) -> "
+ "Option<{{U_ELEMENT_TABLE_TYPE}}<'a>> {";
+ code_ += " if self.{{FIELD_NAME}}_type() == {{U_ELEMENT_ENUM_TYPE}} {";
+ code_ += " self.{{FIELD_NAME}}().map(|u| "
+ "{{U_ELEMENT_TABLE_TYPE}}::init_from_table(u))";
+ code_ += " } else {";
+ code_ += " None";
+ code_ += " }";
+ code_ += " }";
+ code_ += "";
+ }
+ }
+
+ code_ += "}"; // End of table impl.
+ code_ += "";
+
+ // Generate an args struct:
+ code_.SetValue("MAYBE_LT",
+ TableBuilderArgsNeedsLifetime(struct_def) ? "<'a>" : "");
+ code_ += "pub struct {{STRUCT_NAME}}Args{{MAYBE_LT}} {";
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end(); ++it) {
+ const auto &field = **it;
+ if (!field.deprecated) {
+ code_.SetValue("PARAM_NAME", Name(field));
+ code_.SetValue("PARAM_TYPE", TableBuilderArgsDefnType(field, "'a "));
+ code_ += " pub {{PARAM_NAME}}: {{PARAM_TYPE}},";
+ }
+ }
+ code_ += "}";
+
+ // Generate an impl of Default for the *Args type:
+ code_ += "impl<'a> Default for {{STRUCT_NAME}}Args{{MAYBE_LT}} {";
+ code_ += " #[inline]";
+ code_ += " fn default() -> Self {";
+ code_ += " {{STRUCT_NAME}}Args {";
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end(); ++it) {
+ const auto &field = **it;
+ if (!field.deprecated) {
+ code_.SetValue("PARAM_VALUE", TableBuilderArgsDefaultValue(field));
+ code_.SetValue("REQ", field.required ? " // required field" : "");
+ code_.SetValue("PARAM_NAME", Name(field));
+ code_ += " {{PARAM_NAME}}: {{PARAM_VALUE}},{{REQ}}";
+ }
+ }
+ code_ += " }";
+ code_ += " }";
+ code_ += "}";
+
+ // Generate a builder struct:
+ code_ += "pub struct {{STRUCT_NAME}}Builder<'a: 'b, 'b> {";
+ code_ += " fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a>,";
+ code_ += " start_: flatbuffers::WIPOffset<"
+ "flatbuffers::TableUnfinishedWIPOffset>,";
+ code_ += "}";
+
+ // Generate builder functions:
+ code_ += "impl<'a: 'b, 'b> {{STRUCT_NAME}}Builder<'a, 'b> {";
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end(); ++it) {
+ const auto &field = **it;
+ if (!field.deprecated) {
+ const bool is_scalar = IsScalar(field.value.type.base_type);
+
+ std::string offset = GetFieldOffsetName(field);
+
+ // Generate functions to add data, which take one of two forms.
+ //
+ // If a value has a default:
+ // fn add_x(x_: type) {
+ // fbb_.push_slot::<type>(offset, x_, Some(default));
+ // }
+ //
+ // If a value does not have a default:
+ // fn add_x(x_: type) {
+ // fbb_.push_slot_always::<type>(offset, x_);
+ // }
+ code_.SetValue("FIELD_NAME", Name(field));
+ code_.SetValue("FIELD_OFFSET", Name(struct_def) + "::" + offset);
+ code_.SetValue("FIELD_TYPE", TableBuilderArgsAddFuncType(field, "'b "));
+ code_.SetValue("FUNC_BODY", TableBuilderArgsAddFuncBody(field));
+ code_ += " #[inline]";
+ code_ += " pub fn add_{{FIELD_NAME}}(&mut self, {{FIELD_NAME}}: "
+ "{{FIELD_TYPE}}) {";
+ if (is_scalar) {
+ code_.SetValue("FIELD_DEFAULT_VALUE",
+ TableBuilderAddFuncDefaultValue(field));
+ code_ += " {{FUNC_BODY}}({{FIELD_OFFSET}}, {{FIELD_NAME}}, "
+ "{{FIELD_DEFAULT_VALUE}});";
+ } else {
+ code_ += " {{FUNC_BODY}}({{FIELD_OFFSET}}, {{FIELD_NAME}});";
+ }
+ code_ += " }";
+ }
+ }
+
+ // Struct initializer (all fields required);
+ code_ += " #[inline]";
+ code_ +=
+ " pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a>) -> "
+ "{{STRUCT_NAME}}Builder<'a, 'b> {";
+ code_.SetValue("NUM_FIELDS", NumToString(struct_def.fields.vec.size()));
+ code_ += " let start = _fbb.start_table();";
+ code_ += " {{STRUCT_NAME}}Builder {";
+ code_ += " fbb_: _fbb,";
+ code_ += " start_: start,";
+ code_ += " }";
+ code_ += " }";
+
+ // finish() function.
+ code_ += " #[inline]";
+ code_ += " pub fn finish(self) -> "
+ "flatbuffers::WIPOffset<{{STRUCT_NAME}}<'a>> {";
+ code_ += " let o = self.fbb_.end_table(self.start_);";
+
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end(); ++it) {
+ const auto &field = **it;
+ if (!field.deprecated && field.required) {
+ code_.SetValue("FIELD_NAME", MakeSnakeCase(Name(field)));
+ code_.SetValue("OFFSET_NAME", GetFieldOffsetName(field));
+ code_ += " self.fbb_.required(o, {{STRUCT_NAME}}::{{OFFSET_NAME}},"
+ "\"{{FIELD_NAME}}\");";
+ }
+ }
+ code_ += " flatbuffers::WIPOffset::new(o.value())";
+ code_ += " }";
+ code_ += "}";
+ code_ += "";
+ }
+
+ // Generate functions to compare tables and structs by key. This function
+ // must only be called if the field key is defined.
+ void GenKeyFieldMethods(const FieldDef &field) {
+ FLATBUFFERS_ASSERT(field.key);
+
+ code_.SetValue("KEY_TYPE", GenTableAccessorFuncReturnType(field, ""));
+
+ code_ += " #[inline]";
+ code_ += " pub fn key_compare_less_than(&self, o: &{{STRUCT_NAME}}) -> "
+ " bool {";
+ code_ += " self.{{FIELD_NAME}}() < o.{{FIELD_NAME}}()";
+ code_ += " }";
+ code_ += "";
+ code_ += " #[inline]";
+ code_ += " pub fn key_compare_with_value(&self, val: {{KEY_TYPE}}) -> "
+ " ::std::cmp::Ordering {";
+ code_ += " let key = self.{{FIELD_NAME}}();";
+ code_ += " key.cmp(&val)";
+ code_ += " }";
+ }
+
+ // Generate functions for accessing the root table object. This function
+ // must only be called if the root table is defined.
+ void GenRootTableFuncs(const StructDef &struct_def) {
+ FLATBUFFERS_ASSERT(parser_.root_struct_def_ && "root table not defined");
+ auto name = Name(struct_def);
+
+ code_.SetValue("STRUCT_NAME", name);
+ code_.SetValue("STRUCT_NAME_SNAKECASE", MakeSnakeCase(name));
+ code_.SetValue("STRUCT_NAME_CAPS", MakeUpper(MakeSnakeCase(name)));
+
+ // The root datatype accessors:
+ code_ += "#[inline]";
+ code_ +=
+ "pub fn get_root_as_{{STRUCT_NAME_SNAKECASE}}<'a>(buf: &'a [u8])"
+ " -> {{STRUCT_NAME}}<'a> {";
+ code_ += " flatbuffers::get_root::<{{STRUCT_NAME}}<'a>>(buf)";
+ code_ += "}";
+ code_ += "";
+
+ code_ += "#[inline]";
+ code_ += "pub fn get_size_prefixed_root_as_{{STRUCT_NAME_SNAKECASE}}"
+ "<'a>(buf: &'a [u8]) -> {{STRUCT_NAME}}<'a> {";
+ code_ += " flatbuffers::get_size_prefixed_root::<{{STRUCT_NAME}}<'a>>"
+ "(buf)";
+ code_ += "}";
+ code_ += "";
+
+ if (parser_.file_identifier_.length()) {
+ // Declare the identifier
+ code_ += "pub const {{STRUCT_NAME_CAPS}}_IDENTIFIER: &'static str\\";
+ code_ += " = \"" + parser_.file_identifier_ + "\";";
+ code_ += "";
+
+ // Check if a buffer has the identifier.
+ code_ += "#[inline]";
+ code_ += "pub fn {{STRUCT_NAME_SNAKECASE}}_buffer_has_identifier\\";
+ code_ += "(buf: &[u8]) -> bool {";
+ code_ += " return flatbuffers::buffer_has_identifier(buf, \\";
+ code_ += "{{STRUCT_NAME_CAPS}}_IDENTIFIER, false);";
+ code_ += "}";
+ code_ += "";
+ code_ += "#[inline]";
+ code_ += "pub fn {{STRUCT_NAME_SNAKECASE}}_size_prefixed\\";
+ code_ += "_buffer_has_identifier(buf: &[u8]) -> bool {";
+ code_ += " return flatbuffers::buffer_has_identifier(buf, \\";
+ code_ += "{{STRUCT_NAME_CAPS}}_IDENTIFIER, true);";
+ code_ += "}";
+ code_ += "";
+ }
+
+ if (parser_.file_extension_.length()) {
+ // Return the extension
+ code_ += "pub const {{STRUCT_NAME_CAPS}}_EXTENSION: &'static str = \\";
+ code_ += "\"" + parser_.file_extension_ + "\";";
+ code_ += "";
+ }
+
+ // Finish a buffer with a given root object:
+ code_.SetValue("OFFSET_TYPELABEL", Name(struct_def) + "Offset");
+ code_ += "#[inline]";
+ code_ += "pub fn finish_{{STRUCT_NAME_SNAKECASE}}_buffer<'a, 'b>(";
+ code_ += " fbb: &'b mut flatbuffers::FlatBufferBuilder<'a>,";
+ code_ += " root: flatbuffers::WIPOffset<{{STRUCT_NAME}}<'a>>) {";
+ if (parser_.file_identifier_.length()) {
+ code_ += " fbb.finish(root, Some({{STRUCT_NAME_CAPS}}_IDENTIFIER));";
+ } else {
+ code_ += " fbb.finish(root, None);";
+ }
+ code_ += "}";
+ code_ += "";
+ code_ += "#[inline]";
+ code_ += "pub fn finish_size_prefixed_{{STRUCT_NAME_SNAKECASE}}_buffer"
+ "<'a, 'b>("
+ "fbb: &'b mut flatbuffers::FlatBufferBuilder<'a>, "
+ "root: flatbuffers::WIPOffset<{{STRUCT_NAME}}<'a>>) {";
+ if (parser_.file_identifier_.length()) {
+ code_ += " fbb.finish_size_prefixed(root, "
+ "Some({{STRUCT_NAME_CAPS}}_IDENTIFIER));";
+ } else {
+ code_ += " fbb.finish_size_prefixed(root, None);";
+ }
+ code_ += "}";
+ }
+
+ static void GenPadding(
+ const FieldDef &field, std::string *code_ptr, int *id,
+ const std::function<void(int bits, std::string *code_ptr, int *id)> &f) {
+ if (field.padding) {
+ for (int i = 0; i < 4; i++) {
+ if (static_cast<int>(field.padding) & (1 << i)) {
+ f((1 << i) * 8, code_ptr, id);
+ }
+ }
+ assert(!(field.padding & ~0xF));
+ }
+ }
+
+ static void PaddingDefinition(int bits, std::string *code_ptr, int *id) {
+ *code_ptr += " padding" + NumToString((*id)++) + "__: u" + \
+ NumToString(bits) + ",";
+ }
+
+ static void PaddingInitializer(int bits, std::string *code_ptr, int *id) {
+ (void)bits;
+ *code_ptr += "padding" + NumToString((*id)++) + "__: 0,";
+ }
+
+ // Generate an accessor struct with constructor for a flatbuffers struct.
+ void GenStruct(const StructDef &struct_def) {
+ // Generates manual padding and alignment.
+ // Variables are private because they contain little endian data on all
+ // platforms.
+ GenComment(struct_def.doc_comment);
+ code_.SetValue("ALIGN", NumToString(struct_def.minalign));
+ code_.SetValue("STRUCT_NAME", Name(struct_def));
+
+ code_ += "// struct {{STRUCT_NAME}}, aligned to {{ALIGN}}";
+ code_ += "#[repr(C, align({{ALIGN}}))]";
+
+ // PartialEq is useful to derive because we can correctly compare structs
+ // for equality by just comparing their underlying byte data. This doesn't
+ // hold for PartialOrd/Ord.
+ code_ += "#[derive(Clone, Copy, Debug, PartialEq)]";
+ code_ += "pub struct {{STRUCT_NAME}} {";
+
+ int padding_id = 0;
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end(); ++it) {
+ const auto &field = **it;
+ code_.SetValue("FIELD_TYPE", GetTypeGet(field.value.type));
+ code_.SetValue("FIELD_NAME", Name(field));
+ code_ += " {{FIELD_NAME}}_: {{FIELD_TYPE}},";
+
+ if (field.padding) {
+ std::string padding;
+ GenPadding(field, &padding, &padding_id, PaddingDefinition);
+ code_ += padding;
+ }
+ }
+
+ code_ += "} // pub struct {{STRUCT_NAME}}";
+
+ // Generate impls for SafeSliceAccess (because all structs are endian-safe),
+ // Follow for the value type, Follow for the reference type, Push for the
+ // value type, and Push for the reference type.
+ code_ += "impl flatbuffers::SafeSliceAccess for {{STRUCT_NAME}} {}";
+ code_ += "impl<'a> flatbuffers::Follow<'a> for {{STRUCT_NAME}} {";
+ code_ += " type Inner = &'a {{STRUCT_NAME}};";
+ code_ += " #[inline]";
+ code_ += " fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {";
+ code_ += " <&'a {{STRUCT_NAME}}>::follow(buf, loc)";
+ code_ += " }";
+ code_ += "}";
+ code_ += "impl<'a> flatbuffers::Follow<'a> for &'a {{STRUCT_NAME}} {";
+ code_ += " type Inner = &'a {{STRUCT_NAME}};";
+ code_ += " #[inline]";
+ code_ += " fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {";
+ code_ += " flatbuffers::follow_cast_ref::<{{STRUCT_NAME}}>(buf, loc)";
+ code_ += " }";
+ code_ += "}";
+ code_ += "impl<'b> flatbuffers::Push for {{STRUCT_NAME}} {";
+ code_ += " type Output = {{STRUCT_NAME}};";
+ code_ += " #[inline]";
+ code_ += " fn push(&self, dst: &mut [u8], _rest: &[u8]) {";
+ code_ += " let src = unsafe {";
+ code_ += " ::std::slice::from_raw_parts("
+ "self as *const {{STRUCT_NAME}} as *const u8, Self::size())";
+ code_ += " };";
+ code_ += " dst.copy_from_slice(src);";
+ code_ += " }";
+ code_ += "}";
+ code_ += "impl<'b> flatbuffers::Push for &'b {{STRUCT_NAME}} {";
+ code_ += " type Output = {{STRUCT_NAME}};";
+ code_ += "";
+ code_ += " #[inline]";
+ code_ += " fn push(&self, dst: &mut [u8], _rest: &[u8]) {";
+ code_ += " let src = unsafe {";
+ code_ += " ::std::slice::from_raw_parts("
+ "*self as *const {{STRUCT_NAME}} as *const u8, Self::size())";
+ code_ += " };";
+ code_ += " dst.copy_from_slice(src);";
+ code_ += " }";
+ code_ += "}";
+ code_ += "";
+ code_ += "";
+
+ // Generate a constructor that takes all fields as arguments.
+ code_ += "impl {{STRUCT_NAME}} {";
+ std::string arg_list;
+ std::string init_list;
+ padding_id = 0;
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end(); ++it) {
+ const auto &field = **it;
+ const auto member_name = Name(field) + "_";
+ const auto reference = StructMemberAccessNeedsCopy(field.value.type)
+ ? "" : "&'a ";
+ const auto arg_name = "_" + Name(field);
+ const auto arg_type = reference + GetTypeGet(field.value.type);
+
+ if (it != struct_def.fields.vec.begin()) {
+ arg_list += ", ";
+ }
+ arg_list += arg_name + ": ";
+ arg_list += arg_type;
+ init_list += " " + member_name;
+ if (StructMemberAccessNeedsCopy(field.value.type)) {
+ init_list += ": " + arg_name + ".to_little_endian(),\n";
+ } else {
+ init_list += ": *" + arg_name + ",\n";
+ }
+ }
+
+ code_.SetValue("ARG_LIST", arg_list);
+ code_.SetValue("INIT_LIST", init_list);
+ code_ += " pub fn new<'a>({{ARG_LIST}}) -> Self {";
+ code_ += " {{STRUCT_NAME}} {";
+ code_ += "{{INIT_LIST}}";
+ padding_id = 0;
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end(); ++it) {
+ const auto &field = **it;
+ if (field.padding) {
+ std::string padding;
+ GenPadding(field, &padding, &padding_id, PaddingInitializer);
+ code_ += " " + padding;
+ }
+ }
+ code_ += " }";
+ code_ += " }";
+
+ // Generate accessor methods for the struct.
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end(); ++it) {
+ const auto &field = **it;
+
+ auto field_type = TableBuilderArgsAddFuncType(field, "'a");
+ auto member = "self." + Name(field) + "_";
+ auto value = StructMemberAccessNeedsCopy(field.value.type) ?
+ member + ".from_little_endian()" : member;
+
+ code_.SetValue("FIELD_NAME", Name(field));
+ code_.SetValue("FIELD_TYPE", field_type);
+ code_.SetValue("FIELD_VALUE", value);
+ code_.SetValue("REF", IsStruct(field.value.type) ? "&" : "");
+
+ GenComment(field.doc_comment, " ");
+ code_ += " pub fn {{FIELD_NAME}}<'a>(&'a self) -> {{FIELD_TYPE}} {";
+ code_ += " {{REF}}{{FIELD_VALUE}}";
+ code_ += " }";
+
+ // Generate a comparison function for this field if it is a key.
+ if (field.key) {
+ GenKeyFieldMethods(field);
+ }
+ }
+ code_ += "}";
+ code_ += "";
+ }
+
+ void GenNamespaceImports(const int white_spaces) {
+ std::string indent = std::string(white_spaces, ' ');
+ code_ += "";
+ code_ += indent + "use std::mem;";
+ code_ += indent + "use std::cmp::Ordering;";
+ code_ += "";
+ code_ += indent + "extern crate flatbuffers;";
+ code_ += indent + "use self::flatbuffers::EndianScalar;";
+ }
+
+ // Set up the correct namespace. This opens a namespace if the current
+ // namespace is different from the target namespace. This function
+ // closes and opens the namespaces only as necessary.
+ //
+ // The file must start and end with an empty (or null) namespace so that
+ // namespaces are properly opened and closed.
+ void SetNameSpace(const Namespace *ns) {
+ if (cur_name_space_ == ns) { return; }
+
+ // Compute the size of the longest common namespace prefix.
+ // If cur_name_space is A::B::C::D and ns is A::B::E::F::G,
+ // the common prefix is A::B:: and we have old_size = 4, new_size = 5
+ // and common_prefix_size = 2
+ size_t old_size = cur_name_space_ ? cur_name_space_->components.size() : 0;
+ size_t new_size = ns ? ns->components.size() : 0;
+
+ size_t common_prefix_size = 0;
+ while (common_prefix_size < old_size && common_prefix_size < new_size &&
+ ns->components[common_prefix_size] ==
+ cur_name_space_->components[common_prefix_size]) {
+ common_prefix_size++;
+ }
+
+ // Close cur_name_space in reverse order to reach the common prefix.
+ // In the previous example, D then C are closed.
+ for (size_t j = old_size; j > common_prefix_size; --j) {
+ code_ += "} // pub mod " + cur_name_space_->components[j - 1];
+ }
+ if (old_size != common_prefix_size) { code_ += ""; }
+
+ // open namespace parts to reach the ns namespace
+ // in the previous example, E, then F, then G are opened
+ for (auto j = common_prefix_size; j != new_size; ++j) {
+ code_ += "#[allow(unused_imports, dead_code)]";
+ code_ += "pub mod " + MakeSnakeCase(ns->components[j]) + " {";
+ // Generate local namespace imports.
+ GenNamespaceImports(2);
+ }
+ if (new_size != common_prefix_size) { code_ += ""; }
+
+ cur_name_space_ = ns;
+ }
+};
+
+} // namespace rust
+
+bool GenerateRust(const Parser &parser, const std::string &path,
+ const std::string &file_name) {
+ rust::RustGenerator generator(parser, path, file_name);
+ return generator.generate();
+}
+
+std::string RustMakeRule(const Parser &parser, const std::string &path,
+ const std::string &file_name) {
+ std::string filebase =
+ flatbuffers::StripPath(flatbuffers::StripExtension(file_name));
+ std::string make_rule = GeneratedFileName(path, filebase) + ": ";
+
+ auto included_files = parser.GetIncludedFilesRecursive(file_name);
+ for (auto it = included_files.begin(); it != included_files.end(); ++it) {
+ make_rule += " " + *it;
+ }
+ return make_rule;
+}
+
+} // namespace flatbuffers
+
+// TODO(rw): Generated code should import other generated files.
+// TODO(rw): Generated code should refer to namespaces in included files in a
+// way that makes them referrable.
+// TODO(rw): Generated code should indent according to nesting level.
+// TODO(rw): Generated code should generate endian-safe Debug impls.
+// TODO(rw): Generated code could use a Rust-only enum type to access unions,
+// instead of making the user use _type() to manually switch.