Implement Rust object API defaults (#6444)
authorCasper <casperneo@uchicago.edu>
Sun, 7 Feb 2021 21:51:33 +0000 (16:51 -0500)
committerGitHub <noreply@github.com>
Sun, 7 Feb 2021 21:51:33 +0000 (16:51 -0500)
* Implment Rust object API defaults

* satisfy return analysis

* git clang format

Co-authored-by: Casper Neo <cneo@google.com>
samples/monster_generated.rs
src/idl_gen_rust.cpp
tests/monster_test_generated.rs
tests/namespace_test/namespace_test1_generated.rs
tests/namespace_test/namespace_test2_generated.rs
tests/optional_scalars_generated.rs
tests/rust_usage_test/tests/integration_test.rs
tests/rust_usage_test/tests/optional_scalars_test.rs

index cc2051e..aae5c6c 100644 (file)
@@ -698,7 +698,7 @@ impl std::fmt::Debug for Monster<'_> {
   }
 }
 #[non_exhaustive]
-#[derive(Debug, Clone, PartialEq, Default)]
+#[derive(Debug, Clone, PartialEq)]
 pub struct MonsterT {
   pub pos: Option<Vec3T>,
   pub mana: i16,
@@ -710,6 +710,21 @@ pub struct MonsterT {
   pub equipped: EquipmentT,
   pub path: Option<Vec<Vec3T>>,
 }
+impl Default for MonsterT {
+  fn default() -> Self {
+    Self {
+      pos: None,
+      mana: 150,
+      hp: 100,
+      name: None,
+      inventory: None,
+      color: Color::Blue,
+      weapons: None,
+      equipped: EquipmentT::NONE,
+      path: None,
+    }
+  }
+}
 impl MonsterT {
   pub fn pack<'b>(
     &self,
@@ -864,11 +879,19 @@ impl std::fmt::Debug for Weapon<'_> {
   }
 }
 #[non_exhaustive]
-#[derive(Debug, Clone, PartialEq, Default)]
+#[derive(Debug, Clone, PartialEq)]
 pub struct WeaponT {
   pub name: Option<String>,
   pub damage: i16,
 }
