mirror of
https://github.com/google/flatbuffers.git
synced 2026-06-05 04:58:57 +00:00
Rework how Rust generated files are laid out (#6731)
* Refactored Rust Generated code into a module directory. Each symbol will be generated into one file and then imported into a module. This breaks the "out_dir" pattern where some users would generate code in their target/ directory. Also, these objects are best used in their own module. It will be hard for users to share their own module structure with flatbuffers namespaces. There may be solutions to these drawbacks but that should be discussed. I don't want to overengineer here. * shadow error * try fix .bat file * fix .bat 2 * Restore accidentally deleted files * Fixed some DONOTSUBMITs and made Rust outdir pattern use symlinks. * fixed binary files * git clang format * make generated onefiles not public and fix .bat * reduced diff with master in generate_code.sh * fix shadowed variable * add object api flags to .bat * space * Removed extern crate and extra & * use statement * more clippy lints * format * Undo extern crate -> use change, it actually matters to our tests Co-authored-by: Casper Neo <cneo@google.com>
This commit is contained in:
@@ -210,6 +210,63 @@ bool IsOptionalToBuilder(const FieldDef &field) {
|
||||
return field.IsOptional() || !IsScalar(field.value.type.base_type);
|
||||
}
|
||||
|
||||
bool GenerateRustModuleRootFile(const Parser &parser,
|
||||
const std::string &output_dir) {
|
||||
// We gather the symbols into a tree of namespaces (which are rust mods) and
|
||||
// generate a file that gathers them all.
|
||||
struct Module {
|
||||
std::map<std::string, Module> sub_modules;
|
||||
std::vector<std::string> generated_files;
|
||||
// Add a symbol into the tree.
|
||||
void Insert(const Definition *s, const std::string suffix) {
|
||||
const Definition &symbol = *s;
|
||||
Module *current_module = this;
|
||||
for (auto it = symbol.defined_namespace->components.begin();
|
||||
it != symbol.defined_namespace->components.end(); it++) {
|
||||
std::string ns_component = MakeSnakeCase(*it);
|
||||
current_module = ¤t_module->sub_modules[ns_component];
|
||||
}
|
||||
current_module->generated_files.push_back(MakeSnakeCase(symbol.name) +
|
||||
suffix);
|
||||
}
|
||||
// Recursively create the importer file.
|
||||
void GenerateImports(CodeWriter &code) {
|
||||
for (auto it = sub_modules.begin(); it != sub_modules.end(); it++) {
|
||||
code += "pub mod " + it->first + " {";
|
||||
code.IncrementIdentLevel();
|
||||
code += "use super::*;";
|
||||
it->second.GenerateImports(code);
|
||||
code.DecrementIdentLevel();
|
||||
code += "} // " + it->first;
|
||||
}
|
||||
for (auto it = generated_files.begin(); it != generated_files.end();
|
||||
it++) {
|
||||
code += "mod " + *it + ";";
|
||||
code += "pub use self::" + *it + "::*;";
|
||||
}
|
||||
}
|
||||
};
|
||||
Module root_module;
|
||||
for (auto it = parser.enums_.vec.begin(); it != parser.enums_.vec.end();
|
||||
it++) {
|
||||
root_module.Insert(*it, parser.opts.filename_suffix);
|
||||
}
|
||||
for (auto it = parser.structs_.vec.begin(); it != parser.structs_.vec.end();
|
||||
it++) {
|
||||
root_module.Insert(*it, parser.opts.filename_suffix);
|
||||
}
|
||||
CodeWriter code(" ");
|
||||
// TODO(caspern): Move generated warning out of BaseGenerator.
|
||||
code +=
|
||||
"// Automatically generated by the Flatbuffers compiler. "
|
||||
"Do not modify.";
|
||||
root_module.GenerateImports(code);
|
||||
const bool success =
|
||||
SaveFile((output_dir + "mod.rs").c_str(), code.ToString(), false);
|
||||
code.Clear();
|
||||
return success;
|
||||
}
|
||||
|
||||
namespace rust {
|
||||
|
||||
class RustGenerator : public BaseGenerator {
|
||||
@@ -320,13 +377,79 @@ class RustGenerator : public BaseGenerator {
|
||||
"ENUM_MAX",
|
||||
"ENUM_MIN",
|
||||
"ENUM_VALUES",
|
||||
// clang-format on
|
||||
};
|
||||
for (auto kw = keywords; *kw; kw++) keywords_.insert(*kw);
|
||||
}
|
||||
|
||||
bool generate() {
|
||||
if (parser_.opts.one_file) {
|
||||
return GenerateOneFile();
|
||||
} else {
|
||||
return GenerateIndividualFiles();
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool GenerateSymbols(const SymbolTable<T> &symbols,
|
||||
std::function<void(const T &)> gen_symbol) {
|
||||
for (auto it = symbols.vec.begin(); it != symbols.vec.end(); it++) {
|
||||
const T &symbol = **it;
|
||||
if (symbol.generated) continue;
|
||||
code_.Clear();
|
||||
code_ += "// " + std::string(FlatBuffersGeneratedWarning());
|
||||
code_ += "extern crate flatbuffers;";
|
||||
code_ += "use std::mem;";
|
||||
code_ += "use std::cmp::Ordering;";
|
||||
code_ += "use self::flatbuffers::{EndianScalar, Follow};";
|
||||
code_ += "use super::*;";
|
||||
cur_name_space_ = symbol.defined_namespace;
|
||||
gen_symbol(symbol);
|
||||
std::stringstream file_path;
|
||||
file_path << path_;
|
||||
// DO NOT SUBMIT: CASPER: Refactor out common path name generation.
|
||||
if (symbol.defined_namespace)
|
||||
for (auto i = symbol.defined_namespace->components.begin();
|
||||
i != symbol.defined_namespace->components.end(); i++) {
|
||||
file_path << MakeSnakeCase(*i) << kPathSeparator;
|
||||
EnsureDirExists(file_path.str());
|
||||
}
|
||||
file_path << MakeSnakeCase(symbol.name) << parser_.opts.filename_suffix
|
||||
<< ".rs";
|
||||
const bool save_success =
|
||||
SaveFile(file_path.str().c_str(), code_.ToString(),
|
||||
/*binary=*/false);
|
||||
if (!save_success) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GenerateIndividualFiles() {
|
||||
code_.Clear();
|
||||
// Don't bother with imports. Use absolute paths everywhere.
|
||||
return GenerateSymbols<EnumDef>(
|
||||
parser_.enums_, [&](const EnumDef &e) { this->GenEnum(e); }) &&
|
||||
GenerateSymbols<StructDef>(
|
||||
parser_.structs_, [&](const StructDef &s) {
|
||||
if (s.fixed) {
|
||||
this->GenStruct(s);
|
||||
} else {
|
||||
this->GenTable(s);
|
||||
if (this->parser_.opts.generate_object_based_api) {
|
||||
this->GenTableObject(s);
|
||||
}
|
||||
}
|
||||
if (this->parser_.root_struct_def_ == &s) {
|
||||
this->GenRootTableFuncs(s);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Generates code organized by .fbs files. This is broken legacy behavior
|
||||
// that does not work with multiple fbs files with shared namespaces.
|
||||
// Iterate through all definitions we haven't generated code for (enums,
|
||||
// structs, and tables) and output them to a single file.
|
||||
bool generate() {
|
||||
bool GenerateOneFile() {
|
||||
code_.Clear();
|
||||
code_ += "// " + std::string(FlatBuffersGeneratedWarning()) + "\n\n";
|
||||
|
||||
@@ -496,24 +619,17 @@ class RustGenerator : public BaseGenerator {
|
||||
// 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) + "::";
|
||||
}
|
||||
size_t common = 0;
|
||||
std::vector<std::string> s, d;
|
||||
if (src) s = src->components;
|
||||
if (dst) d = dst->components;
|
||||
while (common < s.size() && common < d.size() && s[common] == d[common])
|
||||
common++;
|
||||
// If src namespace is empty, this must be an absolute path.
|
||||
for (size_t i = common; i < s.size(); i++) stream << "super::";
|
||||
for (size_t i = common; i < d.size(); i++)
|
||||
stream << MakeSnakeCase(d[i]) + "::";
|
||||
return stream.str();
|
||||
}
|
||||
|
||||
@@ -822,6 +938,8 @@ class RustGenerator : public BaseGenerator {
|
||||
code_.SetValue("NATIVE_NAME", NativeName(enum_def));
|
||||
|
||||
// Generate native union.
|
||||
code_ += "#[allow(clippy::upper_case_acronyms)]"; // NONE's spelling is
|
||||
// intended.
|
||||
code_ += "#[non_exhaustive]";
|
||||
code_ += "#[derive(Debug, Clone, PartialEq)]";
|
||||
code_ += "pub enum {{NATIVE_NAME}} {";
|
||||
@@ -1850,7 +1968,7 @@ class RustGenerator : public BaseGenerator {
|
||||
// All types besides unions.
|
||||
code_.SetValue("TY", FollowType(field.value.type, "'_"));
|
||||
code_ +=
|
||||
"\n .visit_field::<{{TY}}>(&\"{{FIELD_NAME}}\", "
|
||||
"\n .visit_field::<{{TY}}>(\"{{FIELD_NAME}}\", "
|
||||
"Self::{{OFFSET_NAME}}, {{IS_REQ}})?\\";
|
||||
return;
|
||||
}
|
||||
@@ -1859,8 +1977,8 @@ class RustGenerator : public BaseGenerator {
|
||||
code_.SetValue("UNION_TYPE", WrapInNameSpace(union_def));
|
||||
code_ +=
|
||||
"\n .visit_union::<{{UNION_TYPE}}, _>("
|
||||
"&\"{{FIELD_NAME}}_type\", Self::{{OFFSET_NAME}}_TYPE, "
|
||||
"&\"{{FIELD_NAME}}\", Self::{{OFFSET_NAME}}, {{IS_REQ}}, "
|
||||
"\"{{FIELD_NAME}}_type\", Self::{{OFFSET_NAME}}_TYPE, "
|
||||
"\"{{FIELD_NAME}}\", Self::{{OFFSET_NAME}}, {{IS_REQ}}, "
|
||||
"|key, v, pos| {";
|
||||
code_ += " match key {";
|
||||
ForAllUnionVariantsBesidesNone(union_def, [&](const EnumVal &unused) {
|
||||
@@ -2187,6 +2305,7 @@ class RustGenerator : public BaseGenerator {
|
||||
FLATBUFFERS_ASSERT(field.key);
|
||||
|
||||
code_.SetValue("KEY_TYPE", GenTableAccessorFuncReturnType(field, ""));
|
||||
code_.SetValue("REF", IsString(field.value.type) ? "" : "&");
|
||||
|
||||
code_ += " #[inline]";
|
||||
code_ +=
|
||||
@@ -2200,7 +2319,7 @@ class RustGenerator : public BaseGenerator {
|
||||
" pub fn key_compare_with_value(&self, val: {{KEY_TYPE}}) -> "
|
||||
" ::std::cmp::Ordering {";
|
||||
code_ += " let key = self.{{FIELD_NAME}}();";
|
||||
code_ += " key.cmp(&val)";
|
||||
code_ += " key.cmp({{REF}}val)";
|
||||
code_ += " }";
|
||||
}
|
||||
|
||||
@@ -2543,7 +2662,7 @@ class RustGenerator : public BaseGenerator {
|
||||
code_ += " let mut s = Self([0; {{STRUCT_SIZE}}]);";
|
||||
ForAllStructFields(struct_def, [&](const FieldDef &unused) {
|
||||
(void)unused;
|
||||
code_ += " s.set_{{FIELD_NAME}}({{REF}}{{FIELD_NAME}});";
|
||||
code_ += " s.set_{{FIELD_NAME}}({{FIELD_NAME}});";
|
||||
});
|
||||
code_ += " s";
|
||||
code_ += " }";
|
||||
|
||||
Reference in New Issue
Block a user