mirror of
https://github.com/google/flatbuffers.git
synced 2026-06-10 23:17:27 +00:00
Default strings and vectors: Parser + Rust support (#6421)
* Fix tests.cpp * Parser support for vector/string defaults * tests and default empty vectors * addressed comments * Default strings and vectors for Rust * Tested Rust more_defaults * git-clang-format * add more_defaults_test * fixed vector default * removed commented out code * more unreachable Co-authored-by: Casper Neo <cneo@google.com>
This commit is contained in:
@@ -183,6 +183,12 @@ bool IsBitFlagsEnum(const FieldDef &field) {
|
||||
return ed && IsBitFlagsEnum(*ed);
|
||||
}
|
||||
|
||||
// TableArgs make required non-scalars "Option<_>".
|
||||
// TODO(cneo): Rework how we do defaults and stuff.
|
||||
bool IsOptionalToBuilder(const FieldDef &field) {
|
||||
return field.IsOptional() || !IsScalar(field.value.type.base_type);
|
||||
}
|
||||
|
||||
namespace rust {
|
||||
|
||||
class RustGenerator : public BaseGenerator {
|
||||
@@ -883,8 +889,10 @@ class RustGenerator : public BaseGenerator {
|
||||
return "VT_" + MakeUpper(Name(field));
|
||||
}
|
||||
|
||||
std::string GetDefaultValue(const FieldDef &field, bool for_builder) {
|
||||
if (for_builder) {
|
||||
enum DefaultContext { kBuilder, kAccessor, kObject };
|
||||
std::string GetDefaultValue(const FieldDef &field,
|
||||
const DefaultContext context) {
|
||||
if (context == kBuilder) {
|
||||
// Builders and Args structs model nonscalars "optional" even if they're
|
||||
// required or have defaults according to the schema. I guess its because
|
||||
// WIPOffset is not nullable.
|
||||
@@ -915,9 +923,19 @@ class RustGenerator : public BaseGenerator {
|
||||
return ObjectFieldType(field, true) + "::NONE";
|
||||
}
|
||||
case ftString: {
|
||||
// Required strings.
|
||||
return "String::new()"; // No default strings yet.
|
||||
// Required fields do not have defaults defined by the schema, but we
|
||||
// need one for Rust's Default trait so we use empty string. The usual
|
||||
// value of field.value.constant is `0`, which is non-sensical except
|
||||
// maybe to c++ (nullptr == 0).
|
||||
// TODO: Escape strings?
|
||||
const std::string defval =
|
||||
field.IsRequired() ? "\"\"" : "\"" + field.value.constant + "\"";
|
||||
if (context == kObject) return defval + ".to_string()";
|
||||
if (context == kAccessor) return "&" + defval;
|
||||
FLATBUFFERS_ASSERT("Unreachable.");
|
||||
return "INVALID_CODE_GENERATION";
|
||||
}
|
||||
|
||||
case ftVectorOfBool:
|
||||
case ftVectorOfFloat:
|
||||
case ftVectorOfInteger:
|
||||
@@ -925,14 +943,16 @@ class RustGenerator : public BaseGenerator {
|
||||
case ftVectorOfStruct:
|
||||
case ftVectorOfTable:
|
||||
case ftVectorOfEnumKey:
|
||||
case ftVectorOfUnionValue: {
|
||||
// Required vectors.
|
||||
return "Vec::new()"; // No default strings yet.
|
||||
}
|
||||
case ftVectorOfUnionValue:
|
||||
case ftStruct:
|
||||
case ftTable: {
|
||||
// Required struct/tables.
|
||||
return "Default::default()"; // punt.
|
||||
// We only support empty vectors which matches the defaults for
|
||||
// &[T] and Vec<T> anyway.
|
||||
//
|
||||
// For required structs and tables fields, we defer to their object API
|
||||
// defaults. This works so long as there's nothing recursive happening,
|
||||
// but `table Infinity { i: Infinity (required); }` does compile.
|
||||
return "Default::default()";
|
||||
}
|
||||
}
|
||||
FLATBUFFERS_ASSERT("Unreachable.");
|
||||
@@ -953,30 +973,38 @@ class RustGenerator : public BaseGenerator {
|
||||
std::string TableBuilderArgsDefnType(const FieldDef &field,
|
||||
const std::string &lifetime) {
|
||||
const Type &type = field.value.type;
|
||||
auto WrapOption = [&](std::string s) {
|
||||
return IsOptionalToBuilder(field) ? "Option<" + s + ">" : s;
|
||||
};
|
||||
auto WrapVector = [&](std::string ty) {
|
||||
return WrapOption("flatbuffers::WIPOffset<flatbuffers::Vector<" +
|
||||
lifetime + ", " + ty + ">>");
|
||||
};
|
||||
auto WrapUOffsetsVector = [&](std::string ty) {
|
||||
return WrapVector("flatbuffers::ForwardsUOffset<" + ty + ">");
|
||||
};
|
||||
|
||||
switch (GetFullType(type)) {
|
||||
case ftInteger:
|
||||
case ftFloat:
|
||||
case ftBool: {
|
||||
const auto typname = GetTypeBasic(type);
|
||||
return field.IsOptional() ? "Option<" + typname + ">" : typname;
|
||||
return WrapOption(GetTypeBasic(type));
|
||||
}
|
||||
case ftStruct: {
|
||||
const auto typname = WrapInNameSpace(*type.struct_def);
|
||||
return "Option<&" + lifetime + " " + typname + ">";
|
||||
return WrapOption("&" + lifetime + " " + typname);
|
||||
}
|
||||
case ftTable: {
|
||||
const auto typname = WrapInNameSpace(*type.struct_def);
|
||||
return "Option<flatbuffers::WIPOffset<" + typname + "<" + lifetime +
|
||||
">>>";
|
||||
return WrapOption("flatbuffers::WIPOffset<" + typname + "<" + lifetime +
|
||||
">>");
|
||||
}
|
||||
case ftString: {
|
||||
return "Option<flatbuffers::WIPOffset<&" + lifetime + " str>>";
|
||||
return WrapOption("flatbuffers::WIPOffset<&" + lifetime + " str>");
|
||||
}
|
||||
case ftEnumKey:
|
||||
case ftUnionKey: {
|
||||
const auto typname = WrapInNameSpace(*type.enum_def);
|
||||
return field.IsOptional() ? "Option<" + typname + ">" : typname;
|
||||
return WrapOption(WrapInNameSpace(*type.enum_def));
|
||||
}
|
||||
case ftUnionValue: {
|
||||
return "Option<flatbuffers::WIPOffset<flatbuffers::UnionWIPOffset>>";
|
||||
@@ -986,36 +1014,25 @@ class RustGenerator : public BaseGenerator {
|
||||
case ftVectorOfBool:
|
||||
case ftVectorOfFloat: {
|
||||
const auto typname = GetTypeBasic(type.VectorType());
|
||||
return "Option<flatbuffers::WIPOffset<flatbuffers::Vector<" + lifetime +
|
||||
", " + typname + ">>>";
|
||||
return WrapVector(typname);
|
||||
}
|
||||
case ftVectorOfEnumKey: {
|
||||
const auto typname = WrapInNameSpace(*type.enum_def);
|
||||
return "Option<flatbuffers::WIPOffset<flatbuffers::Vector<" + lifetime +
|
||||
", " + typname + ">>>";
|
||||
return WrapVector(typname);
|
||||
}
|
||||
case ftVectorOfStruct: {
|
||||
const auto typname = WrapInNameSpace(*type.struct_def);
|
||||
return "Option<flatbuffers::WIPOffset<flatbuffers::Vector<" + lifetime +
|
||||
", " + typname + ">>>";
|
||||
return WrapVector(typname);
|
||||
}
|
||||
case ftVectorOfTable: {
|
||||
const auto typname = WrapInNameSpace(*type.struct_def);
|
||||
return "Option<flatbuffers::WIPOffset<flatbuffers::Vector<" + lifetime +
|
||||
", flatbuffers::ForwardsUOffset<" + typname + "<" + lifetime +
|
||||
">>>>>";
|
||||
return WrapUOffsetsVector(typname + "<" + lifetime + ">");
|
||||
}
|
||||
case ftVectorOfString: {
|
||||
return "Option<flatbuffers::WIPOffset<flatbuffers::Vector<" + lifetime +
|
||||
", flatbuffers::ForwardsUOffset<&" + lifetime + " str>>>>";
|
||||
return WrapUOffsetsVector("&" + 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 WrapUOffsetsVector("flatbuffers::Table<" + lifetime + ">");
|
||||
}
|
||||
}
|
||||
return "INVALID_CODE_GENERATION"; // for return analysis
|
||||
@@ -1084,8 +1101,7 @@ class RustGenerator : public BaseGenerator {
|
||||
return "INVALID_CODE_GENERATION"; // OH NO!
|
||||
}
|
||||
}
|
||||
if (in_a_table && !IsUnion(type) &&
|
||||
(IsScalar(type.base_type) ? field.IsOptional() : !field.IsRequired())) {
|
||||
if (in_a_table && !IsUnion(type) && field.IsOptional()) {
|
||||
return "Option<" + ty + ">";
|
||||
} else {
|
||||
return ty;
|
||||
@@ -1207,73 +1223,64 @@ class RustGenerator : public BaseGenerator {
|
||||
std::string GenTableAccessorFuncReturnType(const FieldDef &field,
|
||||
const std::string &lifetime) {
|
||||
const Type &type = field.value.type;
|
||||
const auto WrapOption = [&](std::string s) {
|
||||
return field.IsOptional() ? "Option<" + s + ">" : s;
|
||||
};
|
||||
|
||||
switch (GetFullType(field.value.type)) {
|
||||
case ftInteger:
|
||||
case ftFloat:
|
||||
case ftBool: {
|
||||
const auto typname = GetTypeBasic(type);
|
||||
return field.IsOptional() ? "Option<" + typname + ">" : typname;
|
||||
return WrapOption(GetTypeBasic(type));
|
||||
}
|
||||
case ftStruct: {
|
||||
const auto typname = WrapInNameSpace(*type.struct_def);
|
||||
return WrapInOptionIfNotRequired("&" + lifetime + " " + typname,
|
||||
field.IsRequired());
|
||||
return WrapOption("&" + lifetime + " " + typname);
|
||||
}
|
||||
case ftTable: {
|
||||
const auto typname = WrapInNameSpace(*type.struct_def);
|
||||
return WrapInOptionIfNotRequired(typname + "<" + lifetime + ">",
|
||||
field.IsRequired());
|
||||
return WrapOption(typname + "<" + lifetime + ">");
|
||||
}
|
||||
case ftEnumKey:
|
||||
case ftUnionKey: {
|
||||
const auto typname = WrapInNameSpace(*type.enum_def);
|
||||
return field.IsOptional() ? "Option<" + typname + ">" : typname;
|
||||
return WrapOption(WrapInNameSpace(*type.enum_def));
|
||||
}
|
||||
|
||||
case ftUnionValue: {
|
||||
return WrapInOptionIfNotRequired("flatbuffers::Table<" + lifetime + ">",
|
||||
field.IsRequired());
|
||||
return WrapOption("flatbuffers::Table<" + lifetime + ">");
|
||||
}
|
||||
case ftString: {
|
||||
return WrapInOptionIfNotRequired("&" + lifetime + " str",
|
||||
field.IsRequired());
|
||||
return WrapOption("&" + lifetime + " str");
|
||||
}
|
||||
case ftVectorOfInteger:
|
||||
case ftVectorOfBool:
|
||||
case ftVectorOfFloat: {
|
||||
const auto typname = GetTypeBasic(type.VectorType());
|
||||
if (IsOneByte(type.VectorType().base_type)) {
|
||||
return WrapInOptionIfNotRequired(
|
||||
"&" + lifetime + " [" + typname + "]", field.IsRequired());
|
||||
}
|
||||
return WrapInOptionIfNotRequired(
|
||||
"flatbuffers::Vector<" + lifetime + ", " + typname + ">",
|
||||
field.IsRequired());
|
||||
const auto vector_type =
|
||||
IsOneByte(type.VectorType().base_type)
|
||||
? "&" + lifetime + " [" + typname + "]"
|
||||
: "flatbuffers::Vector<" + lifetime + ", " + typname + ">";
|
||||
return WrapOption(vector_type);
|
||||
}
|
||||
case ftVectorOfEnumKey: {
|
||||
const auto typname = WrapInNameSpace(*type.enum_def);
|
||||
return WrapInOptionIfNotRequired(
|
||||
"flatbuffers::Vector<" + lifetime + ", " + typname + ">",
|
||||
field.IsRequired());
|
||||
return WrapOption("flatbuffers::Vector<" + lifetime + ", " + typname +
|
||||
">");
|
||||
}
|
||||
case ftVectorOfStruct: {
|
||||
const auto typname = WrapInNameSpace(*type.struct_def);
|
||||
return WrapInOptionIfNotRequired("&" + lifetime + " [" + typname + "]",
|
||||
field.IsRequired());
|
||||
return WrapOption("&" + lifetime + " [" + typname + "]");
|
||||
}
|
||||
case ftVectorOfTable: {
|
||||
const auto typname = WrapInNameSpace(*type.struct_def);
|
||||
return WrapInOptionIfNotRequired("flatbuffers::Vector<" + lifetime +
|
||||
", flatbuffers::ForwardsUOffset<" +
|
||||
typname + "<" + lifetime + ">>>",
|
||||
field.IsRequired());
|
||||
return WrapOption("flatbuffers::Vector<" + lifetime +
|
||||
", flatbuffers::ForwardsUOffset<" + typname + "<" +
|
||||
lifetime + ">>>");
|
||||
}
|
||||
case ftVectorOfString: {
|
||||
return WrapInOptionIfNotRequired(
|
||||
"flatbuffers::Vector<" + lifetime +
|
||||
", flatbuffers::ForwardsUOffset<&" + lifetime + " str>>",
|
||||
field.IsRequired());
|
||||
return WrapOption("flatbuffers::Vector<" + lifetime +
|
||||
", flatbuffers::ForwardsUOffset<&" + lifetime +
|
||||
" str>>");
|
||||
}
|
||||
case ftVectorOfUnionValue: {
|
||||
FLATBUFFERS_ASSERT(false && "vectors of unions are not yet supported");
|
||||
@@ -1354,7 +1361,7 @@ class RustGenerator : public BaseGenerator {
|
||||
// Default-y fields (scalars so far) are neither optional nor required.
|
||||
const std::string default_value =
|
||||
!(field.IsOptional() || field.IsRequired())
|
||||
? "Some(" + GetDefaultValue(field, /*builder=*/true) + ")"
|
||||
? "Some(" + GetDefaultValue(field, kAccessor) + ")"
|
||||
: "None";
|
||||
const std::string unwrap = field.IsOptional() ? "" : ".unwrap()";
|
||||
|
||||
@@ -1373,18 +1380,6 @@ class RustGenerator : public BaseGenerator {
|
||||
", " + default_value + ")" + safe_slice + unwrap;
|
||||
}
|
||||
|
||||
bool TableFieldReturnsOption(const FieldDef &field) {
|
||||
if (field.IsOptional()) 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) {
|
||||
@@ -1425,7 +1420,7 @@ class RustGenerator : public BaseGenerator {
|
||||
code_.SetValue("OFFSET_NAME", GetFieldOffsetName(field));
|
||||
code_.SetValue("OFFSET_VALUE", NumToString(field.value.offset));
|
||||
code_.SetValue("FIELD_NAME", Name(field));
|
||||
code_.SetValue("BLDR_DEF_VAL", GetDefaultValue(field, /*builder=*/true));
|
||||
code_.SetValue("BLDR_DEF_VAL", GetDefaultValue(field, kBuilder));
|
||||
cb(field);
|
||||
};
|
||||
const auto &fields = struct_def.fields.vec;
|
||||
@@ -1498,7 +1493,7 @@ class RustGenerator : public BaseGenerator {
|
||||
if (struct_def.sortbysize &&
|
||||
size != SizeOf(field.value.type.base_type))
|
||||
return;
|
||||
if (TableFieldReturnsOption(field)) {
|
||||
if (IsOptionalToBuilder(field)) {
|
||||
code_ +=
|
||||
" if let Some(x) = args.{{FIELD_NAME}} "
|
||||
"{ builder.add_{{FIELD_NAME}}(x); }";
|
||||
@@ -1943,7 +1938,7 @@ class RustGenerator : public BaseGenerator {
|
||||
code_ += " Self {";
|
||||
ForAllObjectTableFields(table, [&](const FieldDef &field) {
|
||||
if (field.value.type.base_type == BASE_TYPE_UTYPE) return;
|
||||
std::string default_value = GetDefaultValue(field, /*builder=*/false);
|
||||
std::string default_value = GetDefaultValue(field, kObject);
|
||||
code_ += " {{FIELD_NAME}}: " + default_value + ",";
|
||||
});
|
||||
code_ += " }";
|
||||
@@ -2064,17 +2059,17 @@ class RustGenerator : public BaseGenerator {
|
||||
}
|
||||
}
|
||||
void MapNativeTableField(const FieldDef &field, const std::string &expr) {
|
||||
if (field.IsRequired()) {
|
||||
if (field.IsOptional()) {
|
||||
code_ += " let {{FIELD_NAME}} = self.{{FIELD_NAME}}.as_ref().map(|x|{";
|
||||
code_ += " " + expr;
|
||||
code_ += " });";
|
||||
} else {
|
||||
// For some reason Args has optional types for required fields.
|
||||
// TODO(cneo): Fix this... but its a breaking change?
|
||||
code_ += " let {{FIELD_NAME}} = Some({";
|
||||
code_ += " let x = &self.{{FIELD_NAME}};";
|
||||
code_ += " " + expr;
|
||||
code_ += " });";
|
||||
} else {
|
||||
code_ += " let {{FIELD_NAME}} = self.{{FIELD_NAME}}.as_ref().map(|x|{";
|
||||
code_ += " " + expr;
|
||||
code_ += " });";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user