compiler/protogen, internal/strs, internal/impl: expose enum Go name derivation

In order to migrate v1 to wrap v2, we need a way to reproduce
the awful enum "names" that v1 used, which was the concatenation of
the proto package with the Go identifier used for the enum.

To support this:
* Move the camel case logic from compiler/protogen to internal/strs
* Add a small stub in internal/impl to expose this functionality

Change-Id: I8ff31daa9ae541e5788dc04d2e89eae1574877e4
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/191637
Reviewed-by: Damien Neil <dneil@google.com>
diff --git a/internal/impl/legacy_enum.go b/internal/impl/legacy_enum.go
index 279baa9..4ec31df 100644
--- a/internal/impl/legacy_enum.go
+++ b/internal/impl/legacy_enum.go
@@ -11,10 +11,27 @@
 	"sync"
 
 	"google.golang.org/protobuf/internal/filedesc"
+	"google.golang.org/protobuf/internal/strs"
 	"google.golang.org/protobuf/reflect/protoreflect"
 	pref "google.golang.org/protobuf/reflect/protoreflect"
 )
 
+// legacyEnumName returns the name of enums used in legacy code.
+// It is neither the protobuf full name nor the qualified Go name,
+// but rather an odd hybrid of both.
+func legacyEnumName(ed pref.EnumDescriptor) string {
+	var protoPkg string
+	enumName := string(ed.FullName())
+	if fd := ed.ParentFile(); fd != nil {
+		protoPkg = string(fd.Package())
+		enumName = strings.TrimPrefix(enumName, protoPkg+".")
+	}
+	if protoPkg == "" {
+		return strs.GoCamelCase(enumName)
+	}
+	return protoPkg + "." + strs.GoCamelCase(enumName)
+}
+
 // legacyWrapEnum wraps v as a protoreflect.Enum,
 // where v must be a int32 kind and not implement the v2 API already.
 func legacyWrapEnum(v reflect.Value) pref.Enum {
diff --git a/internal/impl/legacy_export.go b/internal/impl/legacy_export.go
index 07c16b5..29c1b01 100644
--- a/internal/impl/legacy_export.go
+++ b/internal/impl/legacy_export.go
@@ -21,6 +21,11 @@
 // These functions exist to support exported APIs in generated protobufs.
 // While these are deprecated, they cannot be removed for compatibility reasons.
 
+// LegacyEnumName returns the name of enums used in legacy code.
+func (Export) LegacyEnumName(ed pref.EnumDescriptor) string {
+	return legacyEnumName(ed)
+}
+
 // UnmarshalJSONEnum unmarshals an enum from a JSON-encoded input.
 // The input can either be a string representing the enum value by name,
 // or a number representing the enum number itself.
diff --git a/internal/impl/legacy_extension.go b/internal/impl/legacy_extension.go
index b484067..ec5420d 100644
--- a/internal/impl/legacy_extension.go
+++ b/internal/impl/legacy_extension.go
@@ -77,31 +77,10 @@
 		}
 	}
 
-	// Reconstruct the legacy enum full name, which is an odd mixture of the
-	// proto package name with the Go type name.
+	// Reconstruct the legacy enum full name.
 	var enumName string
 	if xd.Kind() == pref.EnumKind {
-		// Derive Go type name.
-		t := extType
-		if t.Kind() == reflect.Ptr || t.Kind() == reflect.Slice {
-			t = t.Elem()
-		}
-		enumName = t.Name()
-
-		// Derive the proto package name.
-		// For legacy enums, obtain the proto package from the raw descriptor.
-		var protoPkg string
-		if fd := xd.Enum().ParentFile(); fd != nil {
-			protoPkg = string(fd.Package())
-		}
-		if ed, ok := reflect.Zero(t).Interface().(enumV1); ok && protoPkg == "" {
-			b, _ := ed.EnumDescriptor()
-			protoPkg = string(legacyLoadFileDesc(b).Package())
-		}
-
-		if protoPkg != "" {
-			enumName = protoPkg + "." + enumName
-		}
+		enumName = legacyEnumName(xd.Enum())
 	}
 
 	// Derive the proto file that the extension was declared within.