internal/value: expose Converter.{MessageType,EnumType}

Rather than having the Converter carry a NewMessage method, have the struct
simply expose the MessageType or EnumType since they carry more information
and are retrieved anyways as part of the functionality of NewConverter.
While changing Converter, export the fields and remove all the methods.
Also, add an IsLegacy boolean, which is useful for the later implementation
of the extension fields.

Add a wrapLegacyEnum function which is used to wrap v1 enums as v2 enums.
We use this functionality in NewLegacyConverter to detrive the EnumType.
Additionally, modify wrapLegacyMessage to return a protoreflect.ProtoMessage
to be consistent with wrapLegacyEnum which must return a protoreflect.ProtoEnum.

Change-Id: Idc8989d07e4895d30de4ebc22c9ffa7357815cad
Reviewed-on: https://go-review.googlesource.com/c/148827
Reviewed-by: Herbie Ong <herbie@google.com>
diff --git a/internal/value/convert.go b/internal/value/convert.go
index cd3402a..e4692a7 100644
--- a/internal/value/convert.go
+++ b/internal/value/convert.go
@@ -53,14 +53,17 @@
 // protoc-gen-go historically generated to be able to automatically wrap some
 // v1 messages generated by other forks of protoc-gen-go.
 func NewConverter(t reflect.Type, k pref.Kind) Converter {
-	return NewLegacyConverter(t, k, nil)
+	return NewLegacyConverter(t, k, nil, nil)
 }
 
 // NewLegacyConverter is identical to NewConverter,
 // but supports wrapping legacy v1 messages to implement the v2 message API
-// using the provided wrapLegacyMessage function.
+// using the provided wrapEnum and wrapMessage functions.
 // The wrapped message must implement Unwrapper.