+impl Default for WeaponT {
+  fn default() -> Self {
+    Self {
+      name: None,
+      damage: 0,
+    }
+  }
+}
 impl WeaponT {
   pub fn pack<'b>(
     &self,
index 424112c..5236649 100644 (file)
@@ -883,31 +883,60 @@ class RustGenerator : public BaseGenerator {
     return "VT_" + MakeUpper(Name(field));
   }
 
-  std::string GetDefaultScalarValue(const FieldDef &field) {
+  std::string GetDefaultValue(const FieldDef &field, bool for_builder) {
+    if (for_builder) {
+      // 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.
+      if (!IsScalar(field.value.type.base_type) || field.IsOptional()) {
+        return "None";
+      }
+    } else {
+      // This for defaults in objects.
+      // Unions have a NONE variant instead of using Rust's None.
+      if (field.IsOptional() && !IsUnion(field.value.type)) { return "None"; }
+    }
     switch (GetFullType(field.value.type)) {
       case ftInteger:
       case ftFloat: {
-        return field.IsOptional() ? "None" : field.value.constant;
+        return field.value.constant;
       }
       case ftBool: {
-        return field.IsOptional() ? "None"
-                              : field.value.constant == "0" ? "false" : "true";
+        return field.value.constant == "0" ? "false" : "true";
       }
       case ftUnionKey:
       case ftEnumKey: {
-        if (field.IsOptional()) { return "None"; }
         auto ev = field.value.type.enum_def->FindByValue(field.value.constant);
         if (!ev) return "Default::default()";  // Bitflags enum.
         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";
+      case ftUnionValue: {
+        return ObjectFieldType(field, true) + "::NONE";
+      }
+      case ftString: {
+        // Required strings.
+        return "String::new()";  // No default strings yet.
+      }
+      case ftVectorOfBool:
+      case ftVectorOfFloat:
+      case ftVectorOfInteger:
+      case ftVectorOfString:
+      case ftVectorOfStruct:
+      case ftVectorOfTable:
+      case ftVectorOfEnumKey:
+      case ftVectorOfUnionValue: {
+        // Required vectors.
+        return "Vec::new()";  // No default strings yet.
+      }
+      case ftStruct:
+      case ftTable: {
+        // Required struct/tables.
+        return "Default::default()";  // punt.
       }
     }
+    FLATBUFFERS_ASSERT("Unreachable.");
+    return "INVALID_CODE_GENERATION";
   }
 
   // Create the return type for fields in the *BuilderArgs structs that are
@@ -1138,14 +1167,14 @@ class RustGenerator : public BaseGenerator {
       case ftFloat: {
         const auto typname = GetTypeBasic(field.value.type);
         return (field.IsOptional() ? "self.fbb_.push_slot_always::<"
-                               : "self.fbb_.push_slot::<") +
+                                   : "self.fbb_.push_slot::<") +
                typname + ">";
       }
       case ftEnumKey:
       case ftUnionKey: {
         const auto underlying_typname = GetTypeBasic(type);
         return (field.IsOptional() ? "self.fbb_.push_slot_always::<"
-                               : "self.fbb_.push_slot::<") +
+                                   : "self.fbb_.push_slot::<") +
                underlying_typname + ">";
       }
 
@@ -1325,7 +1354,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(" + GetDefaultScalarValue(field) + ")"
+            ? "Some(" + GetDefaultValue(field, /*builder=*/true) + ")"
             : "None";
     const std::string unwrap = field.IsOptional() ? "" : ".unwrap()";
 
@@ -1396,7 +1425,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("DEFAULT_VALUE", GetDefaultScalarValue(field));
+      code_.SetValue("BLDR_DEF_VAL", GetDefaultValue(field, /*builder=*/true));
       cb(field);
     };
     const auto &fields = struct_def.fields.vec;
@@ -1769,7 +1798,7 @@ class RustGenerator : public BaseGenerator {
     code_ += "    fn default() -> Self {";
     code_ += "        {{STRUCT_NAME}}Args {";
     ForAllTableFields(struct_def, [&](const FieldDef &field) {
-      code_ += "            {{FIELD_NAME}}: {{DEFAULT_VALUE}},\\";
+      code_ += "            {{FIELD_NAME}}: {{BLDR_DEF_VAL}},\\";
       code_ += field.IsRequired() ? " // required field" : "";
     });
     code_ += "        }";
@@ -1810,7 +1839,7 @@ class RustGenerator : public BaseGenerator {
       if (is_scalar && !field.IsOptional()) {
         code_ +=
             "    {{FUNC_BODY}}({{FIELD_OFFSET}}, {{FIELD_NAME}}, "
-            "{{DEFAULT_VALUE}});";
+            "{{BLDR_DEF_VAL}});";
       } else {
         code_ += "    {{FUNC_BODY}}({{FIELD_OFFSET}}, {{FIELD_NAME}});";
       }
@@ -1899,7 +1928,7 @@ class RustGenerator : public BaseGenerator {
 
     // Generate the native object.
     code_ += "#[non_exhaustive]";
-    code_ += "#[derive(Debug, Clone, PartialEq, Default)]";
+    code_ += "#[derive(Debug, Clone, PartialEq)]";
     code_ += "pub struct {{OBJECT_NAME}} {";
     ForAllObjectTableFields(table, [&](const FieldDef &field) {
       // Union objects combine both the union discriminant and value, so we
@@ -1909,6 +1938,18 @@ class RustGenerator : public BaseGenerator {
     });
     code_ += "}";
 
+    code_ += "impl Default for {{OBJECT_NAME}} {";
+    code_ += "  fn default() -> Self {";
+    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);
+      code_ += "      {{FIELD_NAME}}: " + default_value + ",";
+    });
+    code_ += "    }";
+    code_ += "  }";
+    code_ += "}";
+
     // TODO(cneo): Generate defaults for Native tables. However, since structs
     // may be required, they, and therefore enums need defaults.
 
index a514c7a..2e4b840 100644 (file)
@@ -106,9 +106,15 @@ impl std::fmt::Debug for InParentNamespace<'_> {
   }
 }
 #[non_exhaustive]
