Commit 718ddea5 authored by David Reiss's avatar David Reiss Committed by Robert

[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
parent 8d86b534
This diff is collapsed.
......@@ -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) + ")"
}
......@@ -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) + ")"
}
......@@ -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) + ")"
}
......@@ -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) + ")"
}
......@@ -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
}
......
......@@ -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) {
......
......@@ -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 {
......
......@@ -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{})) {
......
......@@ -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) + ")"
}
......@@ -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 {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment