Optional Scalars support for Rust (#6034)
authorCasper <casperneo@uchicago.edu>
Thu, 23 Jul 2020 23:30:27 +0000 (16:30 -0700)
committerGitHub <noreply@github.com>
Thu, 23 Jul 2020 23:30:27 +0000 (16:30 -0700)
* First draft of rust optionals

* Code cleanup around ftBool and ftVectorOfBool

* Tests for Rust optional scalars

* test bools too

Co-authored-by: Casper Neo <cneo@google.com>
12 files changed:
rust/flexbuffers/src/lib.rs
src/idl_gen_rust.cpp
src/idl_parser.cpp
tests/generate_code.bat
tests/generate_code.sh
tests/monster_test_generated.rs
tests/optional_scalars.fbs [new file with mode: 0644]
tests/optional_scalars_generated.rs [new file with mode: 0644]
tests/rust_usage_test/Cargo.toml
tests/rust_usage_test/tests/integration_test.rs
tests/rust_usage_test/tests/optional_scalars_test.rs [new file with mode: 0644]
tests/test.cpp

index 482e15c..20983b7 100644 (file)
@@ -33,7 +33,6 @@ extern crate bitflags;
 extern crate byteorder;
 #[macro_use]
 extern crate serde_derive;
-#[macro_use]
 extern crate num_enum;
 extern crate serde;
 
index 45d5faf..8cdf647 100644 (file)
@@ -661,22 +661,15 @@ class RustGenerator : public BaseGenerator {
     return "VT_" + MakeUpper(Name(field));
   }
 
-  std::string GetDefaultConstant(const FieldDef &field) {
-    return field.value.type.base_type == BASE_TYPE_FLOAT
-               ? field.value.constant + ""
-               : field.value.constant;
-  }
-
   std::string GetDefaultScalarValue(const FieldDef &field) {
     switch (GetFullType(field.value.type)) {
-      case ftInteger: {
-        return GetDefaultConstant(field);
-      }
+      case ftInteger:
       case ftFloat: {
-        return GetDefaultConstant(field);
+        return field.nullable ? "None" : field.value.constant;
       }
       case ftBool: {
-        return field.value.constant == "0" ? "false" : "true";
+        return field.nullable ? "None" :
+          field.value.constant == "0" ? "false" : "true";
       }
       case ftUnionKey:
       case ftEnumKey: {
@@ -714,7 +707,7 @@ class RustGenerator : public BaseGenerator {
       case ftFloat:
       case ftBool: {
         const auto typname = GetTypeBasic(type);
-        return typname;
+        return field.nullable ? "Option<" + typname + ">" : typname;
       }
       case ftStruct: {
         const auto typname = WrapInNameSpace(*type.struct_def);
@@ -738,14 +731,12 @@ class RustGenerator : public BaseGenerator {
       }
 
       case ftVectorOfInteger:
+      case ftVectorOfBool:
       case ftVectorOfFloat: {
         const auto typname = GetTypeBasic(type.VectorType());
         return "Option<flatbuffers::WIPOffset<flatbuffers::Vector<" + lifetime +
                ",  " + typname + ">>>";
-      }
-      case ftVectorOfBool: {
-        return "Option<flatbuffers::WIPOffset<flatbuffers::Vector<" + lifetime +
-               ", bool>>>";
+               // TODO(cneo): Fix whitespace in generated code.
       }
       case ftVectorOfEnumKey: {
         const auto typname = WrapInNameSpace(*type.enum_def);
@@ -815,15 +806,12 @@ class RustGenerator : public BaseGenerator {
                ">>>>";
       }
       case ftVectorOfInteger:
+      case ftVectorOfBool:
       case ftVectorOfFloat: {
         const auto typname = GetTypeBasic(type.VectorType());
         return "flatbuffers::WIPOffset<flatbuffers::Vector<" + lifetime + ", " +
                typname + ">>";
       }
-      case ftVectorOfBool: {
-        return "flatbuffers::WIPOffset<flatbuffers::Vector<" + lifetime +
-               ", bool>>";
-      }
       case ftVectorOfString: {
         return "flatbuffers::WIPOffset<flatbuffers::Vector<" + lifetime +
                ", flatbuffers::ForwardsUOffset<&" + lifetime + " str>>>";
@@ -851,12 +839,9 @@ class RustGenerator : public BaseGenerator {
         return "flatbuffers::WIPOffset<" + typname + "<" + lifetime + ">>";
       }
       case ftInteger:
+      case ftBool:
       case ftFloat: {
-        const auto typname = GetTypeBasic(type);
-        return typname;
-      }
-      case ftBool: {
-        return "bool";
+        return GetTypeBasic(type);
       }
       case ftString: {
         return "flatbuffers::WIPOffset<&" + lifetime + " str>";
@@ -878,14 +863,13 @@ class RustGenerator : public BaseGenerator {
 
     switch (GetFullType(field.value.type)) {
       case ftInteger:
+      case ftBool:
       case ftFloat: {
         const auto typname = GetTypeBasic(field.value.type);
-        return "self.fbb_.push_slot::<" + typname + ">";
+        return (field.nullable ?
+                   "self.fbb_.push_slot_always::<" :
+                   "self.fbb_.push_slot::<") + typname + ">";
       }
-      case ftBool: {
-        return "self.fbb_.push_slot::<bool>";
-      }
-
       case ftEnumKey:
       case ftUnionKey: {
         const auto underlying_typname = GetTypeBasic(type);
@@ -924,12 +908,10 @@ class RustGenerator : public BaseGenerator {
 
     switch (GetFullType(field.value.type)) {
       case ftInteger:
-      case ftFloat: {
-        const auto typname = GetTypeBasic(type);
-        return typname;
-      }
+      case ftFloat:
       case ftBool: {
-        return "bool";
+        const auto typname = GetTypeBasic(type);
+        return field.nullable ? "Option<" + typname + ">" : typname;
       }
       case ftStruct: {
         const auto typname = WrapInNameSpace(*type.struct_def);
@@ -956,6 +938,7 @@ class RustGenerator : public BaseGenerator {
                                          field.required);
       }
       case ftVectorOfInteger:
+      case ftVectorOfBool:
       case ftVectorOfFloat: {
         const auto typname = GetTypeBasic(type.VectorType());
         if (IsOneByte(type.VectorType().base_type)) {
@@ -966,10 +949,6 @@ class RustGenerator : public BaseGenerator {
             "flatbuffers::Vector<" + lifetime + ", " + typname + ">",
             field.required);
       }
-      case ftVectorOfBool: {
-        return WrapInOptionIfNotRequired("&" + lifetime + " [bool]",
-                                         field.required);
-      }
       case ftVectorOfEnumKey: {
         const auto typname = WrapInNameSpace(*type.enum_def);
         return WrapInOptionIfNotRequired(
@@ -1016,9 +995,13 @@ class RustGenerator : public BaseGenerator {
       case ftFloat:
       case ftBool: {
         const auto typname = GetTypeBasic(type);
-        const auto default_value = GetDefaultScalarValue(field);
-        return "self._tab.get::<" + typname + ">(" + offset_name + ", Some(" +
-               default_value + ")).unwrap()";
+        if (field.nullable) {
+          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);
@@ -1056,6 +1039,7 @@ class RustGenerator : public BaseGenerator {
       }
 
       case ftVectorOfInteger:
+      case ftVectorOfBool:
       case ftVectorOfFloat: {
         const auto typname = GetTypeBasic(type.VectorType());
         std::string s =
@@ -1068,14 +1052,6 @@ class RustGenerator : public BaseGenerator {
         }
         return AddUnwrapIfRequired(s, field.required);
       }
-      case ftVectorOfBool: {
-        return AddUnwrapIfRequired(
-            "self._tab.get::<flatbuffers::ForwardsUOffset<"
-            "flatbuffers::Vector<" +
-                lifetime + ", bool>>>(" + offset_name +
-                ", None).map(|v| v.safe_slice())",
-            field.required);
-      }
       case ftVectorOfEnumKey: {
         const auto typname = WrapInNameSpace(*type.enum_def);
         return AddUnwrapIfRequired(
@@ -1116,8 +1092,9 @@ class RustGenerator : public BaseGenerator {
     return "INVALID_CODE_GENERATION";  // for return analysis
   }
 
-  bool TableFieldReturnsOption(const Type &type) {
-    switch (GetFullType(type)) {
+  bool TableFieldReturnsOption(const FieldDef &field) {
+    if (field.nullable) return true;
+    switch (GetFullType(field.value.type)) {
       case ftInteger:
       case ftFloat:
       case ftBool:
@@ -1205,7 +1182,7 @@ class RustGenerator : public BaseGenerator {
         if (!field.deprecated && (!struct_def.sortbysize ||
                                   size == SizeOf(field.value.type.base_type))) {
           code_.SetValue("FIELD_NAME", Name(field));
-          if (TableFieldReturnsOption(field.value.type)) {
+          if (TableFieldReturnsOption(field)) {
             code_ +=
                 "      if let Some(x) = args.{{FIELD_NAME}} "
                 "{ builder.add_{{FIELD_NAME}}(x); }";
@@ -1421,7 +1398,6 @@ class RustGenerator : public BaseGenerator {
       const auto &field = **it;
       if (!field.deprecated) {
         const bool is_scalar = IsScalar(field.value.type.base_type);
-
         std::string offset = GetFieldOffsetName(field);
 
         // Generate functions to add data, which take one of two forms.
@@ -1443,7 +1419,7 @@ class RustGenerator : public BaseGenerator {
         code_ +=
             "  pub fn add_{{FIELD_NAME}}(&mut self, {{FIELD_NAME}}: "
             "{{FIELD_TYPE}}) {";
-        if (is_scalar) {
+        if (is_scalar && !field.nullable) {
           code_.SetValue("FIELD_DEFAULT_VALUE",
                          TableBuilderAddFuncDefaultValue(field));
           code_ +=
index 5ccd690..eabf783 100644 (file)
@@ -2258,7 +2258,7 @@ CheckedError Parser::CheckClash(std::vector<FieldDef *> &fields,
 
 
 bool Parser::SupportsNullableScalars() const {
-  return opts.lang_to_generate == 0;  // No support yet.
+  return !(opts.lang_to_generate & ~IDLOptions::kRust);
 }
 
 bool Parser::SupportsAdvancedUnionFeatures() const {
index 6954c6b..05c58ba 100644 (file)
@@ -53,6 +53,9 @@ set TEST_NOINCL_FLAGS=%TEST_BASE_FLAGS% --no-includes --no-fb-import
 ..\%buildtype%\flatc.exe --python %TEST_BASE_FLAGS% arrays_test.fbs || goto FAIL
 ..\%buildtype%\flatc.exe --cpp %TEST_BASE_FLAGS% --cpp-ptr-type flatbuffers::unique_ptr native_type_test.fbs || goto FAIL
 
+@rem Generate the optional scalar code for tests.
+..\%buildtype%\flatc.exe --rust optional_scalars.fbs || goto FAIL
+
 @rem Generate the schema evolution tests
 ..\%buildtype%\flatc.exe --cpp --scoped-enums %TEST_CPP_FLAGS% -o evolution_test ./evolution_test/evolution_v1.fbs ./evolution_test/evolution_v2.fbs || goto FAIL
 
index 0b3e134..df583c3 100755 (executable)
@@ -51,6 +51,9 @@ $TEST_NOINCL_FLAGS $TEST_CPP_FLAGS $TEST_CS_FLAGS -o namespace_test namespace_te
 ../flatc --python $TEST_BASE_FLAGS arrays_test.fbs
 ../flatc --dart monster_extra.fbs
 
+# Generate optional scalar code for tests.
+../flatc --rust optional_scalars.fbs
+
 # Generate the schema evolution tests
 ../flatc --cpp --scoped-enums $TEST_CPP_FLAGS -o evolution_test ./evolution_test/evolution_v*.fbs
 
index 528505c..43fe727 100644 (file)
@@ -1488,7 +1488,7 @@ pub struct MonsterArgs<'a> {
     pub testhashu32_fnv1a: u32,
     pub testhashs64_fnv1a: i64,
     pub testhashu64_fnv1a: u64,
-    pub testarrayofbools: Option<flatbuffers::WIPOffset<flatbuffers::Vector<'a , bool>>>,
+    pub testarrayofbools: Option<flatbuffers::WIPOffset<flatbuffers::Vector<'a ,  bool>>>,
     pub testf: f32,
     pub testf2: f32,
     pub testf3: f32,
diff --git a/tests/optional_scalars.fbs b/tests/optional_scalars.fbs
new file mode 100644 (file)
index 0000000..051bae3
--- /dev/null
@@ -0,0 +1,44 @@
+namespace optional_scalars;
+
+// This table tests optional scalars in tables. It should be integrated with
+// the main monster test once most languages support optional scalars.
+table ScalarStuff {
+  just_i8: int8;
+  maybe_i8: int8 = null;
+  default_i8: int8 = 42;
+  just_u8: uint8;
+  maybe_u8: uint8 = null;
+  default_u8: uint8 = 42;
+
+  just_i16: int16;
+  maybe_i16: int16 = null;
+  default_i16: int16 = 42;
+  just_u16: uint16;
+  maybe_u16: uint16 = null;
+  default_u16: uint16 = 42;
+
+  just_i32: int32;
+  maybe_i32: int32 = null;
+  default_i32: int32 = 42;
+  just_u32: uint32;
+  maybe_u32: uint32 = null;
+  default_u32: uint32 = 42;
+
+  just_i64: int64;
+  maybe_i64: int64 = null;
+  default_i64: int64 = 42;
+  just_u64: uint64;
+  maybe_u64: uint64 = null;
+  default_u64: uint64 = 42;
+
+  just_f32: float32;
+  maybe_f32: float32 = null;
+  default_f32: float32 = 42;
+  just_f64: float64;
+  maybe_f64: float64 = null;
+  default_f64: float64 = 42;
+
+  just_bool: bool;
+  maybe_bool: bool = null;
+  default_bool: bool = true;
+}
diff --git a/tests/optional_scalars_generated.rs b/tests/optional_scalars_generated.rs
new file mode 100644 (file)
index 0000000..78ee856
--- /dev/null
@@ -0,0 +1,481 @@
+// automatically generated by the FlatBuffers compiler, do not modify
+
+
+
+use std::mem;
+use std::cmp::Ordering;
+
+extern crate flatbuffers;
+use self::flatbuffers::EndianScalar;
+
+#[allow(unused_imports, dead_code)]
+pub mod optional_scalars {
+
+  use std::mem;
+  use std::cmp::Ordering;
+
+  extern crate flatbuffers;
+  use self::flatbuffers::EndianScalar;
+
+pub enum ScalarStuffOffset {}
+#[derive(Copy, Clone, Debug, PartialEq)]
+
+pub struct ScalarStuff<'a> {
+  pub _tab: flatbuffers::Table<'a>,
+}
+
+impl<'a> flatbuffers::Follow<'a> for ScalarStuff<'a> {
+    type Inner = ScalarStuff<'a>;
+    #[inline]
+    fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {
+        Self {
+            _tab: flatbuffers::Table { buf: buf, loc: loc },
+        }
+    }
+}
+
+impl<'a> ScalarStuff<'a> {
+    #[inline]
+    pub fn init_from_table(table: flatbuffers::Table<'a>) -> Self {
+        ScalarStuff {
+            _tab: table,
+        }
+    }
+    #[allow(unused_mut)]
+    pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr>(
+        _fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr>,
+        args: &'args ScalarStuffArgs) -> flatbuffers::WIPOffset<ScalarStuff<'bldr>> {
+      let mut builder = ScalarStuffBuilder::new(_fbb);
+      builder.add_default_f64(args.default_f64);
+      if let Some(x) = args.maybe_f64 { builder.add_maybe_f64(x); }
+      builder.add_just_f64(args.just_f64);
+      builder.add_default_u64(args.default_u64);
+      if let Some(x) = args.maybe_u64 { builder.add_maybe_u64(x); }
+      builder.add_just_u64(args.just_u64);
+      builder.add_default_i64(args.default_i64);
+      if let Some(x) = args.maybe_i64 { builder.add_maybe_i64(x); }
+      builder.add_just_i64(args.just_i64);
+      builder.add_default_f32(args.default_f32);
+      if let Some(x) = args.maybe_f32 { builder.add_maybe_f32(x); }
+      builder.add_just_f32(args.just_f32);
+      builder.add_default_u32(args.default_u32);
+      if let Some(x) = args.maybe_u32 { builder.add_maybe_u32(x); }
+      builder.add_just_u32(args.just_u32);
+      builder.add_default_i32(args.default_i32);
+      if let Some(x) = args.maybe_i32 { builder.add_maybe_i32(x); }
+      builder.add_just_i32(args.just_i32);
+      builder.add_default_u16(args.default_u16);
+      if let Some(x) = args.maybe_u16 { builder.add_maybe_u16(x); }
+      builder.add_just_u16(args.just_u16);
+      builder.add_default_i16(args.default_i16);
+      if let Some(x) = args.maybe_i16 { builder.add_maybe_i16(x); }
+      builder.add_just_i16(args.just_i16);
+      builder.add_default_bool(args.default_bool);
+      if let Some(x) = args.maybe_bool { builder.add_maybe_bool(x); }
+      builder.add_just_bool(args.just_bool);
+      builder.add_default_u8(args.default_u8);
+      if let Some(x) = args.maybe_u8 { builder.add_maybe_u8(x); }
+      builder.add_just_u8(args.just_u8);
+      builder.add_default_i8(args.default_i8);
+      if let Some(x) = args.maybe_i8 { builder.add_maybe_i8(x); }
+      builder.add_just_i8(args.just_i8);
+      builder.finish()
+    }
+
+    pub const VT_JUST_I8: flatbuffers::VOffsetT = 4;
+    pub const VT_MAYBE_I8: flatbuffers::VOffsetT = 6;
+    pub const VT_DEFAULT_I8: flatbuffers::VOffsetT = 8;
+    pub const VT_JUST_U8: flatbuffers::VOffsetT = 10;
+    pub const VT_MAYBE_U8: flatbuffers::VOffsetT = 12;
+    pub const VT_DEFAULT_U8: flatbuffers::VOffsetT = 14;
+    pub const VT_JUST_I16: flatbuffers::VOffsetT = 16;
+    pub const VT_MAYBE_I16: flatbuffers::VOffsetT = 18;
+    pub const VT_DEFAULT_I16: flatbuffers::VOffsetT = 20;
+    pub const VT_JUST_U16: flatbuffers::VOffsetT = 22;
+    pub const VT_MAYBE_U16: flatbuffers::VOffsetT = 24;
+    pub const VT_DEFAULT_U16: flatbuffers::VOffsetT = 26;
+    pub const VT_JUST_I32: flatbuffers::VOffsetT = 28;
+    pub const VT_MAYBE_I32: flatbuffers::VOffsetT = 30;
+    pub const VT_DEFAULT_I32: flatbuffers::VOffsetT = 32;
+    pub const VT_JUST_U32: flatbuffers::VOffsetT = 34;
+    pub const VT_MAYBE_U32: flatbuffers::VOffsetT = 36;
+    pub const VT_DEFAULT_U32: flatbuffers::VOffsetT = 38;
+    pub const VT_JUST_I64: flatbuffers::VOffsetT = 40;
+    pub const VT_MAYBE_I64: flatbuffers::VOffsetT = 42;
+    pub const VT_DEFAULT_I64: flatbuffers::VOffsetT = 44;
+    pub const VT_JUST_U64: flatbuffers::VOffsetT = 46;
+    pub const VT_MAYBE_U64: flatbuffers::VOffsetT = 48;
+    pub const VT_DEFAULT_U64: flatbuffers::VOffsetT = 50;
+    pub const VT_JUST_F32: flatbuffers::VOffsetT = 52;
+    pub const VT_MAYBE_F32: flatbuffers::VOffsetT = 54;
+    pub const VT_DEFAULT_F32: flatbuffers::VOffsetT = 56;
+    pub const VT_JUST_F64: flatbuffers::VOffsetT = 58;
+    pub const VT_MAYBE_F64: flatbuffers::VOffsetT = 60;
+    pub const VT_DEFAULT_F64: flatbuffers::VOffsetT = 62;
+    pub const VT_JUST_BOOL: flatbuffers::VOffsetT = 64;
+    pub const VT_MAYBE_BOOL: flatbuffers::VOffsetT = 66;
+    pub const VT_DEFAULT_BOOL: flatbuffers::VOffsetT = 68;
+
+  #[inline]
+  pub fn just_i8(&self) -> i8 {
+    self._tab.get::<i8>(ScalarStuff::VT_JUST_I8, Some(0)).unwrap()
+  }
+  #[inline]
+  pub fn maybe_i8(&self) -> Option<i8> {
+    self._tab.get::<i8>(ScalarStuff::VT_MAYBE_I8, None)
+  }
+  #[inline]
+  pub fn default_i8(&self) -> i8 {
+    self._tab.get::<i8>(ScalarStuff::VT_DEFAULT_I8, Some(42)).unwrap()
+  }
+  #[inline]
+  pub fn just_u8(&self) -> u8 {
+    self._tab.get::<u8>(ScalarStuff::VT_JUST_U8, Some(0)).unwrap()
+  }
+  #[inline]
+  pub fn maybe_u8(&self) -> Option<u8> {
+    self._tab.get::<u8>(ScalarStuff::VT_MAYBE_U8, None)
+  }
+  #[inline]
+  pub fn default_u8(&self) -> u8 {
+    self._tab.get::<u8>(ScalarStuff::VT_DEFAULT_U8, Some(42)).unwrap()
+  }
+  #[inline]
+  pub fn just_i16(&self) -> i16 {
+    self._tab.get::<i16>(ScalarStuff::VT_JUST_I16, Some(0)).unwrap()
+  }
+  #[inline]
+  pub fn maybe_i16(&self) -> Option<i16> {
+    self._tab.get::<i16>(ScalarStuff::VT_MAYBE_I16, None)
+  }
+  #[inline]
+  pub fn default_i16(&self) -> i16 {
+    self._tab.get::<i16>(ScalarStuff::VT_DEFAULT_I16, Some(42)).unwrap()
+  }
+  #[inline]
+  pub fn just_u16(&self) -> u16 {
+    self._tab.get::<u16>(ScalarStuff::VT_JUST_U16, Some(0)).unwrap()
+  }
+  #[inline]
+  pub fn maybe_u16(&self) -> Option<u16> {
+    self._tab.get::<u16>(ScalarStuff::VT_MAYBE_U16, None)
+  }
+  #[inline]
+  pub fn default_u16(&self) -> u16 {
+    self._tab.get::<u16>(ScalarStuff::VT_DEFAULT_U16, Some(42)).unwrap()
+  }
+  #[inline]
+  pub fn just_i32(&self) -> i32 {
+    self._tab.get::<i32>(ScalarStuff::VT_JUST_I32, Some(0)).unwrap()
+  }
+  #[inline]
+  pub fn maybe_i32(&self) -> Option<i32> {
+    self._tab.get::<i32>(ScalarStuff::VT_MAYBE_I32, None)
+  }
+  #[inline]
+  pub fn default_i32(&self) -> i32 {
+    self._tab.get::<i32>(ScalarStuff::VT_DEFAULT_I32, Some(42)).unwrap()
+  }
+  #[inline]
+  pub fn just_u32(&self) -> u32 {
+    self._tab.get::<u32>(ScalarStuff::VT_JUST_U32, Some(0)).unwrap()
+  }
+  #[inline]
+  pub fn maybe_u32(&self) -> Option<u32> {
+    self._tab.get::<u32>(ScalarStuff::VT_MAYBE_U32, None)
+  }
+  #[inline]
+  pub fn default_u32(&self) -> u32 {
+    self._tab.get::<u32>(ScalarStuff::VT_DEFAULT_U32, Some(42)).unwrap()
+  }
+  #[inline]
+  pub fn just_i64(&self) -> i64 {
+    self._tab.get::<i64>(ScalarStuff::VT_JUST_I64, Some(0)).unwrap()
+  }
+  #[inline]
+  pub fn maybe_i64(&self) -> Option<i64> {
+    self._tab.get::<i64>(ScalarStuff::VT_MAYBE_I64, None)
+  }
+  #[inline]
+  pub fn default_i64(&self) -> i64 {
+    self._tab.get::<i64>(ScalarStuff::VT_DEFAULT_I64, Some(42)).unwrap()
+  }
+  #[inline]
+  pub fn just_u64(&self) -> u64 {
+    self._tab.get::<u64>(ScalarStuff::VT_JUST_U64, Some(0)).unwrap()
+  }
+  #[inline]
+  pub fn maybe_u64(&self) -> Option<u64> {
+    self._tab.get::<u64>(ScalarStuff::VT_MAYBE_U64, None)
+  }
+  #[inline]
+  pub fn default_u64(&self) -> u64 {
+    self._tab.get::<u64>(ScalarStuff::VT_DEFAULT_U64, Some(42)).unwrap()
+  }
+  #[inline]
+  pub fn just_f32(&self) -> f32 {
+    self._tab.get::<f32>(ScalarStuff::VT_JUST_F32, Some(0.0)).unwrap()
+  }
+  #[inline]
+  pub fn maybe_f32(&self) -> Option<f32> {
+    self._tab.get::<f32>(ScalarStuff::VT_MAYBE_F32, None)
+  }
+  #[inline]
+  pub fn default_f32(&self) -> f32 {
+    self._tab.get::<f32>(ScalarStuff::VT_DEFAULT_F32, Some(42.0)).unwrap()
+  }
+  #[inline]
+  pub fn just_f64(&self) -> f64 {
+    self._tab.get::<f64>(ScalarStuff::VT_JUST_F64, Some(0.0)).unwrap()
+  }
+  #[inline]
+  pub fn maybe_f64(&self) -> Option<f64> {
+    self._tab.get::<f64>(ScalarStuff::VT_MAYBE_F64, None)
+  }
+  #[inline]
+  pub fn default_f64(&self) -> f64 {
+    self._tab.get::<f64>(ScalarStuff::VT_DEFAULT_F64, Some(42.0)).unwrap()
+  }
+  #[inline]
+  pub fn just_bool(&self) -> bool {
+    self._tab.get::<bool>(ScalarStuff::VT_JUST_BOOL, Some(false)).unwrap()
+  }
+  #[inline]
+  pub fn maybe_bool(&self) -> Option<bool> {
+    self._tab.get::<bool>(ScalarStuff::VT_MAYBE_BOOL, None)
+  }
+  #[inline]
+  pub fn default_bool(&self) -> bool {
+    self._tab.get::<bool>(ScalarStuff::VT_DEFAULT_BOOL, Some(true)).unwrap()
+  }
+}
+
+pub struct ScalarStuffArgs {
+    pub just_i8: i8,
+    pub maybe_i8: Option<i8>,
+    pub default_i8: i8,
+    pub just_u8: u8,
+    pub maybe_u8: Option<u8>,
+    pub default_u8: u8,
+    pub just_i16: i16,
+    pub maybe_i16: Option<i16>,
+    pub default_i16: i16,
+    pub just_u16: u16,
+    pub maybe_u16: Option<u16>,
+    pub default_u16: u16,
+    pub just_i32: i32,
+    pub maybe_i32: Option<i32>,
+    pub default_i32: i32,
+    pub just_u32: u32,
+    pub maybe_u32: Option<u32>,
+    pub default_u32: u32,
+    pub just_i64: i64,
+    pub maybe_i64: Option<i64>,
+    pub default_i64: i64,
+    pub just_u64: u64,
+    pub maybe_u64: Option<u64>,
+    pub default_u64: u64,
+    pub just_f32: f32,
+    pub maybe_f32: Option<f32>,
+    pub default_f32: f32,
+    pub just_f64: f64,
+    pub maybe_f64: Option<f64>,
+    pub default_f64: f64,
+    pub just_bool: bool,
+    pub maybe_bool: Option<bool>,
+    pub default_bool: bool,
+}
+impl<'a> Default for ScalarStuffArgs {
+    #[inline]
+    fn default() -> Self {
+        ScalarStuffArgs {
+            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,
+        }
+    }
+}
+pub struct ScalarStuffBuilder<'a: 'b, 'b> {
+  fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a>,
+  start_: flatbuffers::WIPOffset<flatbuffers::TableUnfinishedWIPOffset>,
+}
+impl<'a: 'b, 'b> ScalarStuffBuilder<'a, 'b> {
+  #[inline]
+  pub fn add_just_i8(&mut self, just_i8: i8) {
+    self.fbb_.push_slot::<i8>(ScalarStuff::VT_JUST_I8, just_i8, 0);
+  }
+  #[inline]
+  pub fn add_maybe_i8(&mut self, maybe_i8: i8) {
+    self.fbb_.push_slot_always::<i8>(ScalarStuff::VT_MAYBE_I8, maybe_i8);
+  }
+  #[inline]
+  pub fn add_default_i8(&mut self, default_i8: i8) {
+    self.fbb_.push_slot::<i8>(ScalarStuff::VT_DEFAULT_I8, default_i8, 42);
+  }
+  #[inline]
+  pub fn add_just_u8(&mut self, just_u8: u8) {
+    self.fbb_.push_slot::<u8>(ScalarStuff::VT_JUST_U8, just_u8, 0);
+  }
+  #[inline]
+  pub fn add_maybe_u8(&mut self, maybe_u8: u8) {
+    self.fbb_.push_slot_always::<u8>(ScalarStuff::VT_MAYBE_U8, maybe_u8);
+  }
+  #[inline]
+  pub fn add_default_u8(&mut self, default_u8: u8) {
+    self.fbb_.push_slot::<u8>(ScalarStuff::VT_DEFAULT_U8, default_u8, 42);
+  }
+  #[inline]
+  pub fn add_just_i16(&mut self, just_i16: i16) {
+    self.fbb_.push_slot::<i16>(ScalarStuff::VT_JUST_I16, just_i16, 0);
+  }
+  #[inline]
+  pub fn add_maybe_i16(&mut self, maybe_i16: i16) {
+    self.fbb_.push_slot_always::<i16>(ScalarStuff::VT_MAYBE_I16, maybe_i16);
+  }
+  #[inline]
+  pub fn add_default_i16(&mut self, default_i16: i16) {
+    self.fbb_.push_slot::<i16>(ScalarStuff::VT_DEFAULT_I16, default_i16, 42);
+  }
+  #[inline]
+  pub fn add_just_u16(&mut self, just_u16: u16) {
+    self.fbb_.push_slot::<u16>(ScalarStuff::VT_JUST_U16, just_u16, 0);
+  }
+  #[inline]
+  pub fn add_maybe_u16(&mut self, maybe_u16: u16) {
+    self.fbb_.push_slot_always::<u16>(ScalarStuff::VT_MAYBE_U16, maybe_u16);
+  }
+  #[inline]
+  pub fn add_default_u16(&mut self, default_u16: u16) {
+    self.fbb_.push_slot::<u16>(ScalarStuff::VT_DEFAULT_U16, default_u16, 42);
+  }
+  #[inline]
+  pub fn add_just_i32(&mut self, just_i32: i32) {
+    self.fbb_.push_slot::<i32>(ScalarStuff::VT_JUST_I32, just_i32, 0);
+  }
+  #[inline]
+  pub fn add_maybe_i32(&mut self, maybe_i32: i32) {
+    self.fbb_.push_slot_always::<i32>(ScalarStuff::VT_MAYBE_I32, maybe_i32);
+  }
+  #[inline]
+  pub fn add_default_i32(&mut self, default_i32: i32) {
+    self.fbb_.push_slot::<i32>(ScalarStuff::VT_DEFAULT_I32, default_i32, 42);
+  }
+  #[inline]
+  pub fn add_just_u32(&mut self, just_u32: u32) {
+    self.fbb_.push_slot::<u32>(ScalarStuff::VT_JUST_U32, just_u32, 0);
+  }
+  #[inline]
+  pub fn add_maybe_u32(&mut self, maybe_u32: u32) {
+    self.fbb_.push_slot_always::<u32>(ScalarStuff::VT_MAYBE_U32, maybe_u32);
+  }
+  #[inline]
+  pub fn add_default_u32(&mut self, default_u32: u32) {
+    self.fbb_.push_slot::<u32>(ScalarStuff::VT_DEFAULT_U32, default_u32, 42);
+  }
+  #[inline]
+  pub fn add_just_i64(&mut self, just_i64: i64) {
+    self.fbb_.push_slot::<i64>(ScalarStuff::VT_JUST_I64, just_i64, 0);
+  }
+  #[inline]
+  pub fn add_maybe_i64(&mut self, maybe_i64: i64) {
+    self.fbb_.push_slot_always::<i64>(ScalarStuff::VT_MAYBE_I64, maybe_i64);
+  }
+  #[inline]
+  pub fn add_default_i64(&mut self, default_i64: i64) {
+    self.fbb_.push_slot::<i64>(ScalarStuff::VT_DEFAULT_I64, default_i64, 42);
+  }
+  #[inline]
+  pub fn add_just_u64(&mut self, just_u64: u64) {
+    self.fbb_.push_slot::<u64>(ScalarStuff::VT_JUST_U64, just_u64, 0);
+  }
+  #[inline]
+  pub fn add_maybe_u64(&mut self, maybe_u64: u64) {
+    self.fbb_.push_slot_always::<u64>(ScalarStuff::VT_MAYBE_U64, maybe_u64);
+  }
+  #[inline]
+  pub fn add_default_u64(&mut self, default_u64: u64) {
+    self.fbb_.push_slot::<u64>(ScalarStuff::VT_DEFAULT_U64, default_u64, 42);
+  }
+  #[inline]
+  pub fn add_just_f32(&mut self, just_f32: f32) {
+    self.fbb_.push_slot::<f32>(ScalarStuff::VT_JUST_F32, just_f32, 0.0);
+  }
+  #[inline]
+  pub fn add_maybe_f32(&mut self, maybe_f32: f32) {
+    self.fbb_.push_slot_always::<f32>(ScalarStuff::VT_MAYBE_F32, maybe_f32);
+  }
+  #[inline]
+  pub fn add_default_f32(&mut self, default_f32: f32) {
+    self.fbb_.push_slot::<f32>(ScalarStuff::VT_DEFAULT_F32, default_f32, 42.0);
+  }
+  #[inline]
+  pub fn add_just_f64(&mut self, just_f64: f64) {
+    self.fbb_.push_slot::<f64>(ScalarStuff::VT_JUST_F64, just_f64, 0.0);
+  }
+  #[inline]
+  pub fn add_maybe_f64(&mut self, maybe_f64: f64) {
+    self.fbb_.push_slot_always::<f64>(ScalarStuff::VT_MAYBE_F64, maybe_f64);
+  }
+  #[inline]
+  pub fn add_default_f64(&mut self, default_f64: f64) {
+    self.fbb_.push_slot::<f64>(ScalarStuff::VT_DEFAULT_F64, default_f64, 42.0);
+  }
+  #[inline]
+  pub fn add_just_bool(&mut self, just_bool: bool) {
+    self.fbb_.push_slot::<bool>(ScalarStuff::VT_JUST_BOOL, just_bool, false);
+  }
+  #[inline]
+  pub fn add_maybe_bool(&mut self, maybe_bool: bool) {
+    self.fbb_.push_slot_always::<bool>(ScalarStuff::VT_MAYBE_BOOL, maybe_bool);
+  }
+  #[inline]
+  pub fn add_default_bool(&mut self, default_bool: bool) {
+    self.fbb_.push_slot::<bool>(ScalarStuff::VT_DEFAULT_BOOL, default_bool, true);
+  }
+  #[inline]
+  pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a>) -> ScalarStuffBuilder<'a, 'b> {
+    let start = _fbb.start_table();
+    ScalarStuffBuilder {
+      fbb_: _fbb,
+      start_: start,
+    }
+  }
+  #[inline]
+  pub fn finish(self) -> flatbuffers::WIPOffset<ScalarStuff<'a>> {
+    let o = self.fbb_.end_table(self.start_);
+    flatbuffers::WIPOffset::new(o.value())
+  }
+}
+
+}  // pub mod optional_scalars
+
index e6a5c1d..6fa98a9 100644 (file)
@@ -1,7 +1,9 @@
 [package]
 name = "rust_usage_test"
 version = "0.1.0"
-authors = ["Robert Winslow <hello@rwinslow.com>", "FlatBuffers Maintainers"]
+authors = ["Robert Winslow <hello@rwinslow.com>",
+           "Casper Neo <cneo@google.com>",
+           "FlatBuffers Maintainers"]
 
 [dependencies]
 flatbuffers = { path = "../../rust/flatbuffers" }
index 1aea51c..b861cf0 100644 (file)
@@ -27,6 +27,7 @@ extern crate serde_derive;
 extern crate quickcheck_derive;
 
 mod flexbuffers_tests;
+mod optional_scalars_test;
 
 #[allow(dead_code, unused_imports)]
 #[path = "../../include_test/include_test1_generated.rs"]
@@ -41,6 +42,10 @@ pub mod include_test2_generated;
 mod monster_test_generated;
 pub use monster_test_generated::my_game;
 
+#[allow(dead_code, unused_imports)]
+#[path = "../../optional_scalars_generated.rs"]
+mod optional_scalars_generated;
+
 #[rustfmt::skip] // TODO: Use standard rust formatting and remove dead code.
 #[allow(dead_code)]
 mod flatbuffers_tests {
diff --git a/tests/rust_usage_test/tests/optional_scalars_test.rs b/tests/rust_usage_test/tests/optional_scalars_test.rs
new file mode 100644 (file)
index 0000000..4b63de2
--- /dev/null
@@ -0,0 +1,52 @@
+#[allow(dead_code, unused_imports)]
+#[path = "../../optional_scalars_generated.rs"]
+mod optional_scalars_generated;
+use crate::optional_scalars_generated::optional_scalars::*;
+
+// There are 3 variants of scalars in tables - those specified with default=42,
+// optional scalars, and those with nothing specified (implicitly default=0).
+// This tests that you can read what you write.
+macro_rules! make_test {
+    (
+        $test_name: ident,
+        $just: ident, $default: ident, $maybe: ident,
+        $five: expr, $zero: expr, $fortytwo: expr
+    ) => {
+        #[test]
+        fn $test_name() {
+            let mut builder = flatbuffers::FlatBufferBuilder::new();
+            // Test five makes sense when specified.
+            let ss = ScalarStuff::create(&mut builder, &ScalarStuffArgs {
+                $just: $five,
+                $default: $five,
+                $maybe: Some($five),
+                ..Default::default()
+            });
+            builder.finish(ss, None);
+
+            let s = flatbuffers::get_root::<ScalarStuff>(builder.finished_data());
+            assert_eq!(s.$just(), $five);
+            assert_eq!(s.$default(), $five);
+            assert_eq!(s.$maybe(), Some($five));
+
+            // Test defaults are used when not specified.
+            let s = flatbuffers::get_root::<ScalarStuff>(&[0; 8]);
+            assert_eq!(s.$just(), $zero);
+            assert_eq!(s.$default(), $fortytwo);
+            assert_eq!(s.$maybe(), None);
+        }
+
+    };
+}
+
+make_test!(optional_i8, just_i8, default_i8, maybe_i8, 5, 0, 42);
+make_test!(optional_u8, just_u8, default_u8, maybe_u8, 5, 0, 42);
+make_test!(optional_i16, just_i16, default_i16, maybe_i16, 5, 0, 42);
+make_test!(optional_u16, just_u16, default_u16, maybe_u16, 5, 0, 42);
+make_test!(optional_i32, just_i32, default_i32, maybe_i32, 5, 0, 42);
+make_test!(optional_u32, just_u32, default_u32, maybe_u32, 5, 0, 42);
+make_test!(optional_i64, just_i64, default_i64, maybe_i64, 5, 0, 42);
+make_test!(optional_u64, just_u64, default_u64, maybe_u64, 5, 0, 42);
+make_test!(optional_f32, just_f32, default_f32, maybe_f32, 5.0, 0.0, 42.0);
+make_test!(optional_f64, just_f64, default_f64, maybe_f64, 5.0, 0.0, 42.0);
+make_test!(optional_bool, just_bool, default_bool, maybe_bool, true, false, true);
index b4a358c..50e2902 100644 (file)
@@ -3457,9 +3457,11 @@ void NullableScalarsTest() {
 
   // Test if nullable scalars are allowed for each language.
   const int kNumLanguages = 17;
+  const auto supported = flatbuffers::IDLOptions::kRust;
   for (int lang=0; lang<kNumLanguages; lang++) {
     flatbuffers::IDLOptions opts;
     opts.lang_to_generate |= 1 << lang;
+    if (opts.lang_to_generate & supported) continue;
     for (auto schema = schemas.begin(); schema < schemas.end(); schema++) {
       const bool has_null = schema->find("null") != std::string::npos;
       flatbuffers::Parser parser(opts);