-#[derive(Debug, Clone, PartialEq, Default)]
+#[derive(Debug, Clone, PartialEq)]
 pub struct InParentNamespaceT {
 }
+impl Default for InParentNamespaceT {
+  fn default() -> Self {
+    Self {
+    }
+  }
+}
 impl InParentNamespaceT {
   pub fn pack<'b>(
     &self,
@@ -214,9 +220,15 @@ impl std::fmt::Debug for Monster<'_> {
   }
 }
 #[non_exhaustive]
-#[derive(Debug, Clone, PartialEq, Default)]
+#[derive(Debug, Clone, PartialEq)]
 pub struct MonsterT {
 }
+impl Default for MonsterT {
+  fn default() -> Self {
+    Self {
+    }
+  }
+}
 impl MonsterT {
   pub fn pack<'b>(
     &self,
@@ -1598,10 +1610,17 @@ impl std::fmt::Debug for TestSimpleTableWithEnum<'_> {
   }
 }
 #[non_exhaustive]
-#[derive(Debug, Clone, PartialEq, Default)]
+#[derive(Debug, Clone, PartialEq)]
 pub struct TestSimpleTableWithEnumT {
   pub color: Color,
 }
+impl Default for TestSimpleTableWithEnumT {
+  fn default() -> Self {
+    Self {
+      color: Color::Green,
+    }
+  }
+}
 impl TestSimpleTableWithEnumT {
   pub fn pack<'b>(
     &self,
@@ -1759,12 +1778,21 @@ impl std::fmt::Debug for Stat<'_> {
   }
 }
 #[non_exhaustive]
-#[derive(Debug, Clone, PartialEq, Default)]
+#[derive(Debug, Clone, PartialEq)]
 pub struct StatT {
   pub id: Option<String>,
   pub val: i64,
   pub count: u16,
 }
+impl Default for StatT {
+  fn default() -> Self {
+    Self {
+      id: None,
+      val: 0,
+      count: 0,
+    }
+  }
+}
 impl StatT {
   pub fn pack<'b>(
     &self,
@@ -1894,10 +1922,17 @@ impl std::fmt::Debug for Referrable<'_> {
   }
 }
 #[non_exhaustive]
-#[derive(Debug, Clone, PartialEq, Default)]
+#[derive(Debug, Clone, PartialEq)]
 pub struct ReferrableT {
   pub id: u64,
 }
+impl Default for ReferrableT {
+  fn default() -> Self {
+    Self {
+      id: 0,
+    }
+  }
+}
 impl ReferrableT {
   pub fn pack<'b>(
     &self,
@@ -3106,7 +3141,7 @@ impl std::fmt::Debug for Monster<'_> {
   }
 }
 #[non_exhaustive]
