| /* |
| * 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 { |
| |
| // 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 += CharToLower(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 += CharToLower(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 += CharToUpper(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 (IsString(type)) { |
| return ftString; |
| } else if (type.base_type == BASE_TYPE_STRUCT) { |
| if (type.struct_def->fixed) { |
| return ftStruct; |
| } else { |
| return ftTable; |
| } |
| } else if (IsVector(type)) { |
| 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; |
| } |
| } |
| |
| bool IsBitFlagsEnum(const EnumDef &enum_def) { |
| return enum_def.attributes.Lookup("bit_flags") != nullptr; |
| } |
| bool IsBitFlagsEnum(const FieldDef &field) { |
| EnumDef* ed = field.value.type.enum_def; |
| return ed && IsBitFlagsEnum(*ed); |
| } |
| |
| 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, "", "::", "rs"), |
| 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, |
| |
| // used by Enum constants |
| "ENUM_MAX", "ENUM_MIN", "ENUM_VALUES", |
| }; |
| 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_, parser_.opts); |
| 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; |
| } |
| |
| 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, ...) \ |
| #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"); |
| } |
| |
| // clang-format off |
| static const char *ctypename[] = { |
| #define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, \ |
| RTYPE, ...) \ |
| #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 GetEnumValue(const EnumDef &enum_def, |
| const EnumVal &enum_val) const { |
| return Name(enum_def) + "::" + Name(enum_val); |
| } |
| |
| // 1 suffix since old C++ can't figure out the overload. |
| void ForAllEnumValues1(const EnumDef &enum_def, |
| std::function<void(const EnumVal&)> cb) { |
| for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); ++it) { |
| const auto &ev = **it; |
| code_.SetValue("VARIANT", Name(ev)); |
| code_.SetValue("VALUE", enum_def.ToString(ev)); |
| cb(ev); |
| } |
| } |
| void ForAllEnumValues(const EnumDef &enum_def, std::function<void()> cb) { |
| std::function<void(const EnumVal&)> wrapped = [&](const EnumVal& unused) { |
| (void) unused; |
| cb(); |
| }; |
| ForAllEnumValues1(enum_def, wrapped); |
| } |
| // 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)); |
| code_.SetValue("ENUM_NAME_SNAKE", MakeSnakeCase(Name(enum_def))); |
| code_.SetValue("ENUM_NAME_CAPS", MakeUpper(MakeSnakeCase(Name(enum_def)))); |
| const EnumVal *minv = enum_def.MinValue(); |
| const EnumVal *maxv = enum_def.MaxValue(); |
| FLATBUFFERS_ASSERT(minv && maxv); |
| code_.SetValue("ENUM_MIN_BASE_VALUE", enum_def.ToString(*minv)); |
| code_.SetValue("ENUM_MAX_BASE_VALUE", enum_def.ToString(*maxv)); |
| |
| if (IsBitFlagsEnum(enum_def)) { |
| // Defer to the convenient and canonical bitflags crate. We declare it in a |
| // module to #allow camel case constants in a smaller scope. This matches |
| // Flatbuffers c-modeled enums where variants are associated constants but |
| // in camel case. |
| code_ += "#[allow(non_upper_case_globals)]"; |
| code_ += "mod bitflags_{{ENUM_NAME_SNAKE}} {"; |
| code_ += " flatbuffers::bitflags::bitflags! {"; |
| GenComment(enum_def.doc_comment, " "); |
| code_ += " pub struct {{ENUM_NAME}}: {{BASE_TYPE}} {"; |
| ForAllEnumValues1(enum_def, [&](const EnumVal &ev){ |
| this->GenComment(ev.doc_comment, " "); |
| code_ += " const {{VARIANT}} = {{VALUE}};"; |
| }); |
| code_ += " }"; |
| code_ += " }"; |
| code_ += "}"; |
| code_ += "pub use self::bitflags_{{ENUM_NAME_SNAKE}}::{{ENUM_NAME}};"; |
| code_ += ""; |
| |
| // Generate Follow and Push so we can serialize and stuff. |
| 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_ += " let bits = flatbuffers::read_scalar_at::<{{BASE_TYPE}}>(buf, loc);"; |
| code_ += " unsafe { Self::from_bits_unchecked(bits) }"; |
| 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::<{{BASE_TYPE}}>" |
| "(dst, self.bits());"; |
| code_ += " }"; |
| code_ += "}"; |
| code_ += ""; |
| code_ += "impl flatbuffers::EndianScalar for {{ENUM_NAME}} {"; |
| code_ += " #[inline]"; |
| code_ += " fn to_little_endian(self) -> Self {"; |
| code_ += " let bits = {{BASE_TYPE}}::to_le(self.bits());"; |
| code_ += " unsafe { Self::from_bits_unchecked(bits) }"; |
| code_ += " }"; |
| code_ += " #[inline]"; |
| code_ += " fn from_little_endian(self) -> Self {"; |
| code_ += " let bits = {{BASE_TYPE}}::from_le(self.bits());"; |
| code_ += " unsafe { Self::from_bits_unchecked(bits) }"; |
| code_ += " }"; |
| code_ += "}"; |
| code_ += ""; |
| return; |
| } |
| |
| // Deprecated associated constants; |
| code_ += "#[deprecated(since = \"1.13\", note = \"Use associated constants" |
| " instead. This will no longer be generated in 2021.\")]"; |
| code_ += "pub const ENUM_MIN_{{ENUM_NAME_CAPS}}: {{BASE_TYPE}}" |
| " = {{ENUM_MIN_BASE_VALUE}};"; |
| code_ += "#[deprecated(since = \"1.13\", note = \"Use associated constants" |
| " instead. This will no longer be generated in 2021.\")]"; |
| code_ += "pub const ENUM_MAX_{{ENUM_NAME_CAPS}}: {{BASE_TYPE}}" |
| " = {{ENUM_MAX_BASE_VALUE}};"; |
| auto num_fields = NumToString(enum_def.size()); |
| code_ += "#[deprecated(since = \"1.13\", note = \"Use associated constants" |
| " instead. This will no longer be generated in 2021.\")]"; |
| code_ += "#[allow(non_camel_case_types)]"; |
| code_ += "pub const ENUM_VALUES_{{ENUM_NAME_CAPS}}: [{{ENUM_NAME}}; " + |
| num_fields + "] = ["; |
| ForAllEnumValues1(enum_def, [&](const EnumVal &ev){ |
| code_ += " " + GetEnumValue(enum_def, ev) + ","; |
| }); |
| code_ += "];"; |
| code_ += ""; |
| |
| GenComment(enum_def.doc_comment); |
| code_ += |
| "#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]"; |
| code_ += "#[repr(transparent)]"; |
| code_ += "pub struct {{ENUM_NAME}}(pub {{BASE_TYPE}});"; |
| code_ += "#[allow(non_upper_case_globals)]"; |
| code_ += "impl {{ENUM_NAME}} {"; |
| ForAllEnumValues1(enum_def, [&](const EnumVal &ev){ |
| this->GenComment(ev.doc_comment, " "); |
| code_ += " pub const {{VARIANT}}: Self = Self({{VALUE}});"; |
| }); |
| code_ += ""; |
| // Generate Associated constants |
| code_ += " pub const ENUM_MIN: {{BASE_TYPE}} = {{ENUM_MIN_BASE_VALUE}};"; |
| code_ += " pub const ENUM_MAX: {{BASE_TYPE}} = {{ENUM_MAX_BASE_VALUE}};"; |
| code_ += " pub const ENUM_VALUES: &'static [Self] = &["; |
| ForAllEnumValues(enum_def, [&](){ |
| code_ += " Self::{{VARIANT}},"; |
| }); |
| code_ += " ];"; |
| code_ += " /// Returns the variant's name or \"\" if unknown."; |
| code_ += " pub fn variant_name(self) -> Option<&'static str> {"; |
| code_ += " match self {"; |
| ForAllEnumValues(enum_def, [&](){ |
| code_ += " Self::{{VARIANT}} => Some(\"{{VARIANT}}\"),"; |
| }); |
| code_ += " _ => None,"; |
| code_ += " }"; |
| code_ += " }"; |
| code_ += "}"; |
| |
| // Generate Debug. Unknown variants are printed like "<UNKNOWN 42>". |
| code_ += "impl std::fmt::Debug for {{ENUM_NAME}} {"; |
| code_ += " fn fmt(&self, f: &mut std::fmt::Formatter) ->" |
| " std::fmt::Result {"; |
| code_ += " if let Some(name) = self.variant_name() {"; |
| code_ += " f.write_str(name)"; |
| code_ += " } else {"; |
| code_ += " f.write_fmt(format_args!(\"<UNKNOWN {:?}>\", self.0))"; |
| code_ += " }"; |
| code_ += " }"; |
| code_ += "}"; |
| |
| // Generate Follow and Push so we can serialize and stuff. |
| 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_ += " Self(flatbuffers::read_scalar_at::<{{BASE_TYPE}}>(buf, loc))"; |
| 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::<{{BASE_TYPE}}>" |
| "(dst, self.0);"; |
| code_ += " }"; |
| code_ += "}"; |
| code_ += ""; |
| code_ += "impl flatbuffers::EndianScalar for {{ENUM_NAME}} {"; |
| code_ += " #[inline]"; |
| code_ += " fn to_little_endian(self) -> Self {"; |
| code_ += " Self({{BASE_TYPE}}::to_le(self.0))"; |
| code_ += " }"; |
| code_ += " #[inline]"; |
| code_ += " fn from_little_endian(self) -> Self {"; |
| code_ += " Self({{BASE_TYPE}}::from_le(self.0))"; |
| code_ += " }"; |
| 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 GetDefaultScalarValue(const FieldDef &field) { |
| switch (GetFullType(field.value.type)) { |
| case ftInteger: |
| case ftFloat: { |
| return field.optional ? "None" : field.value.constant; |
| } |
| case ftBool: { |
| return field.optional ? "None" |
| : field.value.constant == "0" ? "false" : "true"; |
| } |
| case ftUnionKey: |
| case ftEnumKey: { |
| if (field.optional) { |
| return "None"; |
| } |
| auto ev = field.value.type.enum_def->FindByValue(field.value.constant); |
| assert(ev); |
| return WrapInNameSpace(field.value.type.enum_def->defined_namespace, |
| GetEnumValue(*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 field.optional ? "Option<" + typname + ">" : 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 field.optional ? "Option<" + typname + ">" : typname; |
| } |
| case ftUnionValue: { |
| return "Option<flatbuffers::WIPOffset<flatbuffers::UnionWIPOffset>>"; |
| } |
| |
| case ftVectorOfInteger: |
| case ftVectorOfBool: |
| case ftVectorOfFloat: { |
| const auto typname = GetTypeBasic(type.VectorType()); |
| return "Option<flatbuffers::WIPOffset<flatbuffers::Vector<" + lifetime + |
| ", " + typname + ">>>"; |
| } |
| 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 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 ftVectorOfBool: |
| case ftVectorOfFloat: { |
| const auto typname = GetTypeBasic(type.VectorType()); |
| return "flatbuffers::WIPOffset<flatbuffers::Vector<" + lifetime + ", " + |
| typname + ">>"; |
| } |
| 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 "&" + typname + ""; |
| } |
| case ftTable: { |
| const auto typname = WrapInNameSpace(*type.struct_def); |
| return "flatbuffers::WIPOffset<" + typname + "<" + lifetime + ">>"; |
| } |
| case ftInteger: |
| case ftBool: |
| case ftFloat: { |
| return GetTypeBasic(type); |
| } |
| 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 ftBool: |
| case ftFloat: { |
| const auto typname = GetTypeBasic(field.value.type); |
| return (field.optional ? "self.fbb_.push_slot_always::<" |
| : "self.fbb_.push_slot::<") + |
| typname + ">"; |
| } |
| case ftEnumKey: |
| case ftUnionKey: { |
| const auto underlying_typname = GetTypeBasic(type); |
| return (field.optional ? |
| "self.fbb_.push_slot_always::<" : |
| "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: |
| case ftBool: { |
| const auto typname = GetTypeBasic(type); |
| return field.optional ? "Option<" + typname + ">" : typname; |
| } |
| 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 field.optional ? "Option<" + typname + ">" : typname; |
| } |
| |
| case ftUnionValue: { |
| return WrapInOptionIfNotRequired("flatbuffers::Table<" + lifetime + ">", |
| field.required); |
| } |
| case ftString: { |
| return WrapInOptionIfNotRequired("&" + lifetime + " str", |
| field.required); |
| } |
| case ftVectorOfInteger: |
| case ftVectorOfBool: |
| 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 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); |
| if (field.optional) { |
| return "self._tab.get::<" + typname + ">(" + offset_name + ", None)"; |
| } else { |
| 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 std::string typname = WrapInNameSpace(*type.enum_def); |
| const std::string default_value = GetDefaultScalarValue(field); |
| if (field.optional) { |
| return "self._tab.get::<" + typname + ">(" + offset_name + ", None)"; |
| } else { |
| 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 ftVectorOfBool: |
| 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 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 FieldDef &field) { |
| if (field.optional) return true; |
| switch (GetFullType(field.value.type)) { |
| case ftInteger: |
| case ftFloat: |
| case ftBool: |
| case ftEnumKey: |
| case ftUnionKey: return false; |
| default: return true; |
| } |
| } |
| |
| // Generates a fully-qualified name getter for use with --gen-name-strings |
| void GenFullyQualifiedNameGetter(const StructDef &struct_def, |
| const std::string &name) { |
| code_ += " pub const fn get_fully_qualified_name() -> &'static str {"; |
| code_ += " \"" + |
| struct_def.defined_namespace->GetFullyQualifiedName(name) + "\""; |
| code_ += " }"; |
| code_ += ""; |
| } |
| |
| void ForAllUnionVariantsBesidesNone( |
| const EnumDef &def, |
| std::function<void(const EnumVal &ev)> cb |
| ) { |
| FLATBUFFERS_ASSERT(def.is_union); |
| |
| for (auto it = def.Vals().begin(); it != def.Vals().end(); ++it) { |
| const EnumVal & ev = **it; |
| // TODO(cneo): Can variants be deprecated, should we skip them? |
| if (ev.union_type.base_type == BASE_TYPE_NONE) { continue; } |
| code_.SetValue( |
| "U_ELEMENT_ENUM_TYPE", |
| WrapInNameSpace(def.defined_namespace, GetEnumValue(def, ev))); |
| code_.SetValue("U_ELEMENT_TABLE_TYPE", |
| WrapInNameSpace(ev.union_type.struct_def->defined_namespace, |
| ev.union_type.struct_def->name)); |
| code_.SetValue("U_ELEMENT_NAME", MakeSnakeCase(Name(ev))); |
| cb(ev); |
| } |
| } |
| |
| void ForAllTableFields( |
| const StructDef &struct_def, |
| std::function<void(const FieldDef&)> cb, bool reversed=false) { |
| // TODO(cneo): Remove `reversed` overload. It's only here to minimize the |
| // diff when refactoring to the `ForAllX` helper functions. |
| auto go = [&](const FieldDef& field) { |
| if (field.deprecated) return; |
| code_.SetValue("OFFSET_NAME", GetFieldOffsetName(field)); |
| code_.SetValue("OFFSET_VALUE", NumToString(field.value.offset)); |
| code_.SetValue("FIELD_NAME", Name(field)); |
| code_.SetValue("DEFAULT_VALUE", GetDefaultScalarValue(field)); |
| cb(field); |
| }; |
| const auto &fields = struct_def.fields.vec; |
| if (reversed) { |
| for (auto it = fields.rbegin(); it != fields.rend(); ++it) go(**it); |
| } else { |
| for (auto it = fields.begin(); it != fields.end(); ++it) go(**it); |
| } |
| } |
| // 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, 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 { _tab: flatbuffers::Table { buf, loc } }"; |
| code_ += " }"; |
| code_ += "}"; |
| code_ += ""; |
| code_ += "impl<'a> {{STRUCT_NAME}}<'a> {"; |
| |
| if (parser_.opts.generate_name_strings) { |
| GenFullyQualifiedNameGetter(struct_def, struct_def.name); |
| } |
| |
| 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) { |
| ForAllTableFields(struct_def, [&](const FieldDef &field) { |
| if (struct_def.sortbysize && size != SizeOf(field.value.type.base_type)) |
| return; |
| if (TableFieldReturnsOption(field)) { |
| code_ += |
| " if let Some(x) = args.{{FIELD_NAME}} " |
| "{ builder.add_{{FIELD_NAME}}(x); }"; |
| } else { |
| code_ += " builder.add_{{FIELD_NAME}}(args.{{FIELD_NAME}});"; |
| } |
| }, /*reverse=*/true); |
| } |
| code_ += " builder.finish()"; |
| code_ += " }"; |
| code_ += ""; |
| |
| // Generate field id constants. |
| ForAllTableFields(struct_def, [&](const FieldDef &unused){ |
| (void) unused; |
| code_ += " pub const {{OFFSET_NAME}}: flatbuffers::VOffsetT = " |
| "{{OFFSET_VALUE}};"; |
| }); |
| if (struct_def.fields.vec.size() > 0) 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); |
| ForAllTableFields(struct_def, [&](const FieldDef &field) { |
| code_.SetValue("RETURN_TYPE", |
| GenTableAccessorFuncReturnType(field, "'a")); |
| code_.SetValue("FUNC_BODY", |
| GenTableAccessorFuncBody(field, "'a", offset_prefix)); |
| |
| this->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. |
| |
| code_.SetValue("NESTED", WrapInNameSpace(*nested_root)); |
| code_ += |
| " pub fn {{FIELD_NAME}}_nested_flatbuffer(&'a self) -> \\"; |
| if (field.required) { |
| code_ += "{{NESTED}}<'a> {"; |
| code_ += " let data = self.{{FIELD_NAME}}();"; |
| code_ += " use flatbuffers::Follow;"; |
| code_ += " <flatbuffers::ForwardsUOffset<{{NESTED}}<'a>>>" |
| "::follow(data, 0)"; |
| } else { |
| code_ += "Option<{{NESTED}}<'a>> {"; |
| code_ += " self.{{FIELD_NAME}}().map(|data| {"; |
| code_ += " use flatbuffers::Follow;"; |
| code_ += " <flatbuffers::ForwardsUOffset<{{NESTED}}<'a>>>" |
| "::follow(data, 0)"; |
| code_ += " })"; |
| } |
| code_ += " }"; |
| } |
| }); |
| |
| // Explicit specializations for union accessors |
| ForAllTableFields(struct_def, [&](const FieldDef &field) { |
| if (field.value.type.base_type != BASE_TYPE_UNION) return; |
| code_.SetValue("FIELD_TYPE_FIELD_NAME", field.name); |
| ForAllUnionVariantsBesidesNone( |
| *field.value.type.enum_def, [&](const EnumVal &unused){ |
| (void) unused; |
| code_ += " #[inline]"; |
| code_ += " #[allow(non_snake_case)]"; |
| code_ += |
| " pub fn {{FIELD_NAME}}_as_{{U_ELEMENT_NAME}}(&self) -> " |
| "Option<{{U_ELEMENT_TABLE_TYPE}}<'a>> {"; |
| // If the user defined schemas name a field that clashes with a |
| // language reserved word, flatc will try to escape the field name by |
| // appending an underscore. This works well for most cases, except |
| // one. When generating union accessors (and referring to them |
| // internally within the code generated here), an extra underscore |
| // will be appended to the name, causing build failures. |
| // |
| // This only happens when unions have members that overlap with |
| // language reserved words. |
| // |
| // To avoid this problem the type field name is used unescaped here: |
| code_ += |
| " if self.{{FIELD_TYPE_FIELD_NAME}}_type() == " |
| "{{U_ELEMENT_ENUM_TYPE}} {"; |
| |
| // The following logic is not tested in the integration test, |
| // as of April 10, 2020 |
| if (field.required) { |
| code_ += " let u = self.{{FIELD_NAME}}();"; |
| code_ += " Some({{U_ELEMENT_TABLE_TYPE}}::init_from_table(u))"; |
| } else { |
| code_ += |
| " self.{{FIELD_NAME}}().map(" |
| "{{U_ELEMENT_TABLE_TYPE}}::init_from_table)"; |
| } |
| 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}} {"; |
| ForAllTableFields(struct_def, [&](const FieldDef &field) { |
| code_.SetValue("PARAM_TYPE", TableBuilderArgsDefnType(field, "'a")); |
| code_ += " pub {{FIELD_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 {"; |
| ForAllTableFields(struct_def, [&](const FieldDef &field) { |
| code_ += " {{FIELD_NAME}}: {{DEFAULT_VALUE}},\\"; |
| code_ += field.required ? " // required field" : ""; |
| }); |
| 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> {"; |
| ForAllTableFields(struct_def, [&](const FieldDef &field) { |
| 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_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 && !field.optional) { |
| code_ += |
| " {{FUNC_BODY}}({{FIELD_OFFSET}}, {{FIELD_NAME}}, " |
| "{{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_);"; |
| |
| ForAllTableFields(struct_def, [&](const FieldDef &field) { |
| if (!field.required) return; |
| code_ += |
| " self.fbb_.required(o, {{STRUCT_NAME}}::{{OFFSET_NAME}}," |
| "\"{{FIELD_NAME}}\");"; |
| }); |
| code_ += " flatbuffers::WIPOffset::new(o.value())"; |
| code_ += " }"; |
| code_ += "}"; |
| code_ += ""; |
| |
| code_ += "impl std::fmt::Debug for {{STRUCT_NAME}}<'_> {"; |
| code_ += " fn fmt(&self, f: &mut std::fmt::Formatter<'_>" |
| ") -> std::fmt::Result {"; |
| code_ += " let mut ds = f.debug_struct(\"{{STRUCT_NAME}}\");"; |
| ForAllTableFields(struct_def, [&](const FieldDef &field) { |
| if (GetFullType(field.value.type) == ftUnionValue) { |
| // Generate a match statement to handle unions properly. |
| code_.SetValue("KEY_TYPE", GenTableAccessorFuncReturnType(field, "")); |
| code_.SetValue("FIELD_TYPE_FIELD_NAME", field.name); |
| code_.SetValue("UNION_ERR", "&\"InvalidFlatbuffer: Union discriminant" |
| " does not match value.\""); |
| |
| code_ += " match self.{{FIELD_NAME}}_type() {"; |
| ForAllUnionVariantsBesidesNone(*field.value.type.enum_def, |
| [&](const EnumVal &unused){ |
| (void) unused; |
| code_ += " {{U_ELEMENT_ENUM_TYPE}} => {"; |
| code_ += " if let Some(x) = self.{{FIELD_TYPE_FIELD_NAME}}_as_" |
| "{{U_ELEMENT_NAME}}() {"; |
| code_ += " ds.field(\"{{FIELD_NAME}}\", &x)"; |
| code_ += " } else {"; |
| code_ += " ds.field(\"{{FIELD_NAME}}\", {{UNION_ERR}})"; |
| code_ += " }"; |
| code_ += " },"; |
| }); |
| code_ += " _ => { "; |
| code_ += " let x: Option<()> = None;"; |
| code_ += " ds.field(\"{{FIELD_NAME}}\", &x)"; |
| code_ += " },"; |
| code_ += " };"; |
| } else { |
| // Most fields. |
| code_ += " ds.field(\"{{FIELD_NAME}}\", &self.{{FIELD_NAME}}());"; |
| } |
| }); |
| code_ += " ds.finish()"; |
| 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 |
| // (no lifetime needed as constants have static lifetimes by default) |
| code_ += "pub const {{STRUCT_NAME_CAPS}}_IDENTIFIER: &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_ += " 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_ += " 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: &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,"; |
| } |
| |
| void ForAllStructFields( |
| const StructDef &struct_def, |
| std::function<void(const FieldDef &field)> cb |
| ) { |
| 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)); |
| cb(field); |
| } |
| } |
| // 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, PartialEq)]"; |
| code_ += "pub struct {{STRUCT_NAME}} {"; |
| |
| int padding_id = 0; |
| ForAllStructFields(struct_def, [&](const FieldDef &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}}"; |
| |
| // Debug for structs. |
| code_ += "impl std::fmt::Debug for {{STRUCT_NAME}} {"; |
| code_ += " fn fmt(&self, f: &mut std::fmt::Formatter" |
| ") -> std::fmt::Result {"; |
| code_ += " f.debug_struct(\"{{STRUCT_NAME}}\")"; |
| ForAllStructFields(struct_def, [&](const FieldDef &unused) { |
| (void) unused; |
| code_ += " .field(\"{{FIELD_NAME}}\", &self.{{FIELD_NAME}}())"; |
| }); |
| code_ += " .finish()"; |
| code_ += " }"; |
| code_ += "}"; |
| code_ += ""; |
| |
| |
| // 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}} {"; |
| // TODO(cneo): Stop generating args on one line. Make it simpler. |
| bool first_arg = true; |
| code_ += " pub fn new(\\"; |
| ForAllStructFields(struct_def, [&](const FieldDef &field) { |
| if (first_arg) first_arg = false; else code_ += ", \\"; |
| code_.SetValue("REF", IsStruct(field.value.type) ? "&" : ""); |
| code_ += "_{{FIELD_NAME}}: {{REF}}{{FIELD_TYPE}}\\"; |
| }); |
| code_ += ") -> Self {"; |
| code_ += " {{STRUCT_NAME}} {"; |
| |
| ForAllStructFields(struct_def, [&](const FieldDef &field) { |
| const bool is_struct = IsStruct(field.value.type); |
| code_.SetValue("DEREF", is_struct ? "*" : ""); |
| code_.SetValue("TO_LE", is_struct ? "" : ".to_little_endian()"); |
| code_ += " {{FIELD_NAME}}_: {{DEREF}}_{{FIELD_NAME}}{{TO_LE}},"; |
| }); |
| code_ += ""; |
| |
| // TODO(cneo): Does this padding even work? Why after all the fields? |
| padding_id = 0; |
| ForAllStructFields(struct_def, [&](const FieldDef &field) { |
| if (field.padding) { |
| std::string padding; |
| GenPadding(field, &padding, &padding_id, PaddingInitializer); |
| code_ += " " + padding; |
| } |
| }); |
| code_ += " }"; |
| code_ += " }"; |
| |
| if (parser_.opts.generate_name_strings) { |
| GenFullyQualifiedNameGetter(struct_def, struct_def.name); |
| } |
| |
| // Generate accessor methods for the struct. |
| ForAllStructFields(struct_def, [&](const FieldDef &field) { |
| const bool is_struct = IsStruct(field.value.type); |
| code_.SetValue("REF", is_struct ? "&" : ""); |
| code_.SetValue("FROM_LE", is_struct ? "" : ".from_little_endian()"); |
| |
| this->GenComment(field.doc_comment, " "); |
| code_ += " pub fn {{FIELD_NAME}}(&self) -> {{REF}}{{FIELD_TYPE}} {"; |
| code_ += " {{REF}}self.{{FIELD_NAME}}_{{FROM_LE}}"; |
| 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) { |
| if (white_spaces == 0) { |
| code_ += "#![allow(unused_imports, dead_code)]"; |
| } |
| std::string indent = std::string(white_spaces, ' '); |
| code_ += ""; |
| if (!parser_.opts.generate_all) { |
| for (auto it = parser_.included_files_.begin(); |
| it != parser_.included_files_.end(); ++it) { |
| if (it->second.empty()) continue; |
| auto noext = flatbuffers::StripExtension(it->second); |
| auto basename = flatbuffers::StripPath(noext); |
| |
| code_ += indent + "use crate::" + basename + "_generated::*;"; |
| } |
| } |
| |
| 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)); |
| rust::RustGenerator generator(parser, path, file_name); |
| std::string make_rule = |
| generator.GeneratedFileName(path, filebase, parser.opts) + ": "; |
| |
| 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. |
| // TODO(maxburke): There should be test schemas added that use language |
| // keywords as fields of structs, tables, unions, enums, to make sure |
| // that internal code generated references escaped names correctly. |
| // TODO(maxburke): We should see if there is a more flexible way of resolving |
| // module paths for use declarations. Right now if schemas refer to |
| // other flatbuffer files, the include paths in emitted Rust bindings |
| // are crate-relative which may undesirable. |