| /* |
| * 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. |