-#[derive(Debug, Clone, PartialEq, Default)]
+#[derive(Debug, Clone, PartialEq)]
 pub struct MonsterT {
   pub pos: Option<Vec3T>,
   pub mana: i16,
@@ -3156,6 +3191,59 @@ pub struct MonsterT {
   pub testrequirednestedflatbuffer: Option<Vec<u8>>,
   pub scalar_key_sorted_tables: Option<Vec<StatT>>,
 }
+impl Default for MonsterT {
+  fn default() -> Self {
+    Self {
+      pos: None,
+      mana: 150,
+      hp: 100,
+      name: String::new(),
+      inventory: None,
+      color: Color::Blue,
+      test: AnyT::NONE,
+      test4: None,
+      testarrayofstring: None,
+      testarrayoftables: None,
+      enemy: None,
+      testnestedflatbuffer: None,
+      testempty: None,
+      testbool: false,
+      testhashs32_fnv1: 0,
+      testhashu32_fnv1: 0,
+      testhashs64_fnv1: 0,
+      testhashu64_fnv1: 0,
+      testhashs32_fnv1a: 0,
+      testhashu32_fnv1a: 0,
+      testhashs64_fnv1a: 0,
+      testhashu64_fnv1a: 0,
+      testarrayofbools: None,
+      testf: 3.14159,
+      testf2: 3.0,
+      testf3: 0.0,
+      testarrayofstring2: None,
+      testarrayofsortedstruct: None,
+      flex: None,
+      test5: None,
+      vector_of_longs: None,
+      vector_of_doubles: None,
+      parent_namespace_test: None,
+      vector_of_referrables: None,
+      single_weak_reference: 0,
+      vector_of_weak_references: None,
+      vector_of_strong_referrables: None,
+      co_owning_reference: 0,
+      vector_of_co_owning_references: None,
+      non_owning_reference: 0,
+      vector_of_non_owning_references: None,
+      any_unique: AnyUniqueAliasesT::NONE,
+      any_ambiguous: AnyAmbiguousAliasesT::NONE,
+      vector_of_enums: None,
+      signed_enum: Race::None,
+      testrequirednestedflatbuffer: None,
+      scalar_key_sorted_tables: None,
+    }
+  }
+}
 impl MonsterT {
   pub fn pack<'b>(
     &self,
@@ -3597,7 +3685,7 @@ impl std::fmt::Debug for TypeAliases<'_> {
   }
 }
 #[non_exhaustive]
-#[derive(Debug, Clone, PartialEq, Default)]
+#[derive(Debug, Clone, PartialEq)]
 pub struct TypeAliasesT {
   pub i8_: i8,
   pub u8_: u8,
@@ -3612,6 +3700,24 @@ pub struct TypeAliasesT {
   pub v8: Option<Vec<i8>>,
   pub vf64: Option<Vec<f64>>,
 }
+impl Default for TypeAliasesT {
+  fn default() -> Self {
+    Self {
+      i8_: 0,
+      u8_: 0,
+      i16_: 0,
+      u16_: 0,
+      i32_: 0,
+      u32_: 0,
+      i64_: 0,
+      u64_: 0,
+      f32_: 0.0,
+      f64_: 0.0,
+      v8: None,
+      vf64: None,
+    }
+  }
+}
 impl TypeAliasesT {
   pub fn pack<'b>(
     &self,
index aa4dde6..183d904 100644 (file)
@@ -491,10 +491,17 @@ impl std::fmt::Debug for TableInNestedNS<'_> {
   }
 }
 #[non_exhaustive]
-#[derive(Debug, Clone, PartialEq, Default)]
+#[derive(Debug, Clone, PartialEq)]
 pub struct TableInNestedNST {
   pub foo: i32,
 }
+impl Default for TableInNestedNST {
+  fn default() -> Self {
+    Self {
+      foo: 0,
+    }
+  }
+}
 impl TableInNestedNST {
   pub fn pack<'b>(
     &self,
index e0ecd72..815d9f4 100644 (file)
@@ -491,10 +491,17 @@ impl std::fmt::Debug for TableInNestedNS<'_> {
   }
 }
 #[non_exhaustive]
-#[derive(Debug, Clone, PartialEq, Default)]
+#[derive(Debug, Clone, PartialEq)]
 pub struct TableInNestedNST {
   pub foo: i32,
 }
+impl Default for TableInNestedNST {
+  fn default() -> Self {
+    Self {
+      foo: 0,
+    }
+  }
+}
 impl TableInNestedNST {
   pub fn pack<'b>(
     &self,
@@ -710,13 +717,23 @@ impl std::fmt::Debug for TableInFirstNS<'_> {
   }
 }
 #[non_exhaustive]
