diff --git a/rust/flatbuffers/Cargo.toml b/rust/flatbuffers/Cargo.toml index 5202bd97e67..ed37f4ab45b 100644 --- a/rust/flatbuffers/Cargo.toml +++ b/rust/flatbuffers/Cargo.toml @@ -11,3 +11,4 @@ categories = ["encoding", "data-structures", "memory-management"] [dependencies] smallvec = "1.0" +thiserror = "1.0" diff --git a/rust/flatbuffers/src/error.rs b/rust/flatbuffers/src/error.rs new file mode 100644 index 00000000000..4e0eab73ed1 --- /dev/null +++ b/rust/flatbuffers/src/error.rs @@ -0,0 +1,9 @@ +use std::fmt::{Debug, Display}; + +use thiserror::Error; + +#[derive(Error, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug)] +pub enum ConvertError { + #[error("unknown variant in buffer: {0}")] + UnknownVariant(T), +} diff --git a/rust/flatbuffers/src/lib.rs b/rust/flatbuffers/src/lib.rs index a1d2745e1b7..00a970b5305 100644 --- a/rust/flatbuffers/src/lib.rs +++ b/rust/flatbuffers/src/lib.rs @@ -27,9 +27,11 @@ //! //! At this time, to generate Rust code, you will need the latest `master` version of `flatc`, available from here: https://github.com/google/flatbuffers //! (On OSX, you can install FlatBuffers from `HEAD` with the Homebrew package manager.) +extern crate thiserror; mod builder; mod endian_scalar; +mod error; mod follow; mod primitives; mod push; @@ -42,6 +44,7 @@ pub use builder::FlatBufferBuilder; pub use endian_scalar::{ byte_swap_f32, byte_swap_f64, emplace_scalar, read_scalar, read_scalar_at, EndianScalar, }; +pub use error::ConvertError; pub use follow::{Follow, FollowStart}; pub use primitives::*; pub use push::Push; diff --git a/src/idl_gen_rust.cpp b/src/idl_gen_rust.cpp index 1e7502ae03a..802c09440d1 100644 --- a/src/idl_gen_rust.cpp +++ b/src/idl_gen_rust.cpp @@ -515,6 +515,10 @@ class RustGenerator : public BaseGenerator { return Name(enum_def) + "::" + Name(enum_val); } + static bool IsBitFlags(const EnumDef &enum_def) { + return enum_def.attributes.Lookup("bit_flags") != nullptr; + } + // Generate an enum declaration, // an enum string lookup table, // an enum match function, @@ -552,12 +556,19 @@ class RustGenerator : public BaseGenerator { code_.SetValue("ENUM_MIN_BASE_VALUE", enum_def.ToString(*minv)); code_.SetValue("ENUM_MAX_BASE_VALUE", enum_def.ToString(*maxv)); - // Generate enum constants, and impls for Follow, EndianScalar, and Push. + // Generate enum constants, and impls for TryFrom, Follow, EndianScalar, and Push. + code_ += "#[deprecated(since = \"1.13\", note = \"Use associated constants instead.\")]"; code_ += "pub const ENUM_MIN_{{ENUM_NAME_CAPS}}: {{BASE_TYPE}} = \\"; code_ += "{{ENUM_MIN_BASE_VALUE}};"; + code_ += "#[deprecated(since = \"1.13\", note = \"Use associated constants instead.\")]"; code_ += "pub const ENUM_MAX_{{ENUM_NAME_CAPS}}: {{BASE_TYPE}} = \\"; code_ += "{{ENUM_MAX_BASE_VALUE}};"; code_ += ""; + code_ += "impl {{ENUM_NAME}} {"; + code_ += " pub const MIN: {{BASE_TYPE}} = {{ENUM_MIN_BASE_VALUE}};"; + code_ += " pub const MAX: {{BASE_TYPE}} = {{ENUM_MAX_BASE_VALUE}};"; + code_ += "}"; + code_ += ""; code_ += "impl<'a> flatbuffers::Follow<'a> for {{ENUM_NAME}} {"; code_ += " type Inner = Self;"; code_ += " #[inline]"; @@ -592,9 +603,33 @@ class RustGenerator : public BaseGenerator { code_ += "}"; code_ += ""; + if (!IsBitFlags(enum_def)) { + code_ += "impl TryFrom<{{BASE_TYPE}}> for {{ENUM_NAME}} {"; + code_ += " type Error = flatbuffers::ConvertError<{{BASE_TYPE}}>;"; + code_ += ""; + code_ += " #[inline]"; + code_ += " fn try_from(value: {{BASE_TYPE}}) -> Result {"; + code_ += " match value {"; + + for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); ++it) { + const auto &ev = **it; + + code_.SetValue("KEY", Name(ev)); + code_.SetValue("VALUE", enum_def.ToString(ev)); + code_ += " {{VALUE}} => Ok({{ENUM_NAME}}::{{KEY}}),"; + } + + code_ += " _ => Err(Self::Error::UnknownVariant(value))", + code_ += " }"; + code_ += " }"; + code_ += "}"; + code_ += ""; + } + // Generate an array of all enumeration values. auto num_fields = NumToString(enum_def.size()); code_ += "#[allow(non_camel_case_types)]"; + code_ += "#[deprecated(since = \"1.13\", note = \"Use associated constants instead.\")]"; code_ += "pub const ENUM_VALUES_{{ENUM_NAME_CAPS}}: [{{ENUM_NAME}}; " + num_fields + "] = ["; for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); ++it) { @@ -615,21 +650,21 @@ class RustGenerator : public BaseGenerator { // "too sparse". Change at will. static const uint64_t kMaxSparseness = 5; if (range / static_cast(enum_def.size()) < kMaxSparseness) { + code_ += "impl {{ENUM_NAME}} {"; + code_ += " pub const NAMES: [&'static str; " + NumToString(range + 1) + "] = ["; + + EnumerateEnumNames(enum_def); + + code_ += " ];"; + code_ += "}"; + code_ += ""; code_ += "#[allow(non_camel_case_types)]"; + code_ += "#[deprecated(since = \"1.13\", note = \"Use associated constants instead.\")]"; code_ += "pub const ENUM_NAMES_{{ENUM_NAME_CAPS}}: [&str; " + NumToString(range + 1) + "] = ["; - auto val = enum_def.Vals().front(); - for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); - ++it) { - auto ev = *it; - for (auto k = enum_def.Distance(val, ev); k > 1; --k) { - code_ += " \"\","; - } - val = ev; - auto suffix = *it != enum_def.Vals().back() ? "," : ""; - code_ += " \"" + Name(*ev) + "\"" + suffix; - } + EnumerateEnumNames(enum_def); + code_ += "];"; code_ += ""; @@ -644,7 +679,7 @@ class RustGenerator : public BaseGenerator { } code_ += ";"; - code_ += " ENUM_NAMES_{{ENUM_NAME_CAPS}}[index as usize]"; + code_ += " {{ENUM_NAME}}::NAMES[index as usize]"; code_ += "}"; code_ += ""; } @@ -657,6 +692,20 @@ class RustGenerator : public BaseGenerator { } } + void EnumerateEnumNames(const EnumDef &enum_def) { + auto val = enum_def.Vals().front(); + for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); + ++it) { + auto ev = *it; + for (auto k = enum_def.Distance(val, ev); k > 1; --k) { + code_ += " \"\","; + } + val = ev; + auto suffix = *it != enum_def.Vals().back() ? "," : ""; + code_ += " \"" + Name(*ev) + "\"" + suffix; + } + } + std::string GetFieldOffsetName(const FieldDef &field) { return "VT_" + MakeUpper(Name(field)); } @@ -907,7 +956,8 @@ class RustGenerator : public BaseGenerator { } std::string GenTableAccessorFuncReturnType(const FieldDef &field, - const std::string &lifetime) { + const std::string &lifetime, + bool unchecked) { const Type &type = field.value.type; switch (GetFullType(field.value.type)) { @@ -927,11 +977,19 @@ class RustGenerator : public BaseGenerator { return WrapInOptionIfNotRequired(typname + "<" + lifetime + ">", field.required); } - case ftEnumKey: case ftUnionKey: { const auto typname = WrapInNameSpace(*type.enum_def); return field.optional ? "Option<" + typname + ">" : typname; } + case ftEnumKey: { + auto typname = WrapInNameSpace(*type.enum_def); + if (!unchecked) { + const auto underlying_typname = GetEnumTypeForDecl(type.enum_def->underlying_type); + typname = "Result<" + typname + ", flatbuffers::ConvertError<" + underlying_typname + ">>"; + } + + return field.optional ? "Option<" + typname + ">" : typname; + } case ftUnionValue: { return WrapInOptionIfNotRequired("flatbuffers::Table<" + lifetime + ">", @@ -987,6 +1045,41 @@ class RustGenerator : public BaseGenerator { return "INVALID_CODE_GENERATION"; // for return analysis } + std::string GenTableUncheckedAccessor(const FieldDef &field, + const std::string &offset_prefix) { + const std::string offset_name = + offset_prefix + "::" + GetFieldOffsetName(field); + const Type &type = field.value.type; + + if (GetFullType(type) != ftEnumKey) { + FLATBUFFERS_ASSERT(false && "unchecked access is supported for enums only"); + return "INVALID_CODE_GENERATION"; // for return analysis + } + + const auto underlying_typename = GetEnumTypeForDecl(type.enum_def->underlying_type); + if (field.optional) { + return "self._tab.get::<" + underlying_typename + ">(" + offset_name + ", None)" + + ".map(|value| std::mem::transmute(value))"; + } else { + return "self._tab.get::<" + underlying_typename + ">(" + offset_name + ", Some(" + + field.value.constant + ")).map(|value| std::mem::transmute(value)).unwrap()"; + } + } + + std::string GenRawEnumAccessor(const std::string &underlying_type, + const FieldDef &field, + const std::string &offset_prefix) { + const auto offset_name = + offset_prefix + "::" + GetFieldOffsetName(field); + + if (field.optional) { + return "self._tab.get::<" + underlying_type + ">(" + offset_name + ", None)"; + } + + return "self._tab.get::<" + underlying_type + ">(" + offset_name + ", Some(" + + field.value.constant + ")).unwrap()"; + } + std::string GenTableAccessorFuncBody(const FieldDef &field, const std::string &lifetime, const std::string &offset_prefix) { @@ -994,7 +1087,7 @@ class RustGenerator : public BaseGenerator { offset_prefix + "::" + GetFieldOffsetName(field); const Type &type = field.value.type; - switch (GetFullType(field.value.type)) { + switch (GetFullType(type)) { case ftInteger: case ftFloat: case ftBool: { @@ -1027,16 +1120,20 @@ class RustGenerator : public BaseGenerator { lifetime + ">>>(" + offset_name + ", None)", field.required); } - case ftUnionKey: + case ftUnionKey: { + return GenerateEnumOrUnionAccess(field, offset_name, type); + } case ftEnumKey: { - const auto underlying_typname = GetTypeBasic(type); //<- never used - const auto typname = WrapInNameSpace(*type.enum_def); - const auto default_value = GetDefaultScalarValue(field); + if (IsBitFlags(*type.enum_def)) { + return GenerateEnumOrUnionAccess(field, offset_name, type); + } + + const auto underlying_typname = GetEnumTypeForDecl(type.enum_def->underlying_type); if (field.optional) { - return "self._tab.get::<" + typname + ">(" + offset_name + ", None)"; + return "self._tab.get::<" + underlying_typname + ">(" + offset_name + ", None).map(|value| value.try_into())"; } else { - return "self._tab.get::<" + typname + ">(" + offset_name + ", Some(" + - default_value + ")).unwrap()"; + return "self._tab.get::<" + underlying_typname + ">(" + offset_name + ", Some(" + + field.value.constant + ")).map(|value| value.try_into()).unwrap()"; } } case ftString: { @@ -1100,6 +1197,20 @@ class RustGenerator : public BaseGenerator { return "INVALID_CODE_GENERATION"; // for return analysis } + std::string GenerateEnumOrUnionAccess(const FieldDef &field, + const std::string &offset_name, + const Type &type) { + const auto underlying_typname = GetTypeBasic(type); //<- never used + const auto typname = WrapInNameSpace(*type.enum_def); + const auto 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()"; + } + } + bool TableFieldReturnsOption(const FieldDef &field) { if (field.optional) return true; switch (GetFullType(field.value.type)) { @@ -1241,17 +1352,57 @@ class RustGenerator : public BaseGenerator { continue; } - code_.SetValue("FIELD_NAME", Name(field)); - code_.SetValue("RETURN_TYPE", - GenTableAccessorFuncReturnType(field, "'a")); - code_.SetValue("FUNC_BODY", - GenTableAccessorFuncBody(field, "'a", offset_prefix)); + if (GetFullType(field.value.type) == ftEnumKey && !IsBitFlags(*field.value.type.enum_def)) { + code_.SetValue("FIELD_NAME", Name(field)); + code_.SetValue("RETURN_TYPE", + GenTableAccessorFuncReturnType(field, "'a", false)); + code_.SetValue("FUNC_BODY", + GenTableAccessorFuncBody(field, "'a", offset_prefix)); - GenComment(field.doc_comment, " "); - code_ += " #[inline]"; - code_ += " pub fn {{FIELD_NAME}}(&self) -> {{RETURN_TYPE}} {"; - code_ += " {{FUNC_BODY}}"; - code_ += " }"; + GenComment(field.doc_comment, " "); + code_ += " #[inline]"; + code_ += " pub fn {{FIELD_NAME}}(&self) -> {{RETURN_TYPE}} {"; + code_ += " {{FUNC_BODY}}"; + code_ += " }"; + + code_.SetValue("RETURN_TYPE", + GenTableAccessorFuncReturnType(field, "'a", true)); + code_.SetValue("FUNC_BODY", + GenTableUncheckedAccessor(field, offset_prefix)); + + GenComment(field.doc_comment, " "); + code_ += " #[inline]"; + code_ += " pub unsafe fn {{FIELD_NAME}}_unchecked(&self) -> {{RETURN_TYPE}} {"; + code_ += " {{FUNC_BODY}}"; + code_ += " }"; + + const auto underlying_type = GetEnumTypeForDecl(field.value.type.enum_def->underlying_type); + + code_.SetValue("RETURN_TYPE", WrapInOptionIfNotRequired(underlying_type, !field.optional)); + code_.SetValue("FUNC_BODY", GenRawEnumAccessor(underlying_type, field, offset_prefix)); + + GenComment(field.doc_comment, " "); + code_ += " #[inline]"; + code_ += " pub fn {{FIELD_NAME}}_raw(&self) -> {{RETURN_TYPE}} {"; + code_ += " {{FUNC_BODY}}"; + code_ += " }"; + } else { + const auto bit_flags = GetFullType(field.value.type) == ftEnumKey && IsBitFlags(*field.value.type.enum_def); + + code_.SetValue("FIELD_NAME", Name(field)); + + // treat bitflags as unchecked for now + code_.SetValue("RETURN_TYPE", + GenTableAccessorFuncReturnType(field, "'a", bit_flags)); + code_.SetValue("FUNC_BODY", + GenTableAccessorFuncBody(field, "'a", offset_prefix)); + + 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); } @@ -1480,7 +1631,7 @@ class RustGenerator : public BaseGenerator { void GenKeyFieldMethods(const FieldDef &field) { FLATBUFFERS_ASSERT(field.key); - code_.SetValue("KEY_TYPE", GenTableAccessorFuncReturnType(field, "")); + code_.SetValue("KEY_TYPE", GenTableAccessorFuncReturnType(field, "", false)); code_ += " #[inline]"; code_ += @@ -1786,6 +1937,12 @@ class RustGenerator : public BaseGenerator { code_ += indent + "use std::mem;"; code_ += indent + "use std::cmp::Ordering;"; + + if (!parser_.enums_.vec.empty()) { + code_ += indent + "use std::convert::TryFrom;"; + code_ += indent + "use std::convert::TryInto;"; + } + code_ += ""; code_ += indent + "extern crate flatbuffers;"; code_ += indent + "use self::flatbuffers::EndianScalar;"; diff --git a/tests/include_test/include_test1_generated.rs b/tests/include_test/include_test1_generated.rs index 3c549e01143..b63cc3b4c17 100644 --- a/tests/include_test/include_test1_generated.rs +++ b/tests/include_test/include_test1_generated.rs @@ -5,6 +5,8 @@ use crate::include_test2_generated::*; use std::mem; use std::cmp::Ordering; +use std::convert::TryFrom; +use std::convert::TryInto; extern crate flatbuffers; use self::flatbuffers::EndianScalar; diff --git a/tests/include_test/sub/include_test2_generated.rs b/tests/include_test/sub/include_test2_generated.rs index a3500c97414..914bd492ac1 100644 --- a/tests/include_test/sub/include_test2_generated.rs +++ b/tests/include_test/sub/include_test2_generated.rs @@ -5,6 +5,8 @@ use crate::include_test1_generated::*; use std::mem; use std::cmp::Ordering; +use std::convert::TryFrom; +use std::convert::TryInto; extern crate flatbuffers; use self::flatbuffers::EndianScalar; @@ -15,6 +17,8 @@ pub mod my_game { use crate::include_test1_generated::*; use std::mem; use std::cmp::Ordering; + use std::convert::TryFrom; + use std::convert::TryInto; extern crate flatbuffers; use self::flatbuffers::EndianScalar; @@ -24,6 +28,8 @@ pub mod other_name_space { use crate::include_test1_generated::*; use std::mem; use std::cmp::Ordering; + use std::convert::TryFrom; + use std::convert::TryInto; extern crate flatbuffers; use self::flatbuffers::EndianScalar; @@ -36,9 +42,16 @@ pub enum FromInclude { } +#[deprecated(since = "1.13", note = "Use associated constants instead.")] pub const ENUM_MIN_FROM_INCLUDE: i64 = 0; +#[deprecated(since = "1.13", note = "Use associated constants instead.")] pub const ENUM_MAX_FROM_INCLUDE: i64 = 0; +impl FromInclude { + pub const MIN: i64 = 0; + pub const MAX: i64 = 0; +} + impl<'a> flatbuffers::Follow<'a> for FromInclude { type Inner = Self; #[inline] @@ -70,19 +83,39 @@ impl flatbuffers::Push for FromInclude { } } +impl TryFrom for FromInclude { + type Error = flatbuffers::ConvertError; + + #[inline] + fn try_from(value: i64) -> Result { + match value { + 0 => Ok(FromInclude::IncludeVal), + _ => Err(Self::Error::UnknownVariant(value)) + } + } +} + #[allow(non_camel_case_types)] +#[deprecated(since = "1.13", note = "Use associated constants instead.")] pub const ENUM_VALUES_FROM_INCLUDE: [FromInclude; 1] = [ FromInclude::IncludeVal ]; +impl FromInclude { + pub const NAMES: [&'static str; 1] = [ + "IncludeVal" + ]; +} + #[allow(non_camel_case_types)] +#[deprecated(since = "1.13", note = "Use associated constants instead.")] pub const ENUM_NAMES_FROM_INCLUDE: [&str; 1] = [ "IncludeVal" ]; pub fn enum_name_from_include(e: FromInclude) -> &'static str { let index = e as i64; - ENUM_NAMES_FROM_INCLUDE[index as usize] + FromInclude::NAMES[index as usize] } // struct Unused, aligned to 4 diff --git a/tests/monster_test_generated.rs b/tests/monster_test_generated.rs index 4c5231e6939..68c54f70db4 100644 --- a/tests/monster_test_generated.rs +++ b/tests/monster_test_generated.rs @@ -6,6 +6,8 @@ use crate::include_test1_generated::*; use crate::include_test2_generated::*; use std::mem; use std::cmp::Ordering; +use std::convert::TryFrom; +use std::convert::TryInto; extern crate flatbuffers; use self::flatbuffers::EndianScalar; @@ -17,6 +19,8 @@ pub mod my_game { use crate::include_test2_generated::*; use std::mem; use std::cmp::Ordering; + use std::convert::TryFrom; + use std::convert::TryInto; extern crate flatbuffers; use self::flatbuffers::EndianScalar; @@ -93,6 +97,8 @@ pub mod example_2 { use crate::include_test2_generated::*; use std::mem; use std::cmp::Ordering; + use std::convert::TryFrom; + use std::convert::TryInto; extern crate flatbuffers; use self::flatbuffers::EndianScalar; @@ -171,6 +177,8 @@ pub mod example { use crate::include_test2_generated::*; use std::mem; use std::cmp::Ordering; + use std::convert::TryFrom; + use std::convert::TryInto; extern crate flatbuffers; use self::flatbuffers::EndianScalar; @@ -189,9 +197,16 @@ pub enum Color { } +#[deprecated(since = "1.13", note = "Use associated constants instead.")] pub const ENUM_MIN_COLOR: u8 = 1; +#[deprecated(since = "1.13", note = "Use associated constants instead.")] pub const ENUM_MAX_COLOR: u8 = 8; +impl Color { + pub const MIN: u8 = 1; + pub const MAX: u8 = 8; +} + impl<'a> flatbuffers::Follow<'a> for Color { type Inner = Self; #[inline] @@ -224,13 +239,28 @@ impl flatbuffers::Push for Color { } #[allow(non_camel_case_types)] +#[deprecated(since = "1.13", note = "Use associated constants instead.")] pub const ENUM_VALUES_COLOR: [Color; 3] = [ Color::Red, Color::Green, Color::Blue ]; +impl Color { + pub const NAMES: [&'static str; 8] = [ + "Red", + "Green", + "", + "", + "", + "", + "", + "Blue" + ]; +} + #[allow(non_camel_case_types)] +#[deprecated(since = "1.13", note = "Use associated constants instead.")] pub const ENUM_NAMES_COLOR: [&str; 8] = [ "Red", "Green", @@ -244,7 +274,7 @@ pub const ENUM_NAMES_COLOR: [&str; 8] = [ pub fn enum_name_color(e: Color) -> &'static str { let index = e as u8 - Color::Red as u8; - ENUM_NAMES_COLOR[index as usize] + Color::NAMES[index as usize] } #[allow(non_camel_case_types)] @@ -258,9 +288,16 @@ pub enum Race { } +#[deprecated(since = "1.13", note = "Use associated constants instead.")] pub const ENUM_MIN_RACE: i8 = -1; +#[deprecated(since = "1.13", note = "Use associated constants instead.")] pub const ENUM_MAX_RACE: i8 = 2; +impl Race { + pub const MIN: i8 = -1; + pub const MAX: i8 = 2; +} + impl<'a> flatbuffers::Follow<'a> for Race { type Inner = Self; #[inline] @@ -292,7 +329,23 @@ impl flatbuffers::Push for Race { } } +impl TryFrom for Race { + type Error = flatbuffers::ConvertError; + + #[inline] + fn try_from(value: i8) -> Result { + match value { + -1 => Ok(Race::None), + 0 => Ok(Race::Human), + 1 => Ok(Race::Dwarf), + 2 => Ok(Race::Elf), + _ => Err(Self::Error::UnknownVariant(value)) + } + } +} + #[allow(non_camel_case_types)] +#[deprecated(since = "1.13", note = "Use associated constants instead.")] pub const ENUM_VALUES_RACE: [Race; 4] = [ Race::None, Race::Human, @@ -300,7 +353,17 @@ pub const ENUM_VALUES_RACE: [Race; 4] = [ Race::Elf ]; +impl Race { + pub const NAMES: [&'static str; 4] = [ + "None", + "Human", + "Dwarf", + "Elf" + ]; +} + #[allow(non_camel_case_types)] +#[deprecated(since = "1.13", note = "Use associated constants instead.")] pub const ENUM_NAMES_RACE: [&str; 4] = [ "None", "Human", @@ -310,7 +373,7 @@ pub const ENUM_NAMES_RACE: [&str; 4] = [ pub fn enum_name_race(e: Race) -> &'static str { let index = e as i8 - Race::None as i8; - ENUM_NAMES_RACE[index as usize] + Race::NAMES[index as usize] } #[allow(non_camel_case_types)] @@ -324,9 +387,16 @@ pub enum Any { } +#[deprecated(since = "1.13", note = "Use associated constants instead.")] pub const ENUM_MIN_ANY: u8 = 0; +#[deprecated(since = "1.13", note = "Use associated constants instead.")] pub const ENUM_MAX_ANY: u8 = 3; +impl Any { + pub const MIN: u8 = 0; + pub const MAX: u8 = 3; +} + impl<'a> flatbuffers::Follow<'a> for Any { type Inner = Self; #[inline] @@ -358,7 +428,23 @@ impl flatbuffers::Push for Any { } } +impl TryFrom for Any { + type Error = flatbuffers::ConvertError; + + #[inline] + fn try_from(value: u8) -> Result { + match value { + 0 => Ok(Any::NONE), + 1 => Ok(Any::Monster), + 2 => Ok(Any::TestSimpleTableWithEnum), + 3 => Ok(Any::MyGame_Example2_Monster), + _ => Err(Self::Error::UnknownVariant(value)) + } + } +} + #[allow(non_camel_case_types)] +#[deprecated(since = "1.13", note = "Use associated constants instead.")] pub const ENUM_VALUES_ANY: [Any; 4] = [ Any::NONE, Any::Monster, @@ -366,7 +452,17 @@ pub const ENUM_VALUES_ANY: [Any; 4] = [ Any::MyGame_Example2_Monster ]; +impl Any { + pub const NAMES: [&'static str; 4] = [ + "NONE", + "Monster", + "TestSimpleTableWithEnum", + "MyGame_Example2_Monster" + ]; +} + #[allow(non_camel_case_types)] +#[deprecated(since = "1.13", note = "Use associated constants instead.")] pub const ENUM_NAMES_ANY: [&str; 4] = [ "NONE", "Monster", @@ -376,7 +472,7 @@ pub const ENUM_NAMES_ANY: [&str; 4] = [ pub fn enum_name_any(e: Any) -> &'static str { let index = e as u8; - ENUM_NAMES_ANY[index as usize] + Any::NAMES[index as usize] } pub struct AnyUnionTableOffset {} @@ -391,9 +487,16 @@ pub enum AnyUniqueAliases { } +#[deprecated(since = "1.13", note = "Use associated constants instead.")] pub const ENUM_MIN_ANY_UNIQUE_ALIASES: u8 = 0; +#[deprecated(since = "1.13", note = "Use associated constants instead.")] pub const ENUM_MAX_ANY_UNIQUE_ALIASES: u8 = 3; +impl AnyUniqueAliases { + pub const MIN: u8 = 0; + pub const MAX: u8 = 3; +} + impl<'a> flatbuffers::Follow<'a> for AnyUniqueAliases { type Inner = Self; #[inline] @@ -425,7 +528,23 @@ impl flatbuffers::Push for AnyUniqueAliases { } } +impl TryFrom for AnyUniqueAliases { + type Error = flatbuffers::ConvertError; + + #[inline] + fn try_from(value: u8) -> Result { + match value { + 0 => Ok(AnyUniqueAliases::NONE), + 1 => Ok(AnyUniqueAliases::M), + 2 => Ok(AnyUniqueAliases::TS), + 3 => Ok(AnyUniqueAliases::M2), + _ => Err(Self::Error::UnknownVariant(value)) + } + } +} + #[allow(non_camel_case_types)] +#[deprecated(since = "1.13", note = "Use associated constants instead.")] pub const ENUM_VALUES_ANY_UNIQUE_ALIASES: [AnyUniqueAliases; 4] = [ AnyUniqueAliases::NONE, AnyUniqueAliases::M, @@ -433,7 +552,17 @@ pub const ENUM_VALUES_ANY_UNIQUE_ALIASES: [AnyUniqueAliases; 4] = [ AnyUniqueAliases::M2 ]; +impl AnyUniqueAliases { + pub const NAMES: [&'static str; 4] = [ + "NONE", + "M", + "TS", + "M2" + ]; +} + #[allow(non_camel_case_types)] +#[deprecated(since = "1.13", note = "Use associated constants instead.")] pub const ENUM_NAMES_ANY_UNIQUE_ALIASES: [&str; 4] = [ "NONE", "M", @@ -443,7 +572,7 @@ pub const ENUM_NAMES_ANY_UNIQUE_ALIASES: [&str; 4] = [ pub fn enum_name_any_unique_aliases(e: AnyUniqueAliases) -> &'static str { let index = e as u8; - ENUM_NAMES_ANY_UNIQUE_ALIASES[index as usize] + AnyUniqueAliases::NAMES[index as usize] } pub struct AnyUniqueAliasesUnionTableOffset {} @@ -458,9 +587,16 @@ pub enum AnyAmbiguousAliases { } +#[deprecated(since = "1.13", note = "Use associated constants instead.")] pub const ENUM_MIN_ANY_AMBIGUOUS_ALIASES: u8 = 0; +#[deprecated(since = "1.13", note = "Use associated constants instead.")] pub const ENUM_MAX_ANY_AMBIGUOUS_ALIASES: u8 = 3; +impl AnyAmbiguousAliases { + pub const MIN: u8 = 0; + pub const MAX: u8 = 3; +} + impl<'a> flatbuffers::Follow<'a> for AnyAmbiguousAliases { type Inner = Self; #[inline] @@ -492,7 +628,23 @@ impl flatbuffers::Push for AnyAmbiguousAliases { } } +impl TryFrom for AnyAmbiguousAliases { + type Error = flatbuffers::ConvertError; + + #[inline] + fn try_from(value: u8) -> Result { + match value { + 0 => Ok(AnyAmbiguousAliases::NONE), + 1 => Ok(AnyAmbiguousAliases::M1), + 2 => Ok(AnyAmbiguousAliases::M2), + 3 => Ok(AnyAmbiguousAliases::M3), + _ => Err(Self::Error::UnknownVariant(value)) + } + } +} + #[allow(non_camel_case_types)] +#[deprecated(since = "1.13", note = "Use associated constants instead.")] pub const ENUM_VALUES_ANY_AMBIGUOUS_ALIASES: [AnyAmbiguousAliases; 4] = [ AnyAmbiguousAliases::NONE, AnyAmbiguousAliases::M1, @@ -500,7 +652,17 @@ pub const ENUM_VALUES_ANY_AMBIGUOUS_ALIASES: [AnyAmbiguousAliases; 4] = [ AnyAmbiguousAliases::M3 ]; +impl AnyAmbiguousAliases { + pub const NAMES: [&'static str; 4] = [ + "NONE", + "M1", + "M2", + "M3" + ]; +} + #[allow(non_camel_case_types)] +#[deprecated(since = "1.13", note = "Use associated constants instead.")] pub const ENUM_NAMES_ANY_AMBIGUOUS_ALIASES: [&str; 4] = [ "NONE", "M1", @@ -510,7 +672,7 @@ pub const ENUM_NAMES_ANY_AMBIGUOUS_ALIASES: [&str; 4] = [ pub fn enum_name_any_ambiguous_aliases(e: AnyAmbiguousAliases) -> &'static str { let index = e as u8; - ENUM_NAMES_ANY_AMBIGUOUS_ALIASES[index as usize] + AnyAmbiguousAliases::NAMES[index as usize] } pub struct AnyAmbiguousAliasesUnionTableOffset {} @@ -1357,8 +1519,16 @@ impl<'a> Monster<'a> { self._tab.get::>>(Monster::VT_VECTOR_OF_ENUMS, None) } #[inline] - pub fn signed_enum(&self) -> Race { - self._tab.get::(Monster::VT_SIGNED_ENUM, Some(Race::None)).unwrap() + pub fn signed_enum(&self) -> Result> { + self._tab.get::(Monster::VT_SIGNED_ENUM, Some(-1)).map(|value| value.try_into()).unwrap() + } + #[inline] + pub unsafe fn signed_enum_unchecked(&self) -> Race { + self._tab.get::(Monster::VT_SIGNED_ENUM, Some(-1)).map(|value| std::mem::transmute(value)).unwrap() + } + #[inline] + pub fn signed_enum_raw(&self) -> i8 { + self._tab.get::(Monster::VT_SIGNED_ENUM, Some(-1)).unwrap() } #[inline] #[allow(non_snake_case)] diff --git a/tests/namespace_test/namespace_test1_generated.rs b/tests/namespace_test/namespace_test1_generated.rs index 77e5d9b5ef3..6f423d49871 100644 --- a/tests/namespace_test/namespace_test1_generated.rs +++ b/tests/namespace_test/namespace_test1_generated.rs @@ -4,6 +4,8 @@ use std::mem; use std::cmp::Ordering; +use std::convert::TryFrom; +use std::convert::TryInto; extern crate flatbuffers; use self::flatbuffers::EndianScalar; @@ -13,6 +15,8 @@ pub mod namespace_a { use std::mem; use std::cmp::Ordering; + use std::convert::TryFrom; + use std::convert::TryInto; extern crate flatbuffers; use self::flatbuffers::EndianScalar; @@ -21,6 +25,8 @@ pub mod namespace_b { use std::mem; use std::cmp::Ordering; + use std::convert::TryFrom; + use std::convert::TryInto; extern crate flatbuffers; use self::flatbuffers::EndianScalar; @@ -35,9 +41,16 @@ pub enum EnumInNestedNS { } +#[deprecated(since = "1.13", note = "Use associated constants instead.")] pub const ENUM_MIN_ENUM_IN_NESTED_NS: i8 = 0; +#[deprecated(since = "1.13", note = "Use associated constants instead.")] pub const ENUM_MAX_ENUM_IN_NESTED_NS: i8 = 2; +impl EnumInNestedNS { + pub const MIN: i8 = 0; + pub const MAX: i8 = 2; +} + impl<'a> flatbuffers::Follow<'a> for EnumInNestedNS { type Inner = Self; #[inline] @@ -69,14 +82,38 @@ impl flatbuffers::Push for EnumInNestedNS { } } +impl TryFrom for EnumInNestedNS { + type Error = flatbuffers::ConvertError; + + #[inline] + fn try_from(value: i8) -> Result { + match value { + 0 => Ok(EnumInNestedNS::A), + 1 => Ok(EnumInNestedNS::B), + 2 => Ok(EnumInNestedNS::C), + _ => Err(Self::Error::UnknownVariant(value)) + } + } +} + #[allow(non_camel_case_types)] +#[deprecated(since = "1.13", note = "Use associated constants instead.")] pub const ENUM_VALUES_ENUM_IN_NESTED_NS: [EnumInNestedNS; 3] = [ EnumInNestedNS::A, EnumInNestedNS::B, EnumInNestedNS::C ]; +impl EnumInNestedNS { + pub const NAMES: [&'static str; 3] = [ + "A", + "B", + "C" + ]; +} + #[allow(non_camel_case_types)] +#[deprecated(since = "1.13", note = "Use associated constants instead.")] pub const ENUM_NAMES_ENUM_IN_NESTED_NS: [&str; 3] = [ "A", "B", @@ -85,7 +122,7 @@ pub const ENUM_NAMES_ENUM_IN_NESTED_NS: [&str; 3] = [ pub fn enum_name_enum_in_nested_ns(e: EnumInNestedNS) -> &'static str { let index = e as i8; - ENUM_NAMES_ENUM_IN_NESTED_NS[index as usize] + EnumInNestedNS::NAMES[index as usize] } // struct StructInNestedNS, aligned to 4 diff --git a/tests/namespace_test/namespace_test2_generated.rs b/tests/namespace_test/namespace_test2_generated.rs index dfdd8c51fff..cd224c28136 100644 --- a/tests/namespace_test/namespace_test2_generated.rs +++ b/tests/namespace_test/namespace_test2_generated.rs @@ -5,6 +5,8 @@ use crate::namespace_test1_generated::*; use std::mem; use std::cmp::Ordering; +use std::convert::TryFrom; +use std::convert::TryInto; extern crate flatbuffers; use self::flatbuffers::EndianScalar; @@ -15,6 +17,8 @@ pub mod namespace_a { use crate::namespace_test1_generated::*; use std::mem; use std::cmp::Ordering; + use std::convert::TryFrom; + use std::convert::TryInto; extern crate flatbuffers; use self::flatbuffers::EndianScalar; @@ -65,8 +69,16 @@ impl<'a> TableInFirstNS<'a> { self._tab.get::>>(TableInFirstNS::VT_FOO_TABLE, None) } #[inline] - pub fn foo_enum(&self) -> namespace_b::EnumInNestedNS { - self._tab.get::(TableInFirstNS::VT_FOO_ENUM, Some(namespace_b::EnumInNestedNS::A)).unwrap() + pub fn foo_enum(&self) -> Result> { + self._tab.get::(TableInFirstNS::VT_FOO_ENUM, Some(0)).map(|value| value.try_into()).unwrap() + } + #[inline] + pub unsafe fn foo_enum_unchecked(&self) -> namespace_b::EnumInNestedNS { + self._tab.get::(TableInFirstNS::VT_FOO_ENUM, Some(0)).map(|value| std::mem::transmute(value)).unwrap() + } + #[inline] + pub fn foo_enum_raw(&self) -> i8 { + self._tab.get::(TableInFirstNS::VT_FOO_ENUM, Some(0)).unwrap() } #[inline] pub fn foo_struct(&self) -> Option<&'a namespace_b::StructInNestedNS> { @@ -207,6 +219,8 @@ pub mod namespace_c { use crate::namespace_test1_generated::*; use std::mem; use std::cmp::Ordering; + use std::convert::TryFrom; + use std::convert::TryInto; extern crate flatbuffers; use self::flatbuffers::EndianScalar; diff --git a/tests/optional_scalars2_generated.rs b/tests/optional_scalars2_generated.rs index 8e10527f56a..285e80fb80b 100644 --- a/tests/optional_scalars2_generated.rs +++ b/tests/optional_scalars2_generated.rs @@ -4,6 +4,8 @@ use std::mem; use std::cmp::Ordering; +use std::convert::TryFrom; +use std::convert::TryInto; extern crate flatbuffers; use self::flatbuffers::EndianScalar; @@ -13,6 +15,8 @@ pub mod optional_scalars { use std::mem; use std::cmp::Ordering; + use std::convert::TryFrom; + use std::convert::TryInto; extern crate flatbuffers; use self::flatbuffers::EndianScalar; @@ -27,9 +31,16 @@ pub enum OptionalByte { } +#[deprecated(since = "1.13", note = "Use associated constants instead.")] pub const ENUM_MIN_OPTIONAL_BYTE: i8 = 0; +#[deprecated(since = "1.13", note = "Use associated constants instead.")] pub const ENUM_MAX_OPTIONAL_BYTE: i8 = 2; +impl OptionalByte { + pub const MIN: i8 = 0; + pub const MAX: i8 = 2; +} + impl<'a> flatbuffers::Follow<'a> for OptionalByte { type Inner = Self; #[inline] @@ -61,14 +72,38 @@ impl flatbuffers::Push for OptionalByte { } } +impl TryFrom for OptionalByte { + type Error = flatbuffers::ConvertError; + + #[inline] + fn try_from(value: i8) -> Result { + match value { + 0 => Ok(OptionalByte::None), + 1 => Ok(OptionalByte::One), + 2 => Ok(OptionalByte::Two), + _ => Err(Self::Error::UnknownVariant(value)) + } + } +} + #[allow(non_camel_case_types)] +#[deprecated(since = "1.13", note = "Use associated constants instead.")] pub const ENUM_VALUES_OPTIONAL_BYTE: [OptionalByte; 3] = [ OptionalByte::None, OptionalByte::One, OptionalByte::Two ]; +impl OptionalByte { + pub const NAMES: [&'static str; 3] = [ + "None", + "One", + "Two" + ]; +} + #[allow(non_camel_case_types)] +#[deprecated(since = "1.13", note = "Use associated constants instead.")] pub const ENUM_NAMES_OPTIONAL_BYTE: [&str; 3] = [ "None", "One", @@ -77,7 +112,7 @@ pub const ENUM_NAMES_OPTIONAL_BYTE: [&str; 3] = [ pub fn enum_name_optional_byte(e: OptionalByte) -> &'static str { let index = e as i8; - ENUM_NAMES_OPTIONAL_BYTE[index as usize] + OptionalByte::NAMES[index as usize] } pub enum ScalarStuffOffset {} @@ -316,16 +351,40 @@ impl<'a> ScalarStuff<'a> { self._tab.get::(ScalarStuff::VT_DEFAULT_BOOL, Some(true)).unwrap() } #[inline] - pub fn just_enum(&self) -> OptionalByte { - self._tab.get::(ScalarStuff::VT_JUST_ENUM, Some(OptionalByte::None)).unwrap() + pub fn just_enum(&self) -> Result> { + self._tab.get::(ScalarStuff::VT_JUST_ENUM, Some(0)).map(|value| value.try_into()).unwrap() + } + #[inline] + pub unsafe fn just_enum_unchecked(&self) -> OptionalByte { + self._tab.get::(ScalarStuff::VT_JUST_ENUM, Some(0)).map(|value| std::mem::transmute(value)).unwrap() + } + #[inline] + pub fn just_enum_raw(&self) -> i8 { + self._tab.get::(ScalarStuff::VT_JUST_ENUM, Some(0)).unwrap() + } + #[inline] + pub fn maybe_enum(&self) -> Option>> { + self._tab.get::(ScalarStuff::VT_MAYBE_ENUM, None).map(|value| value.try_into()) + } + #[inline] + pub unsafe fn maybe_enum_unchecked(&self) -> Option { + self._tab.get::(ScalarStuff::VT_MAYBE_ENUM, None).map(|value| std::mem::transmute(value)) + } + #[inline] + pub fn maybe_enum_raw(&self) -> Option { + self._tab.get::(ScalarStuff::VT_MAYBE_ENUM, None) + } + #[inline] + pub fn default_enum(&self) -> Result> { + self._tab.get::(ScalarStuff::VT_DEFAULT_ENUM, Some(1)).map(|value| value.try_into()).unwrap() } #[inline] - pub fn maybe_enum(&self) -> Option { - self._tab.get::(ScalarStuff::VT_MAYBE_ENUM, None) + pub unsafe fn default_enum_unchecked(&self) -> OptionalByte { + self._tab.get::(ScalarStuff::VT_DEFAULT_ENUM, Some(1)).map(|value| std::mem::transmute(value)).unwrap() } #[inline] - pub fn default_enum(&self) -> OptionalByte { - self._tab.get::(ScalarStuff::VT_DEFAULT_ENUM, Some(OptionalByte::One)).unwrap() + pub fn default_enum_raw(&self) -> i8 { + self._tab.get::(ScalarStuff::VT_DEFAULT_ENUM, Some(1)).unwrap() } } diff --git a/tests/rust_usage_test/tests/optional_scalars_test.rs b/tests/rust_usage_test/tests/optional_scalars_test.rs index 34a94dbcf67..25feb21f8a9 100644 --- a/tests/rust_usage_test/tests/optional_scalars_test.rs +++ b/tests/rust_usage_test/tests/optional_scalars_test.rs @@ -41,6 +41,41 @@ macro_rules! make_test { }; } +macro_rules! make_enum_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::(builder.finished_data()); + assert_eq!(s.$just(), Ok($five)); + assert_eq!(s.$default(), Ok($five)); + assert_eq!(s.$maybe(), Some(Ok($five))); + + // Test defaults are used when not specified. + let s = flatbuffers::get_root::(&[0; 8]); + assert_eq!(s.$just(), Ok($zero)); + assert_eq!(s.$default(), Ok($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); @@ -76,7 +111,7 @@ make_test!( false, true ); -make_test!( +make_enum_test!( optional_enum, just_enum, default_enum,