Skip to content

Commit

Permalink
proto: add MessageV1, MessageV2, and MessageReflect
Browse files Browse the repository at this point in the history
The MessageV1 and MessageV2 functions convert to/from the v1 and v2
message interfaces.
The MessageReflect function provides a reflective view over message.
These functions do not have an "Of" suffix to be consistent with
the existing MessageName and MessageType functions.

Furthermore, we drop the "Of" suffix from functions in the descriptor
package to be consistent. This is a safe change since none of those
functions have seen a stable release.

We move the descriptor.GeneratedXXX types to the proto package
for documentation purposes.

Fixes #956

Change-Id: I566b74367798e2e3399db9902b58ffeb673199ca
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/219137
Reviewed-by: Damien Neil <[email protected]>
  • Loading branch information
dsnet committed Feb 12, 2020
1 parent f5a698d commit 0fd14f9
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 31 deletions.
40 changes: 20 additions & 20 deletions descriptor/descriptor.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,10 @@ import (
)

// Message is proto.Message with a method to return its descriptor.
// Not every message is guaranteed to implement this interface.
//
// Deprecated: The Descriptor method may not be generated by future
// versions of protoc-gen-go, meaning that this interface may not
// be implemented by many concrete message types.
type Message interface {
proto.Message
Descriptor() ([]byte, []int)
Expand All @@ -34,18 +37,15 @@ type Message interface {
// ForMessage returns the file descriptor proto containing
// the message and the message descriptor proto for the message itself.
// The returned proto messages must not be mutated.
//
// Deprecated: Not all concrete message types satisfy the Message interface.
// Use MessageDescriptorProto instead. If possible, the calling code should
// be rewritten to use protobuf reflection instead.
// See package "google.golang.org/protobuf/reflect/protoreflect" for details.
func ForMessage(m Message) (*descriptorpb.FileDescriptorProto, *descriptorpb.DescriptorProto) {
return MessageDescriptorProtoOf(m)
return MessageDescriptorProto(m)
}

// GeneratedEnum is any enum type generated by protoc-gen-go
// which is a named int32 kind.
type GeneratedEnum interface{}

// GeneratedMessage is any message type generated by protoc-gen-go
// which is a pointer to a named struct kind.
type GeneratedMessage interface{}

type rawDesc struct {
fileDesc []byte
indexes []int
Expand Down Expand Up @@ -99,21 +99,21 @@ func deriveRawDescriptor(d protoreflect.Descriptor) ([]byte, []int) {
return file, idxs
}

// EnumRawDescriptorOf returns the GZIP'd raw file descriptor containing the
// EnumRawDescriptor returns the GZIP'd raw file descriptor containing the
// enum and the index path to reach the enum declaration.
// The returned slices must not be mutated.
func EnumRawDescriptorOf(e GeneratedEnum) ([]byte, []int) {
func EnumRawDescriptor(e proto.GeneratedEnum) ([]byte, []int) {
if ev, ok := e.(interface{ EnumDescriptor() ([]byte, []int) }); ok {
return ev.EnumDescriptor()
}
ed := protoimpl.X.EnumTypeOf(e)
return deriveRawDescriptor(ed.Descriptor())
}

// MessageRawDescriptorOf returns the GZIP'd raw file descriptor containing
// MessageRawDescriptor returns the GZIP'd raw file descriptor containing
// the message and the index path to reach the message declaration.
// The returned slices must not be mutated.
func MessageRawDescriptorOf(m GeneratedMessage) ([]byte, []int) {
func MessageRawDescriptor(m proto.GeneratedMessage) ([]byte, []int) {
if mv, ok := m.(interface{ Descriptor() ([]byte, []int) }); ok {
return mv.Descriptor()
}
Expand Down Expand Up @@ -148,11 +148,11 @@ func deriveFileDescriptor(rawDesc []byte) *descriptorpb.FileDescriptorProto {
return fd
}

// EnumDescriptorProtoOf returns the file descriptor proto containing
// EnumDescriptorProto returns the file descriptor proto containing
// the enum and the enum descriptor proto for the enum itself.
// The returned proto messages must not be mutated.
func EnumDescriptorProtoOf(e GeneratedEnum) (*descriptorpb.FileDescriptorProto, *descriptorpb.EnumDescriptorProto) {
rawDesc, idxs := EnumRawDescriptorOf(e)
func EnumDescriptorProto(e proto.GeneratedEnum) (*descriptorpb.FileDescriptorProto, *descriptorpb.EnumDescriptorProto) {
rawDesc, idxs := EnumRawDescriptor(e)
if rawDesc == nil || idxs == nil {
return nil, nil
}
Expand All @@ -168,11 +168,11 @@ func EnumDescriptorProtoOf(e GeneratedEnum) (*descriptorpb.FileDescriptorProto,
return fd, ed
}

// MessageDescriptorProtoOf returns the file descriptor proto containing
// MessageDescriptorProto returns the file descriptor proto containing
// the message and the message descriptor proto for the message itself.
// The returned proto messages must not be mutated.
func MessageDescriptorProtoOf(m GeneratedMessage) (*descriptorpb.FileDescriptorProto, *descriptorpb.DescriptorProto) {
rawDesc, idxs := MessageRawDescriptorOf(m)
func MessageDescriptorProto(m proto.GeneratedMessage) (*descriptorpb.FileDescriptorProto, *descriptorpb.DescriptorProto) {
rawDesc, idxs := MessageRawDescriptor(m)
if rawDesc == nil || idxs == nil {
return nil, nil
}
Expand Down
12 changes: 6 additions & 6 deletions descriptor/descriptor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import (
descpb "github.com/golang/protobuf/protoc-gen-go/descriptor"
)

func TestEnumDescriptorOf(t *testing.T) {
func TestEnumDescriptor(t *testing.T) {
tests := []struct {
enum protoreflect.Enum
idxs []int
Expand All @@ -37,19 +37,19 @@ func TestEnumDescriptorOf(t *testing.T) {
for _, tt := range tests {
e := struct{ protoreflect.Enum }{tt.enum} // v2-only enum

_, idxs := EnumRawDescriptorOf(e)
_, idxs := EnumRawDescriptor(e)
if diff := cmp.Diff(tt.idxs, idxs); diff != "" {
t.Errorf("path index mismatch (-want +got):\n%v", diff)
}

_, ed := EnumDescriptorProtoOf(e)
_, ed := EnumDescriptorProto(e)
if ed.GetName() != tt.name {
t.Errorf("mismatching enum name: got %v, want %v", ed.GetName(), tt.name)
}
}
}

func TestMessageDescriptorOf(t *testing.T) {
func TestMessageDescriptor(t *testing.T) {
tests := []struct {
message protoreflect.ProtoMessage
idxs []int
Expand All @@ -72,12 +72,12 @@ func TestMessageDescriptorOf(t *testing.T) {
for _, tt := range tests {
m := struct{ protoreflect.ProtoMessage }{tt.message} // v2-only message

_, idxs := MessageRawDescriptorOf(m)
_, idxs := MessageRawDescriptor(m)
if diff := cmp.Diff(tt.idxs, idxs); diff != "" {
t.Errorf("path index mismatch (-want +got):\n%v", diff)
}

_, md := MessageDescriptorProtoOf(m)
_, md := MessageDescriptorProto(m)
if md.GetName() != tt.name {
t.Errorf("mismatching message name: got %v, want %v", md.GetName(), tt.name)
}
Expand Down
48 changes: 43 additions & 5 deletions proto/proto.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
package proto

import (
"google.golang.org/protobuf/proto"
protoV2 "google.golang.org/protobuf/proto"
"google.golang.org/protobuf/reflect/protoreflect"
"google.golang.org/protobuf/runtime/protoiface"
"google.golang.org/protobuf/runtime/protoimpl"
Expand All @@ -26,9 +26,47 @@ const (
ProtoPackageIsVersion4 = true
)

// GeneratedEnum is any enum type generated by protoc-gen-go
// which is a named int32 kind.
// This type exists for documentation purposes.
type GeneratedEnum interface{}

// GeneratedMessage is any message type generated by protoc-gen-go
// which is a pointer to a named struct kind.
// This type exists for documentation purposes.
type GeneratedMessage interface{}

// Message is a protocol buffer message.
//
// This is the v1 version of the message interface and is marginally better
// than an empty interface as it lacks any method to programatically interact
// with the contents of the message.
//
// A v2 message is declared in "google.golang.org/protobuf/proto".Message and
// exposes protobuf reflection as a first-class feature of the interface.
//
// To convert a v1 message to a v2 message, use the MessageV2 function.
// To convert a v2 message to a v1 message, use the MessageV1 function.
type Message = protoiface.MessageV1

// MessageV1 converts either a v1 or v2 message to a v1 message.
// It returns nil if m is nil.
func MessageV1(m GeneratedMessage) protoiface.MessageV1 {
return protoimpl.X.ProtoMessageV1Of(m)
}

// MessageV2 converts either a v1 or v2 message to a v2 message.
// It returns nil if m is nil.
func MessageV2(m GeneratedMessage) protoV2.Message {
return protoimpl.X.ProtoMessageV2Of(m)
}

// MessageReflect returns a reflective view for a message.
// It returns nil if m is nil.
func MessageReflect(m Message) protoreflect.Message {
return protoimpl.X.MessageOf(m)
}

// Marshaler is implemented by messages that can marshal themselves.
// This interface is used by the following functions: Size, Marshal,
// Buffer.Marshal, and Buffer.EncodeMessage.
Expand Down Expand Up @@ -80,8 +118,8 @@ func (e *RequiredNotSetError) RequiredNotSet() bool {
return true
}

func checkRequiredNotSet(m proto.Message) error {
if err := proto.IsInitialized(m); err != nil {
func checkRequiredNotSet(m protoV2.Message) error {
if err := protoV2.IsInitialized(m); err != nil {
return &RequiredNotSetError{err: err}
}
return nil
Expand Down Expand Up @@ -113,7 +151,7 @@ func Merge(dst, src Message) {
m.Merge(src)
return
}
proto.Merge(
protoV2.Merge(
protoimpl.X.ProtoMessageV2Of(dst),
protoimpl.X.ProtoMessageV2Of(src),
)
Expand All @@ -135,7 +173,7 @@ func Merge(dst, src Message) {
// Maps are equal if they have the same set of keys, where the pair of values
// for each key is also equal.
func Equal(x, y Message) bool {
return proto.Equal(
return protoV2.Equal(
protoimpl.X.ProtoMessageV2Of(x),
protoimpl.X.ProtoMessageV2Of(y),
)
Expand Down

0 comments on commit 0fd14f9

Please sign in to comment.