-#[derive(Debug, Clone, PartialEq, Default)]
+#[derive(Debug, Clone, PartialEq)]
 pub struct TableInFirstNST {
   pub foo_table: Option<Box<namespace_b::TableInNestedNST>>,
   pub foo_enum: namespace_b::EnumInNestedNS,
   pub foo_union: namespace_b::UnionInNestedNST,
   pub foo_struct: Option<namespace_b::StructInNestedNST>,
 }
+impl Default for TableInFirstNST {
+  fn default() -> Self {
+    Self {
+      foo_table: None,
+      foo_enum: namespace_b::EnumInNestedNS::A,
+      foo_union: namespace_b::UnionInNestedNST::NONE,
+      foo_struct: None,
+    }
+  }
+}
 impl TableInFirstNST {
   pub fn pack<'b>(
     &self,
@@ -843,10 +860,17 @@ impl std::fmt::Debug for SecondTableInA<'_> {
   }
 }
 #[non_exhaustive]
-#[derive(Debug, Clone, PartialEq, Default)]
+#[derive(Debug, Clone, PartialEq)]
 pub struct SecondTableInAT {
   pub refer_to_c: Option<Box<super::namespace_c::TableInCT>>,
 }
+impl Default for SecondTableInAT {
+  fn default() -> Self {
+    Self {
+      refer_to_c: None,
+    }
+  }
+}
 impl SecondTableInAT {
   pub fn pack<'b>(
     &self,
@@ -993,11 +1017,19 @@ impl std::fmt::Debug for TableInC<'_> {
   }
 }
 #[non_exhaustive]
-#[derive(Debug, Clone, PartialEq, Default)]
+#[derive(Debug, Clone, PartialEq)]
 pub struct TableInCT {
   pub refer_to_a1: Option<Box<super::namespace_a::TableInFirstNST>>,
   pub refer_to_a2: Option<Box<super::namespace_a::SecondTableInAT>>,
 }
+impl Default for TableInCT {
+  fn default() -> Self {
+    Self {
+      refer_to_a1: None,
+      refer_to_a2: None,
+    }
+  }
+}
 impl TableInCT {
   pub fn pack<'b>(
     &self,
index 81483b5..f27ecf4 100644 (file)
@@ -763,7 +763,7 @@ impl std::fmt::Debug for ScalarStuff<'_> {
   }
 }
 #[non_exhaustive]
