types/dynamicpb: add NewEnumType

Add support for creating dynamic EnumTypes.

Change-Id: Ic9f5b73630734848b29cc436f6c179549a8ea74a
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/237219
Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
Reviewed-by: Joe Tsai <joetsai@google.com>
diff --git a/testing/prototest/enum.go b/testing/prototest/enum.go
new file mode 100644
index 0000000..9ee422e
--- /dev/null
+++ b/testing/prototest/enum.go
@@ -0,0 +1,33 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package prototest
+
+import (
+	"testing"
+
+	pref "google.golang.org/protobuf/reflect/protoreflect"
+)
+
+// Enum tests an EnumType implementation.
+type Enum struct{}
+
+func (test Enum) Test(t testing.TB, et pref.EnumType) {
+	ed := et.Descriptor()
+	values := ed.Values()
+	for i := 0; i < values.Len(); i++ {
+		evd := values.Get(i)
+		num := evd.Number()
+		e := et.New(num)
+		if e.Descriptor() != ed {
+			t.Errorf("enumType.New(%v).Descriptor() != enumType.Descriptor(), should match", num)
+		}
+		if e.Type() != et {
+			t.Errorf("enumType.New(%v).Type() != enumType, should match", num)
+		}
+		if got, want := e.Number(), num; got != want {
+			t.Errorf("enumType.New(%v).Number() = %v, want %v", num, got, want)
+		}
+	}
+}
diff --git a/types/dynamicpb/dynamic.go b/types/dynamicpb/dynamic.go
index 7046ef2..7db6e55 100644
--- a/types/dynamicpb/dynamic.go
+++ b/types/dynamicpb/dynamic.go
@@ -14,6 +14,39 @@
 	"google.golang.org/protobuf/runtime/protoimpl"
 )
 
+// enum is a dynamic protoreflect.Enum.
+type enum struct {
+	num pref.EnumNumber
+	typ pref.EnumType
+}
+
+func (e enum) Descriptor() pref.EnumDescriptor { return e.typ.Descriptor() }
+func (e enum) Type() pref.EnumType             { return e.typ }
+func (e enum) Number() pref.EnumNumber         { return e.num }
+
+// enumType is a dynamic protoreflect.EnumType.
+type enumType struct {
+	desc pref.EnumDescriptor
+}
+
+// NewEnumType creates a new EnumType with the provided descriptor.
+//
+// EnumTypes created by this package are equal if their descriptors are equal.
+// That is, if ed1 == ed2, then NewEnumType(ed1) == NewEnumType(ed2).
+//
+// Enum values created by the EnumType are equal if their numbers are equal.
+func NewEnumType(desc pref.EnumDescriptor) pref.EnumType {
+	return enumType{desc}
+}
+
+func (et enumType) New(n pref.EnumNumber) pref.Enum { return enum{n, et} }
+func (et enumType) Descriptor() pref.EnumDescriptor { return et.desc }
+
+// extensionType is a dynamic protoreflect.ExtensionType.
+type extensionType struct {
+	desc extensionTypeDescriptor
+}
+
 // A Message is a dynamically constructed protocol buffer message.
 //
 // Message implements the proto.Message interface, and may be used with all
@@ -577,11 +610,6 @@
 	panic(errors.New("%v: unknown kind %v", fd.FullName(), fd.Kind()))
 }
 
-// extensionType is a dynamic protoreflect.ExtensionType.
-type extensionType struct {
-	desc extensionTypeDescriptor
-}
-
 // NewExtensionType creates a new ExtensionType with the provided descriptor.
 //
 // Dynamic ExtensionTypes with the same descriptor compare as equal. That is,
diff --git a/types/dynamicpb/dynamic_test.go b/types/dynamicpb/dynamic_test.go
index 78e93bb..6835d3e 100644
--- a/types/dynamicpb/dynamic_test.go
+++ b/types/dynamicpb/dynamic_test.go
@@ -39,6 +39,16 @@
 	}
 }
 
+func TestDynamicEnums(t *testing.T) {
+	for _, enum := range []pref.Enum{
+		testpb.TestAllTypes_FOO,
+		test3pb.TestAllTypes_FOO,
+	} {
+		et := dynamicpb.NewEnumType(enum.Descriptor())
+		prototest.Enum{}.Test(t, et)
+	}
+}
+
 type extResolver struct{}
 
 func (extResolver) FindExtensionByName(field pref.FullName) (pref.ExtensionType, error) {