-func NewLegacyConverter(t reflect.Type, k pref.Kind, wrapLegacyMessage func(reflect.Value) pref.Message) Converter {
+func NewLegacyConverter(t reflect.Type, k pref.Kind, wrapEnum func(reflect.Value) pref.ProtoEnum, wrapMessage func(reflect.Value) pref.ProtoMessage) Converter {
+	if (wrapEnum == nil) != (wrapMessage == nil) {
+		panic("legacy enum and message wrappers must both be populated or nil")
+	}
 	switch k {
 	case pref.BoolKind:
 		if t.Kind() == reflect.Bool {
@@ -103,35 +106,39 @@
 		if t.Kind() != reflect.Ptr && t.Implements(enumIfaceV2) {
 			et := reflect.Zero(t).Interface().(pref.ProtoEnum).ProtoReflect().Type()
 			return Converter{
-				toPB: func(v reflect.Value) pref.Value {
+				PBValueOf: func(v reflect.Value) pref.Value {
 					if v.Type() != t {
 						panic(fmt.Sprintf("invalid type: got %v, want %v", v.Type(), t))
 					}
 					e := v.Interface().(pref.ProtoEnum)
 					return pref.ValueOf(e.ProtoReflect().Number())
 				},
-				toGo: func(v pref.Value) reflect.Value {
+				GoValueOf: func(v pref.Value) reflect.Value {
 					rv := reflect.ValueOf(et.New(v.Enum()))
 					if rv.Type() != t {
 						panic(fmt.Sprintf("invalid type: got %v, want %v", rv.Type(), t))
 					}
 					return rv
 				},
+				EnumType: et,
 			}
 		}
 
 		// Handle v1 enums, which we identify as simply a named int32 type.
-		if wrapLegacyMessage != nil && t.Kind() == reflect.Int32 && t.PkgPath() != "" {
+		if wrapEnum != nil && t.PkgPath() != "" && t.Kind() == reflect.Int32 {
+			et := wrapEnum(reflect.Zero(t)).ProtoReflect().Type()
 			return Converter{
-				toPB: func(v reflect.Value) pref.Value {
+				PBValueOf: func(v reflect.Value) pref.Value {
 					if v.Type() != t {
 						panic(fmt.Sprintf("invalid type: got %v, want %v", v.Type(), t))
 					}
 					return pref.ValueOf(pref.EnumNumber(v.Int()))
 				},
-				toGo: func(v pref.Value) reflect.Value {
+				GoValueOf: func(v pref.Value) reflect.Value {
 					return reflect.ValueOf(v.Enum()).Convert(t)
 				},
+				EnumType: et,
+				IsLegacy: true,
 			}
 		}
 	case pref.MessageKind, pref.GroupKind:
@@ -139,44 +146,42 @@
 		if t.Kind() == reflect.Ptr && t.Implements(messageIfaceV2) {
 			mt := reflect.Zero(t).Interface().(pref.ProtoMessage).ProtoReflect().Type()
 			return Converter{
-				toPB: func(v reflect.Value) pref.Value {
+				PBValueOf: func(v reflect.Value) pref.Value {
 					if v.Type() != t {
 						panic(fmt.Sprintf("invalid type: got %v, want %v", v.Type(), t))
 					}
 					return pref.ValueOf(v.Interface())
 				},
-				toGo: func(v pref.Value) reflect.Value {
+				GoValueOf: func(v pref.Value) reflect.Value {
 					rv := reflect.ValueOf(v.Message())
 					if rv.Type() != t {
 						panic(fmt.Sprintf("invalid type: got %v, want %v", rv.Type(), t))
 					}
 					return rv
 				},
-				newMessage: func() pref.Message {
-					return mt.New().ProtoReflect()
-				},
+				MessageType: mt,
 			}
 		}
 
 		// Handle v1 messages, which we need to wrap as a v2 message.
-		if wrapLegacyMessage != nil && t.Kind() == reflect.Ptr && t.Implements(messageIfaceV1) {
+		if wrapMessage != nil && t.Kind() == reflect.Ptr && t.Implements(messageIfaceV1) {
+			mt := wrapMessage(reflect.New(t.Elem())).ProtoReflect().Type()
 			return Converter{
-				toPB: func(v reflect.Value) pref.Value {
+				PBValueOf: func(v reflect.Value) pref.Value {
 					if v.Type() != t {
 						panic(fmt.Sprintf("invalid type: got %v, want %v", v.Type(), t))
 					}
-					return pref.ValueOf(wrapLegacyMessage(v))
+					return pref.ValueOf(wrapMessage(v).ProtoReflect())
 				},
-				toGo: func(v pref.Value) reflect.Value {
+				GoValueOf: func(v pref.Value) reflect.Value {
 					rv := reflect.ValueOf(v.Message().(Unwrapper).Unwrap())
 					if rv.Type() != t {
 						panic(fmt.Sprintf("invalid type: got %v, want %v", rv.Type(), t))
 					}
 					return rv
 				},
-				newMessage: func() pref.Message {
-					return wrapLegacyMessage(reflect.New(t.Elem()))
-				},
+				MessageType: mt,
+				IsLegacy:    true,
 			}
 		}
 	}
@@ -185,7 +190,7 @@
 
 func makeScalarConverter(goType, pbType reflect.Type) Converter {
 	return Converter{
-		toPB: func(v reflect.Value) pref.Value {
+		PBValueOf: func(v reflect.Value) pref.Value {
 			if v.Type() != goType {
 				panic(fmt.Sprintf("invalid type: got %v, want %v", v.Type(), goType))
 			}
@@ -194,7 +199,7 @@
 			}
 			return pref.ValueOf(v.Convert(pbType).Interface())
 		},
-		toGo: func(v pref.Value) reflect.Value {
+		GoValueOf: func(v pref.Value) reflect.Value {
 			rv := reflect.ValueOf(v.Interface())
 			if rv.Type() != pbType {
 				panic(fmt.Sprintf("invalid type: got %v, want %v", rv.Type(), pbType))
@@ -210,11 +215,9 @@
 // Converter provides functions for converting to/from Go reflect.Value types
 // and protobuf protoreflect.Value types.
 type Converter struct {
-	toPB       func(reflect.Value) pref.Value
-	toGo       func(pref.Value) reflect.Value
-	newMessage func() pref.Message
+	PBValueOf   func(reflect.Value) pref.Value
+	GoValueOf   func(pref.Value) reflect.Value
+	EnumType    pref.EnumType
+	MessageType pref.MessageType
+	IsLegacy    bool
 }
-
-func (c Converter) PBValueOf(v reflect.Value) pref.Value { return c.toPB(v) }
-func (c Converter) GoValueOf(v pref.Value) reflect.Value { return c.toGo(v) }
-func (c Converter) NewMessage() pref.Message             { return c.newMessage() }
diff --git a/internal/value/map.go b/internal/value/map.go
index c1590d0..c051074 100644
--- a/internal/value/map.go
+++ b/internal/value/map.go
@@ -59,7 +59,7 @@
 	rv := ms.v.MapIndex(rk)
 	if !rv.IsValid() || rv.IsNil() {
 		// TODO: Is checking for nil proper behavior for custom messages?
-		pv := pref.ValueOf(ms.valConv.NewMessage())
+		pv := pref.ValueOf(ms.valConv.MessageType.New().ProtoReflect())
 		rv = ms.valConv.GoValueOf(pv)
 		ms.v.SetMapIndex(rk, rv)
 	}
diff --git a/internal/value/vector.go b/internal/value/vector.go
index 4c1a78d..664ece2 100644
--- a/internal/value/vector.go
+++ b/internal/value/vector.go
@@ -38,14 +38,14 @@
 	rv := vs.v.Index(i)
 	if rv.IsNil() {
 		// TODO: Is checking for nil proper behavior for custom messages?
-		pv := pref.ValueOf(vs.conv.NewMessage())
+		pv := pref.ValueOf(vs.conv.MessageType.New().ProtoReflect())
 		rv.Set(vs.conv.GoValueOf(pv))
 	}
 	return rv.Interface().(pref.Message)
 }
 func (vs vectorReflect) MutableAppend() pref.Mutable {
 	// MutableAppend is only valid for messages and panics for other kinds.
-	pv := pref.ValueOf(vs.conv.NewMessage())
+	pv := pref.ValueOf(vs.conv.MessageType.New().ProtoReflect())
 	vs.v.Set(reflect.Append(vs.v, vs.conv.GoValueOf(pv)))
 	return vs.v.Index(vs.Len() - 1).Interface().(pref.Message)
 }