-#[derive(Debug, Clone, PartialEq, Default)]
+#[derive(Debug, Clone, PartialEq)]
 pub struct ScalarStuffT {
   pub just_i8: i8,
   pub maybe_i8: Option<i8>,
@@ -802,6 +802,48 @@ pub struct ScalarStuffT {
   pub maybe_enum: Option<OptionalByte>,
   pub default_enum: OptionalByte,
 }
+impl Default for ScalarStuffT {
+  fn default() -> Self {
+    Self {
+      just_i8: 0,
+      maybe_i8: None,
+      default_i8: 42,
+      just_u8: 0,
+      maybe_u8: None,
+      default_u8: 42,
+      just_i16: 0,
+      maybe_i16: None,
+      default_i16: 42,
+      just_u16: 0,
+      maybe_u16: None,
+      default_u16: 42,
+      just_i32: 0,
+      maybe_i32: None,
+      default_i32: 42,
+      just_u32: 0,
+      maybe_u32: None,
+      default_u32: 42,
+      just_i64: 0,
+      maybe_i64: None,
+      default_i64: 42,
+      just_u64: 0,
+      maybe_u64: None,
+      default_u64: 42,
+      just_f32: 0.0,
+      maybe_f32: None,
+      default_f32: 42.0,
+      just_f64: 0.0,
+      maybe_f64: None,
+      default_f64: 42.0,
+      just_bool: false,
+      maybe_bool: None,
+      default_bool: true,
+      just_enum: OptionalByte::None,
+      maybe_enum: None,
+      default_enum: OptionalByte::One,
+    }
+  }
+}
 impl ScalarStuffT {
   pub fn pack<'b>(
     &self,
index fa68647..9bbbe88 100644 (file)
@@ -115,6 +115,74 @@ fn macro_check_is_some() {
     assert!(check_is_some!(none).is_err());
 }
 
+#[test]
+fn object_api_defaults() {
+    use my_game::example::*;
+    assert_eq!(
+        Vec3T::default(), Vec3T {
+        x: 0.0,
+        y: 0.0,
+        z: 0.0,
+        test1: 0.0,
+        test2: Color::empty(),
+        test3: TestT {
+            a: 0,
+            b: 0
+        }
+    });
+    assert_eq!(
+        MonsterT::default(),
+        MonsterT {
+            pos: None,
+            hp: 100,
+            mana: 150,
+            name: String::new(),  // required string => default is empty string.
+            color: Color::Blue,
+            inventory: None,
+            testarrayoftables: None,
+            testarrayofstring: None,
+            testarrayofstring2: None,
+            testarrayofbools: None,
+            testarrayofsortedstruct: None,
+            enemy: None,
+            test: AnyT::NONE,
+            test4: None,
+            test5: None,
+            testnestedflatbuffer: None,
+            testempty: None,
+            testbool: false,
+            testhashs32_fnv1: 0,
+            testhashu32_fnv1: 0,
+            testhashs64_fnv1: 0,
+            testhashu64_fnv1: 0,
+            testhashs32_fnv1a: 0,
+            testhashu32_fnv1a: 0,
+            testhashs64_fnv1a: 0,
+            testhashu64_fnv1a: 0,
+            testf: 3.14159,
+            testf2: 3.0,
+            testf3: 0.0,
+            flex: None,
+            vector_of_longs: None,
+            vector_of_doubles: None,
+            parent_namespace_test: None,
+            vector_of_referrables: None,
+            single_weak_reference: 0,
+            vector_of_weak_references: None,
+            vector_of_strong_referrables: None,
+            co_owning_reference: 0,
+            vector_of_co_owning_references: None,
+            non_owning_reference: 0,
+            vector_of_non_owning_references: None,
+            any_unique: AnyUniqueAliasesT::NONE,
+            any_ambiguous: AnyAmbiguousAliasesT::NONE,
+            vector_of_enums: None,
+            signed_enum: Race::None,
+            testrequirednestedflatbuffer: None,  // despite the name, it is not required.
+            scalar_key_sorted_tables: None,
+        }
+    );
+}
 
 fn create_serialized_example_with_generated_code(builder: &mut flatbuffers::FlatBufferBuilder) {
     let mon = {
index 32e9372..d6c8422 100644 (file)
@@ -95,3 +95,55 @@ make_test!(
      OptionalByte::None,
      OptionalByte::One
 );
+
+#[test]
+fn object_api_defaults() {
+    assert_eq!(
+        ScalarStuffT::default(),
+        ScalarStuffT {
+            just_i8: 0,
+            maybe_i8: None,
+            default_i8: 42,
+            just_u8: 0,
+            maybe_u8: None,
+            default_u8: 42,
+
+            just_i16: 0,
+            maybe_i16: None,
+            default_i16: 42,
+            just_u16: 0,
+            maybe_u16: None,
+            default_u16: 42,
+
+            just_i32: 0,
+            maybe_i32: None,
+            default_i32: 42,
+            just_u32: 0,
+            maybe_u32: None,
+            default_u32: 42,
+
+            just_i64: 0,
+            maybe_i64: None,
+            default_i64: 42,
+            just_u64: 0,
+            maybe_u64: None,
+            default_u64: 42,
+
+            just_f32: 0.0,
+            maybe_f32: None,
+            default_f32: 42.0,
+            just_f64: 0.0,
+            maybe_f64: None,
+            default_f64: 42.0,
+
+            just_bool: false,
+            maybe_bool: None,
+            default_bool: true,
+
+            just_enum: OptionalByte::None,
+            maybe_enum: None,
+            default_enum: OptionalByte::One,
+
+        }
+    );
+}