diff options
author | David Reiss <davidn@gmail.com> | 2019-05-17 12:41:39 -0700 |
---|---|---|
committer | Robert <rw@users.noreply.github.com> | 2019-05-17 12:41:39 -0700 |
commit | 718ddea558b3efb0544fc56bea07ed3da8884125 (patch) | |
tree | c0c0d47ad645c7dea72da3799205e7954ff06053 | |
parent | 8d86b5347fbeba85f11b7bf479334f6700feb455 (diff) | |
download | flatbuffers-718ddea558b3efb0544fc56bea07ed3da8884125.tar.gz flatbuffers-718ddea558b3efb0544fc56bea07ed3da8884125.tar.bz2 flatbuffers-718ddea558b3efb0544fc56bea07ed3da8884125.zip |
[Go] Make enums into real types, add String() (#5235)
* [Go] Make enums into real types, add String()
This changes the generated code for enums: instead of type aliases,
they're now distinct types, allowing for better type-checking. Some
client code may have to be changed to add casts.
Enum types now have a String() method, so they implement fmt.Stringer.
An EnumValues map is now generated, in addition to the existing
EnumNames map, to easily map strings to values.
Generated enum files are now gofmt-clean.
Fixes #5207
* use example.ColorGreen explicitly
* use valid enum value in mutation test, add new test for "invalid" enum
* add length check and comment
-rw-r--r-- | src/idl_gen_go.cpp | 165 | ||||
-rw-r--r-- | tests/MyGame/Example/Any.go | 30 | ||||
-rw-r--r-- | tests/MyGame/Example/AnyAmbiguousAliases.go | 32 | ||||
-rw-r--r-- | tests/MyGame/Example/AnyUniqueAliases.go | 32 | ||||
-rw-r--r-- | tests/MyGame/Example/Color.go | 27 | ||||
-rw-r--r-- | tests/MyGame/Example/Monster.go | 32 | ||||
-rw-r--r-- | tests/MyGame/Example/TestSimpleTableWithEnum.go | 4 | ||||
-rw-r--r-- | tests/MyGame/Example/Vec3.go | 4 | ||||
-rw-r--r-- | tests/go_test.go | 69 | ||||
-rw-r--r-- | tests/namespace_test/NamespaceA/NamespaceB/EnumInNestedNS.go | 23 | ||||
-rw-r--r-- | tests/namespace_test/NamespaceA/TableInFirstNS.go | 4 |
11 files changed, 328 insertions, 94 deletions
diff --git a/src/idl_gen_go.cpp b/src/idl_gen_go.cpp index 8efbdc8d..7987d90a 100644 --- a/src/idl_gen_go.cpp +++ b/src/idl_gen_go.cpp @@ -83,7 +83,7 @@ class GoGenerator : public BaseGenerator { if (parser_.opts.one_file) { one_file_code += enumcode; } else { - if (!SaveType(**it, enumcode, false)) return false; + if (!SaveType(**it, enumcode, false, true)) return false; } } @@ -95,13 +95,14 @@ class GoGenerator : public BaseGenerator { if (parser_.opts.one_file) { one_file_code += declcode; } else { - if (!SaveType(**it, declcode, true)) return false; + if (!SaveType(**it, declcode, true, false)) return false; } } if (parser_.opts.one_file) { std::string code = ""; - BeginFile(LastNamespacePart(go_namespace_), true, &code); + const bool is_enum = !parser_.enums_.vec.empty(); + BeginFile(LastNamespacePart(go_namespace_), true, is_enum, &code); code += one_file_code; const std::string filename = GeneratedFileName(path_, file_name_); return SaveFile(filename.c_str(), code, false); @@ -140,7 +141,7 @@ class GoGenerator : public BaseGenerator { code += "\n}\n\n"; } - // Construct the name of the type alias for this enum. + // Construct the name of the type for this enum. std::string GetEnumTypeName(const EnumDef &enum_def) { return WrapInNameSpaceAndTrack(cur_name_space_, GoIdentity(enum_def.name)); } @@ -148,8 +149,8 @@ class GoGenerator : public BaseGenerator { // Create a type for the enum values. void GenEnumType(const EnumDef &enum_def, std::string *code_ptr) { std::string &code = *code_ptr; - code += "type " + GetEnumTypeName(enum_def) + " = "; - code += GenTypeBasic(enum_def.underlying_type) + "\n"; + code += "type " + GetEnumTypeName(enum_def) + " "; + code += GenTypeBasic(enum_def.underlying_type) + "\n\n"; } // Begin enum code with a class declaration. @@ -160,12 +161,13 @@ class GoGenerator : public BaseGenerator { // A single enum member. void EnumMember(const EnumDef &enum_def, const EnumVal &ev, - std::string *code_ptr) { + const int max_name_length, std::string *code_ptr) { std::string &code = *code_ptr; code += "\t"; code += enum_def.name; code += ev.name; code += " "; + code += std::string(max_name_length - ev.name.length(), ' '); code += GetEnumTypeName(enum_def); code += " = "; code += enum_def.ToString(ev) + "\n"; @@ -177,7 +179,7 @@ class GoGenerator : public BaseGenerator { code += ")\n\n"; } - // Begin enum name code. + // Begin enum name map. void BeginEnumNames(const EnumDef &enum_def, std::string *code_ptr) { std::string &code = *code_ptr; code += "var EnumNames"; @@ -187,22 +189,63 @@ class GoGenerator : public BaseGenerator { // A single enum name member. void EnumNameMember(const EnumDef &enum_def, const EnumVal ev, - std::string *code_ptr) { + const int max_name_length, std::string *code_ptr) { std::string &code = *code_ptr; code += "\t"; code += enum_def.name; code += ev.name; - code += ":\""; + code += ": "; + code += std::string(max_name_length - ev.name.length(), ' '); + code += "\""; code += ev.name; code += "\",\n"; } - // End enum name code. + // End enum name map. void EndEnumNames(std::string *code_ptr) { std::string &code = *code_ptr; code += "}\n\n"; } + // Generate String() method on enum type. + void EnumStringer(const EnumDef &enum_def, std::string *code_ptr) { + std::string &code = *code_ptr; + code += "func (v " + enum_def.name + ") String() string {\n"; + code += "\tif s, ok := EnumNames" + enum_def.name + "[v]; ok {\n"; + code += "\t\treturn s\n"; + code += "\t}\n"; + code += "\treturn \""+ enum_def.name; + code += "(\" + strconv.FormatInt(int64(v), 10) + \")\"\n"; + code += "}\n\n"; + } + + // Begin enum value map. + void BeginEnumValues(const EnumDef &enum_def, std::string *code_ptr) { + std::string &code = *code_ptr; + code += "var EnumValues"; + code += enum_def.name; + code += " = map[string]" + GetEnumTypeName(enum_def) + "{\n"; + } + + // A single enum value member. + void EnumValueMember(const EnumDef &enum_def, const EnumVal ev, + const int max_name_length, std::string *code_ptr) { + std::string &code = *code_ptr; + code += "\t\""; + code += ev.name; + code += "\": "; + code += std::string(max_name_length - ev.name.length(), ' '); + code += enum_def.name; + code += ev.name; + code += ",\n"; + } + + // End enum value map. + void EndEnumValues(std::string *code_ptr) { + std::string &code = *code_ptr; + code += "}\n\n"; + } + // Initialize a new struct or table from existing data. void NewRootTypeFromBuffer(const StructDef &struct_def, std::string *code_ptr) { @@ -281,9 +324,11 @@ class GoGenerator : public BaseGenerator { GenReceiver(struct_def, code_ptr); code += " " + MakeCamel(field.name); code += "() " + TypeName(field) + " {\n"; - code += "\treturn " + getter; - code += "(rcv._tab.Pos + flatbuffers.UOffsetT("; - code += NumToString(field.value.offset) + "))\n}\n"; + code += "\treturn " + CastToEnum( + field.value.type, + getter + "(rcv._tab.Pos + flatbuffers.UOffsetT(" + + NumToString(field.value.offset) + "))"); + code += "\n}\n"; } // Get the value of a table's scalar. @@ -295,8 +340,9 @@ class GoGenerator : public BaseGenerator { GenReceiver(struct_def, code_ptr); code += " " + MakeCamel(field.name); code += "() " + TypeName(field) + " "; - code += OffsetPrefix(field) + "\t\treturn " + getter; - code += "(o + rcv._tab.Pos)\n\t}\n"; + code += OffsetPrefix(field) + "\t\treturn "; + code += CastToEnum(field.value.type, getter + "(o + rcv._tab.Pos)"); + code += "\n\t}\n"; code += "\treturn " + GenConstant(field) + "\n"; code += "}\n\n"; } @@ -364,7 +410,7 @@ class GoGenerator : public BaseGenerator { std::string &code = *code_ptr; GenReceiver(struct_def, code_ptr); code += " " + MakeCamel(field.name) + "("; - code += "obj " + TypeName(field) + ") bool "; + code += "obj " + GenTypePointer(field.value.type) + ") bool "; code += OffsetPrefix(field); code += "\t\t" + GenGetter(field.value.type); code += "(obj, o)\n\t\treturn true\n\t}\n"; @@ -395,8 +441,7 @@ class GoGenerator : public BaseGenerator { code += "}\n\n"; } - // Get the value of a vector's non-struct member. Uses a named return - // argument to conveniently set the zero value for the result. + // Get the value of a vector's non-struct member. void GetMemberOfVectorOfNonStruct(const StructDef &struct_def, const FieldDef &field, std::string *code_ptr) { @@ -408,10 +453,11 @@ class GoGenerator : public BaseGenerator { code += "(j int) " + TypeName(field) + " "; code += OffsetPrefix(field); code += "\t\ta := rcv._tab.Vector(o)\n"; - code += "\t\treturn " + GenGetter(field.value.type) + "("; - code += "a + flatbuffers.UOffsetT(j*"; - code += NumToString(InlineSize(vectortype)) + "))\n"; - code += "\t}\n"; + code += "\t\treturn " + CastToEnum( + field.value.type, + GenGetter(field.value.type) + "(a + flatbuffers.UOffsetT(j*" + + NumToString(InlineSize(vectortype)) + "))"); + code += "\n\t}\n"; if (vectortype.base_type == BASE_TYPE_STRING) { code += "\treturn nil\n"; } else if (vectortype.base_type == BASE_TYPE_BOOL) { @@ -609,7 +655,8 @@ class GoGenerator : public BaseGenerator { code += " Mutate" + MakeCamel(field.name); code += "(n " + TypeName(field) + ") bool {\n\treturn " + setter; code += "(rcv._tab.Pos+flatbuffers.UOffsetT("; - code += NumToString(field.value.offset) + "), n)\n}\n\n"; + code += NumToString(field.value.offset) + "), "; + code += CastToBaseType(field.value.type, "n") + ")\n}\n\n"; } // Mutate the value of a table's scalar. @@ -622,7 +669,8 @@ class GoGenerator : public BaseGenerator { GenReceiver(struct_def, code_ptr); code += " Mutate" + MakeCamel(field.name); code += "(n " + TypeName(field) + ") bool {\n\treturn "; - code += setter + "(" + NumToString(field.value.offset) + ", n)\n"; + code += setter + "(" + NumToString(field.value.offset) + ", "; + code += CastToBaseType(field.value.type, "n") + ")\n"; code += "}\n\n"; } @@ -641,7 +689,8 @@ class GoGenerator : public BaseGenerator { code += "\t\ta := rcv._tab.Vector(o)\n"; code += "\t\treturn " + setter + "("; code += "a+flatbuffers.UOffsetT(j*"; - code += NumToString(InlineSize(vectortype)) + "), n)\n"; + code += NumToString(InlineSize(vectortype)) + "), "; + code += CastToBaseType(vectortype, "n") + ")\n"; code += "\t}\n"; code += "\treturn false\n"; code += "}\n\n"; @@ -726,6 +775,7 @@ class GoGenerator : public BaseGenerator { void GenEnum(const EnumDef &enum_def, std::string *code_ptr) { if (enum_def.generated) return; + const int max_name_length = MaxNameLength(enum_def); cur_name_space_ = enum_def.defined_namespace; GenComment(enum_def.doc_comment, code_ptr, nullptr); @@ -734,16 +784,26 @@ class GoGenerator : public BaseGenerator { for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); ++it) { auto &ev = **it; GenComment(ev.doc_comment, code_ptr, nullptr, "\t"); - EnumMember(enum_def, ev, code_ptr); + EnumMember(enum_def, ev, max_name_length, code_ptr); } EndEnum(code_ptr); BeginEnumNames(enum_def, code_ptr); for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); ++it) { auto &ev = **it; - EnumNameMember(enum_def, ev, code_ptr); + EnumNameMember(enum_def, ev, max_name_length, code_ptr); } EndEnumNames(code_ptr); + + BeginEnumValues(enum_def, code_ptr); + for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); + ++it) { + auto &ev = **it; + EnumValueMember(enum_def, ev, max_name_length, code_ptr); + } + EndEnumValues(code_ptr); + + EnumStringer(enum_def, code_ptr); } // Returns the function name that is able to read a value of the given type. @@ -788,7 +848,7 @@ class GoGenerator : public BaseGenerator { } std::string GenTypeGet(const Type &type) { - if (type.enum_def != nullptr && !type.enum_def->is_union) { + if (type.enum_def != nullptr) { return GetEnumTypeName(*type.enum_def); } return IsScalar(type.base_type) ? GenTypeBasic(type) : GenTypePointer(type); @@ -798,6 +858,26 @@ class GoGenerator : public BaseGenerator { return GenTypeGet(field.value.type); } + // If type is an enum, returns value with a cast to the enum type, otherwise + // returns value as-is. + std::string CastToEnum(const Type &type, std::string value) { + if (type.enum_def == nullptr) { + return value; + } else { + return GenTypeGet(type) + "(" + value + ")"; + } + } + + // If type is an enum, returns value with a cast to the enum base type, + // otherwise returns value as-is. + std::string CastToBaseType(const Type &type, std::string value) { + if (type.enum_def == nullptr) { + return value; + } else { + return GenTypeBasic(type) + "(" + value + ")"; + } + } + std::string GenConstant(const FieldDef &field) { switch (field.value.type.base_type) { case BASE_TYPE_BOOL: return field.value.constant == "0" ? "false" : "true";; @@ -816,12 +896,15 @@ class GoGenerator : public BaseGenerator { } // Begin by declaring namespace and imports. void BeginFile(const std::string name_space_name, const bool needs_imports, - std::string *code_ptr) { + const bool is_enum, std::string *code_ptr) { std::string &code = *code_ptr; code = code + "// Code generated by the FlatBuffers compiler. DO NOT EDIT.\n\n"; code += "package " + name_space_name + "\n\n"; if (needs_imports) { code += "import (\n"; + if (is_enum) { + code += "\t\"strconv\"\n\n"; + } if (!parser_.opts.go_import.empty()) { code += "\tflatbuffers \"" + parser_.opts.go_import + "\"\n"; } else { @@ -837,19 +920,27 @@ class GoGenerator : public BaseGenerator { } } code += ")\n\n"; + } else { + if (is_enum) { + code += "import \"strconv\"\n\n"; + } } } // Save out the generated code for a Go Table type. bool SaveType(const Definition &def, const std::string &classcode, - bool needs_imports) { + const bool needs_imports, const bool is_enum) { if (!classcode.length()) return true; Namespace &ns = go_namespace_.components.empty() ? *def.defined_namespace : go_namespace_; std::string code = ""; - BeginFile(LastNamespacePart(ns), needs_imports, &code); + BeginFile(LastNamespacePart(ns), needs_imports, is_enum, &code); code += classcode; + // Strip extra newlines at end of file to make it gofmt-clean. + while (code.length() > 2 && code.substr(code.length() - 2) == "\n\n") { + code.pop_back(); + } std::string filename = NamespaceDir(ns) + def.name + ".go"; return SaveFile(filename.c_str(), code, false); } @@ -897,6 +988,16 @@ class GoGenerator : public BaseGenerator { } const Namespace *CurrentNameSpace() const { return cur_name_space_; } + + static int MaxNameLength(const EnumDef &enum_def) { + int max = 0; + for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); + ++it) { + const int length = (*it)->name.length(); + max = length > max ? length : max; + } + return max; + } }; } // namespace go diff --git a/tests/MyGame/Example/Any.go b/tests/MyGame/Example/Any.go index 39b89a79..8d9067e1 100644 --- a/tests/MyGame/Example/Any.go +++ b/tests/MyGame/Example/Any.go @@ -2,18 +2,34 @@ package Example -type Any = byte +import "strconv" + +type Any byte + const ( - AnyNONE Any = 0 - AnyMonster Any = 1 + AnyNONE Any = 0 + AnyMonster Any = 1 AnyTestSimpleTableWithEnum Any = 2 AnyMyGame_Example2_Monster Any = 3 ) var EnumNamesAny = map[Any]string{ - AnyNONE:"NONE", - AnyMonster:"Monster", - AnyTestSimpleTableWithEnum:"TestSimpleTableWithEnum", - AnyMyGame_Example2_Monster:"MyGame_Example2_Monster", + AnyNONE: "NONE", + AnyMonster: "Monster", + AnyTestSimpleTableWithEnum: "TestSimpleTableWithEnum", + AnyMyGame_Example2_Monster: "MyGame_Example2_Monster", } +var EnumValuesAny = map[string]Any{ + "NONE": AnyNONE, + "Monster": AnyMonster, + "TestSimpleTableWithEnum": AnyTestSimpleTableWithEnum, + "MyGame_Example2_Monster": AnyMyGame_Example2_Monster, +} + +func (v Any) String() string { + if s, ok := EnumNamesAny[v]; ok { + return s + } + return "Any(" + strconv.FormatInt(int64(v), 10) + ")" +} diff --git a/tests/MyGame/Example/AnyAmbiguousAliases.go b/tests/MyGame/Example/AnyAmbiguousAliases.go index b5eacde4..b9c3793a 100644 --- a/tests/MyGame/Example/AnyAmbiguousAliases.go +++ b/tests/MyGame/Example/AnyAmbiguousAliases.go @@ -2,18 +2,34 @@ package Example -type AnyAmbiguousAliases = byte +import "strconv" + +type AnyAmbiguousAliases byte + const ( AnyAmbiguousAliasesNONE AnyAmbiguousAliases = 0 - AnyAmbiguousAliasesM1 AnyAmbiguousAliases = 1 - AnyAmbiguousAliasesM2 AnyAmbiguousAliases = 2 - AnyAmbiguousAliasesM3 AnyAmbiguousAliases = 3 + AnyAmbiguousAliasesM1 AnyAmbiguousAliases = 1 + AnyAmbiguousAliasesM2 AnyAmbiguousAliases = 2 + AnyAmbiguousAliasesM3 AnyAmbiguousAliases = 3 ) var EnumNamesAnyAmbiguousAliases = map[AnyAmbiguousAliases]string{ - AnyAmbiguousAliasesNONE:"NONE", - AnyAmbiguousAliasesM1:"M1", - AnyAmbiguousAliasesM2:"M2", - AnyAmbiguousAliasesM3:"M3", + AnyAmbiguousAliasesNONE: "NONE", + AnyAmbiguousAliasesM1: "M1", + AnyAmbiguousAliasesM2: "M2", + AnyAmbiguousAliasesM3: "M3", } +var EnumValuesAnyAmbiguousAliases = map[string]AnyAmbiguousAliases{ + "NONE": AnyAmbiguousAliasesNONE, + "M1": AnyAmbiguousAliasesM1, + "M2": AnyAmbiguousAliasesM2, + "M3": AnyAmbiguousAliasesM3, +} + +func (v AnyAmbiguousAliases) String() string { + if s, ok := EnumNamesAnyAmbiguousAliases[v]; ok { + return s + } + return "AnyAmbiguousAliases(" + strconv.FormatInt(int64(v), 10) + ")" +} diff --git a/tests/MyGame/Example/AnyUniqueAliases.go b/tests/MyGame/Example/AnyUniqueAliases.go index 14a2694a..36e4d8e2 100644 --- a/tests/MyGame/Example/AnyUniqueAliases.go +++ b/tests/MyGame/Example/AnyUniqueAliases.go @@ -2,18 +2,34 @@ package Example -type AnyUniqueAliases = byte +import "strconv" + +type AnyUniqueAliases byte + const ( AnyUniqueAliasesNONE AnyUniqueAliases = 0 - AnyUniqueAliasesM AnyUniqueAliases = 1 - AnyUniqueAliasesT AnyUniqueAliases = 2 - AnyUniqueAliasesM2 AnyUniqueAliases = 3 + AnyUniqueAliasesM AnyUniqueAliases = 1 + AnyUniqueAliasesT AnyUniqueAliases = 2 + AnyUniqueAliasesM2 AnyUniqueAliases = 3 ) var EnumNamesAnyUniqueAliases = map[AnyUniqueAliases]string{ - AnyUniqueAliasesNONE:"NONE", - AnyUniqueAliasesM:"M", - AnyUniqueAliasesT:"T", - AnyUniqueAliasesM2:"M2", + AnyUniqueAliasesNONE: "NONE", + AnyUniqueAliasesM: "M", + AnyUniqueAliasesT: "T", + AnyUniqueAliasesM2: "M2", } +var EnumValuesAnyUniqueAliases = map[string]AnyUniqueAliases{ + "NONE": AnyUniqueAliasesNONE, + "M": AnyUniqueAliasesM, + "T": AnyUniqueAliasesT, + "M2": AnyUniqueAliasesM2, +} + +func (v AnyUniqueAliases) String() string { + if s, ok := EnumNamesAnyUniqueAliases[v]; ok { + return s + } + return "AnyUniqueAliases(" + strconv.FormatInt(int64(v), 10) + ")" +} diff --git a/tests/MyGame/Example/Color.go b/tests/MyGame/Example/Color.go index 67718b28..52e28ebc 100644 --- a/tests/MyGame/Example/Color.go +++ b/tests/MyGame/Example/Color.go @@ -2,16 +2,31 @@ package Example -type Color = byte +import "strconv" + +type Color byte + const ( - ColorRed Color = 1 + ColorRed Color = 1 ColorGreen Color = 2 - ColorBlue Color = 8 + ColorBlue Color = 8 ) var EnumNamesColor = map[Color]string{ - ColorRed:"Red", - ColorGreen:"Green", - ColorBlue:"Blue", + ColorRed: "Red", + ColorGreen: "Green", + ColorBlue: "Blue", } +var EnumValuesColor = map[string]Color{ + "Red": ColorRed, + "Green": ColorGreen, + "Blue": ColorBlue, +} + +func (v Color) String() string { + if s, ok := EnumNamesColor[v]; ok { + return s + } + return "Color(" + strconv.FormatInt(int64(v), 10) + ")" +} diff --git a/tests/MyGame/Example/Monster.go b/tests/MyGame/Example/Monster.go index 1f146b0f..ab32ccfd 100644 --- a/tests/MyGame/Example/Monster.go +++ b/tests/MyGame/Example/Monster.go @@ -111,25 +111,25 @@ func (rcv *Monster) MutateInventory(j int, n byte) bool { func (rcv *Monster) Color() Color { o := flatbuffers.UOffsetT(rcv._tab.Offset(16)) if o != 0 { - return rcv._tab.GetByte(o + rcv._tab.Pos) + return Color(rcv._tab.GetByte(o + rcv._tab.Pos)) } return 8 } func (rcv *Monster) MutateColor(n Color) bool { - return rcv._tab.MutateByteSlot(16, n) + return rcv._tab.MutateByteSlot(16, byte(n)) } -func (rcv *Monster) TestType() byte { +func (rcv *Monster) TestType() Any { o := flatbuffers.UOffsetT(rcv._tab.Offset(18)) if o != 0 { - return rcv._tab.GetByte(o + rcv._tab.Pos) + return Any(rcv._tab.GetByte(o + rcv._tab.Pos)) } return 0 } -func (rcv *Monster) MutateTestType(n byte) bool { - return rcv._tab.MutateByteSlot(18, n) +func (rcv *Monster) MutateTestType(n Any) bool { + return rcv._tab.MutateByteSlot(18, byte(n)) } func (rcv *Monster) Test(obj *flatbuffers.Table) bool { @@ -739,16 +739,16 @@ func (rcv *Monster) MutateVectorOfNonOwningReferences(j int, n uint64) bool { return false } -func (rcv *Monster) AnyUniqueType() byte { +func (rcv *Monster) AnyUniqueType() AnyUniqueAliases { o := flatbuffers.UOffsetT(rcv._tab.Offset(90)) if o != 0 { - return rcv._tab.GetByte(o + rcv._tab.Pos) + return AnyUniqueAliases(rcv._tab.GetByte(o + rcv._tab.Pos)) } return 0 } -func (rcv *Monster) MutateAnyUniqueType(n byte) bool { - return rcv._tab.MutateByteSlot(90, n) +func (rcv *Monster) MutateAnyUniqueType(n AnyUniqueAliases) bool { + return rcv._tab.MutateByteSlot(90, byte(n)) } func (rcv *Monster) AnyUnique(obj *flatbuffers.Table) bool { @@ -760,16 +760,16 @@ func (rcv *Monster) AnyUnique(obj *flatbuffers.Table) bool { return false } -func (rcv *Monster) AnyAmbiguousType() byte { +func (rcv *Monster) AnyAmbiguousType() AnyAmbiguousAliases { o := flatbuffers.UOffsetT(rcv._tab.Offset(94)) if o != 0 { - return rcv._tab.GetByte(o + rcv._tab.Pos) + return AnyAmbiguousAliases(rcv._tab.GetByte(o + rcv._tab.Pos)) } return 0 } -func (rcv *Monster) MutateAnyAmbiguousType(n byte) bool { - return rcv._tab.MutateByteSlot(94, n) +func (rcv *Monster) MutateAnyAmbiguousType(n AnyAmbiguousAliases) bool { + return rcv._tab.MutateByteSlot(94, byte(n)) } func (rcv *Monster) AnyAmbiguous(obj *flatbuffers.Table) bool { @@ -785,7 +785,7 @@ func (rcv *Monster) VectorOfEnums(j int) Color { o := flatbuffers.UOffsetT(rcv._tab.Offset(98)) if o != 0 { a := rcv._tab.Vector(o) - return rcv._tab.GetByte(a + flatbuffers.UOffsetT(j*1)) + return Color(rcv._tab.GetByte(a + flatbuffers.UOffsetT(j*1))) } return 0 } @@ -810,7 +810,7 @@ func (rcv *Monster) MutateVectorOfEnums(j int, n Color) bool { o := flatbuffers.UOffsetT(rcv._tab.Offset(98)) if o != 0 { a := rcv._tab.Vector(o) - return rcv._tab.MutateByte(a+flatbuffers.UOffsetT(j*1), n) + return rcv._tab.MutateByte(a+flatbuffers.UOffsetT(j*1), byte(n)) } return false } diff --git a/tests/MyGame/Example/TestSimpleTableWithEnum.go b/tests/MyGame/Example/TestSimpleTableWithEnum.go index 638ed4db..78c4d713 100644 --- a/tests/MyGame/Example/TestSimpleTableWithEnum.go +++ b/tests/MyGame/Example/TestSimpleTableWithEnum.go @@ -29,13 +29,13 @@ func (rcv *TestSimpleTableWithEnum) Table() flatbuffers.Table { func (rcv *TestSimpleTableWithEnum) Color() Color { o := flatbuffers.UOffsetT(rcv._tab.Offset(4)) if o != 0 { - return rcv._tab.GetByte(o + rcv._tab.Pos) + return Color(rcv._tab.GetByte(o + rcv._tab.Pos)) } return 2 } func (rcv *TestSimpleTableWithEnum) MutateColor(n Color) bool { - return rcv._tab.MutateByteSlot(4, n) + return rcv._tab.MutateByteSlot(4, byte(n)) } func TestSimpleTableWithEnumStart(builder *flatbuffers.Builder) { diff --git a/tests/MyGame/Example/Vec3.go b/tests/MyGame/Example/Vec3.go index 81b5c396..a9feaae0 100644 --- a/tests/MyGame/Example/Vec3.go +++ b/tests/MyGame/Example/Vec3.go @@ -48,10 +48,10 @@ func (rcv *Vec3) MutateTest1(n float64) bool { } func (rcv *Vec3) Test2() Color { - return rcv._tab.GetByte(rcv._tab.Pos + flatbuffers.UOffsetT(24)) + return Color(rcv._tab.GetByte(rcv._tab.Pos + flatbuffers.UOffsetT(24))) } func (rcv *Vec3) MutateTest2(n Color) bool { - return rcv._tab.MutateByte(rcv._tab.Pos+flatbuffers.UOffsetT(24), n) + return rcv._tab.MutateByte(rcv._tab.Pos+flatbuffers.UOffsetT(24), byte(n)) } func (rcv *Vec3) Test3(obj *Test) *Test { diff --git a/tests/go_test.go b/tests/go_test.go index fffb0ebc..87741602 100644 --- a/tests/go_test.go +++ b/tests/go_test.go @@ -106,6 +106,12 @@ func TestAll(t *testing.T) { // Verify the enum names CheckEnumNames(t.Fatalf) + // Verify enum String methods + CheckEnumString(t.Fatalf) + + // Verify the enum values maps + CheckEnumValues(t.Fatalf) + // Verify that the Go code used in FlatBuffers documentation passes // some sanity checks: CheckDocExample(generated, off, t.Fatalf) @@ -199,8 +205,8 @@ func CheckReadBuffer(buf []byte, offset flatbuffers.UOffsetT, fail func(string, fail(FailString("Pos.Test1", float64(3.0), got)) } - if got := vec.Test2(); uint8(2) != got { - fail(FailString("Pos.Test2", uint8(2), got)) + if got := vec.Test2(); example.ColorGreen != got { + fail(FailString("Pos.Test2", example.ColorGreen, got)) } // initialize a Test from Test3(...) @@ -331,7 +337,7 @@ func CheckMutateBuffer(org []byte, offset flatbuffers.UOffsetT, fail func(string testcase{"Pos.Y'", func() bool { return monster.Pos(nil).Y() == float32(2.0) }}, testcase{"Pos.Z'", func() bool { return monster.Pos(nil).Z() == float32(3.0) }}, testcase{"Pos.Test1'", func() bool { return monster.Pos(nil).Test1() == float64(3.0) }}, - testcase{"Pos.Test2'", func() bool { return monster.Pos(nil).Test2() == uint8(2) }}, + testcase{"Pos.Test2'", func() bool { return monster.Pos(nil).Test2() == example.ColorGreen }}, testcase{"Pos.Test3.A", func() bool { return monster.Pos(nil).Test3(nil).A() == int16(5) }}, testcase{"Pos.Test3.B", func() bool { return monster.Pos(nil).Test3(nil).B() == int8(6) }}, testcase{"Inventory[2]", func() bool { return monster.Inventory(2) == byte(2) }}, @@ -345,7 +351,7 @@ func CheckMutateBuffer(org []byte, offset flatbuffers.UOffsetT, fail func(string testcase{"Pos.Y", func() bool { return monster.Pos(nil).MutateY(20.0) }}, testcase{"Pos.Z", func() bool { return monster.Pos(nil).MutateZ(30.0) }}, testcase{"Pos.Test1", func() bool { return monster.Pos(nil).MutateTest1(30.0) }}, - testcase{"Pos.Test2", func() bool { return monster.Pos(nil).MutateTest2(20) }}, + testcase{"Pos.Test2", func() bool { return monster.Pos(nil).MutateTest2(example.ColorBlue) }}, testcase{"Pos.Test3.A", func() bool { return monster.Pos(nil).Test3(nil).MutateA(50) }}, testcase{"Pos.Test3.B", func() bool { return monster.Pos(nil).Test3(nil).MutateB(60) }}, testcase{"Inventory[2]", func() bool { return monster.MutateInventory(2, 200) }}, @@ -359,12 +365,17 @@ func CheckMutateBuffer(org []byte, offset flatbuffers.UOffsetT, fail func(string testcase{"Pos.Y'", func() bool { return monster.Pos(nil).Y() == float32(20.0) }}, testcase{"Pos.Z'", func() bool { return monster.Pos(nil).Z() == float32(30.0) }}, testcase{"Pos.Test1'", func() bool { return monster.Pos(nil).Test1() == float64(30.0) }}, - testcase{"Pos.Test2'", func() bool { return monster.Pos(nil).Test2() == uint8(20) }}, + testcase{"Pos.Test2'", func() bool { return monster.Pos(nil).Test2() == example.ColorBlue }}, testcase{"Pos.Test3.A", func() bool { return monster.Pos(nil).Test3(nil).A() == int16(50) }}, testcase{"Pos.Test3.B", func() bool { return monster.Pos(nil).Test3(nil).B() == int8(60) }}, testcase{"Inventory[2]", func() bool { return monster.Inventory(2) == byte(200) }}, } + testInvalidEnumValues := []testcase{ + testcase{"Pos.Test2", func() bool { return monster.Pos(nil).MutateTest2(example.Color(20)) }}, + testcase{"Pos.Test2", func() bool { return monster.Pos(nil).Test2() == example.Color(20) }}, + } + // make sure original values are okay for _, t := range testForOriginalValues { if !t.testfn() { @@ -400,6 +411,14 @@ func CheckMutateBuffer(org []byte, offset flatbuffers.UOffsetT, fail func(string } } + // a couple extra tests for "invalid" enum values, which don't correspond to + // anything in the schema, but are allowed + for _, t := range testInvalidEnumValues { + if !t.testfn() { + fail("field '" + t.field + "' doesn't work with an invalid enum value") + } + } + // reverting all fields to original values should // re-create the original buffer. Mutate all fields // back to their original values and compare buffers. @@ -412,7 +431,7 @@ func CheckMutateBuffer(org []byte, offset flatbuffers.UOffsetT, fail func(string monster.Pos(nil).MutateY(2.0) monster.Pos(nil).MutateZ(3.0) monster.Pos(nil).MutateTest1(3.0) - monster.Pos(nil).MutateTest2(2) + monster.Pos(nil).MutateTest2(example.ColorGreen) monster.Pos(nil).Test3(nil).MutateA(5) monster.Pos(nil).Test3(nil).MutateB(6) monster.MutateInventory(2, 2) @@ -1379,7 +1398,6 @@ func CheckFinishedBytesError(fail func(string, ...interface{})) { // CheckEnumNames checks that the generated enum names are correct. func CheckEnumNames(fail func(string, ...interface{})) { { - want := map[example.Any]string{ example.AnyNONE: "NONE", example.AnyMonster: "Monster", @@ -1404,6 +1422,43 @@ func CheckEnumNames(fail func(string, ...interface{})) { } } +// CheckEnumString checks the String method on generated enum types. +func CheckEnumString(fail func(string, ...interface{})) { + if got := example.AnyMonster.String(); got != "Monster" { + fail("Monster.String: %q != %q", got, "Monster") + } + if got := fmt.Sprintf("color: %s", example.ColorGreen); got != "color: Green" { + fail("color.String: %q != %q", got, "color: Green") + } +} + +// CheckEnumValues checks that the generated enum values maps are correct. +func CheckEnumValues(fail func(string, ...interface{})) { + { + want := map[string]example.Any{ + "NONE": example.AnyNONE, + "Monster": example.AnyMonster, + "TestSimpleTableWithEnum": example.AnyTestSimpleTableWithEnum, + "MyGame_Example2_Monster": example.AnyMyGame_Example2_Monster, + } + got := example.EnumValuesAny + if !reflect.DeepEqual(got, want) { + fail("enum name is not equal") + } + } + { + want := map[string]example.Color{ + "Red": example.ColorRed, + "Green": example.ColorGreen, + "Blue": example.ColorBlue, + } + got := example.EnumValuesColor + if !reflect.DeepEqual(got, want) { + fail("enum name is not equal") + } + } +} + // CheckDocExample checks that the code given in FlatBuffers documentation // is syntactically correct. func CheckDocExample(buf []byte, off flatbuffers.UOffsetT, fail func(string, ...interface{})) { diff --git a/tests/namespace_test/NamespaceA/NamespaceB/EnumInNestedNS.go b/tests/namespace_test/NamespaceA/NamespaceB/EnumInNestedNS.go index 797ea67f..6cec5ffc 100644 --- a/tests/namespace_test/NamespaceA/NamespaceB/EnumInNestedNS.go +++ b/tests/namespace_test/NamespaceA/NamespaceB/EnumInNestedNS.go @@ -2,7 +2,10 @@ package NamespaceB -type EnumInNestedNS = int8 +import "strconv" + +type EnumInNestedNS int8 + const ( EnumInNestedNSA EnumInNestedNS = 0 EnumInNestedNSB EnumInNestedNS = 1 @@ -10,8 +13,20 @@ const ( ) var EnumNamesEnumInNestedNS = map[EnumInNestedNS]string{ - EnumInNestedNSA:"A", - EnumInNestedNSB:"B", - EnumInNestedNSC:"C", + EnumInNestedNSA: "A", + EnumInNestedNSB: "B", + EnumInNestedNSC: "C", } +var EnumValuesEnumInNestedNS = map[string]EnumInNestedNS{ + "A": EnumInNestedNSA, + "B": EnumInNestedNSB, + "C": EnumInNestedNSC, +} + +func (v EnumInNestedNS) String() string { + if s, ok := EnumNamesEnumInNestedNS[v]; ok { + return s + } + return "EnumInNestedNS(" + strconv.FormatInt(int64(v), 10) + ")" +} diff --git a/tests/namespace_test/NamespaceA/TableInFirstNS.go b/tests/namespace_test/NamespaceA/TableInFirstNS.go index 877e2d44..c8da74a9 100644 --- a/tests/namespace_test/NamespaceA/TableInFirstNS.go +++ b/tests/namespace_test/NamespaceA/TableInFirstNS.go @@ -44,13 +44,13 @@ func (rcv *TableInFirstNS) FooTable(obj *NamespaceA__NamespaceB.TableInNestedNS) func (rcv *TableInFirstNS) FooEnum() EnumInNestedNS { o := flatbuffers.UOffsetT(rcv._tab.Offset(6)) if o != 0 { - return rcv._tab.GetInt8(o + rcv._tab.Pos) + return EnumInNestedNS(rcv._tab.GetInt8(o + rcv._tab.Pos)) } return 0 } func (rcv *TableInFirstNS) MutateFooEnum(n EnumInNestedNS) bool { - return rcv._tab.MutateInt8Slot(6, n) + return rcv._tab.MutateInt8Slot(6, int8(n)) } func (rcv *TableInFirstNS) FooStruct(obj *NamespaceA__NamespaceB.StructInNestedNS) *NamespaceA__NamespaceB.StructInNestedNS { |