|  | // Copyright 2019 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 protocmp provides protobuf specific options for the | 
|  | // [github.com/google/go-cmp/cmp] package. | 
|  | // | 
|  | // The primary feature is the [Transform] option, which transform [proto.Message] | 
|  | // types into a [Message] map that is suitable for cmp to introspect upon. | 
|  | // All other options in this package must be used in conjunction with [Transform]. | 
|  | package protocmp | 
|  |  | 
|  | import ( | 
|  | "reflect" | 
|  | "strconv" | 
|  |  | 
|  | "github.com/google/go-cmp/cmp" | 
|  |  | 
|  | "google.golang.org/protobuf/encoding/protowire" | 
|  | "google.golang.org/protobuf/internal/genid" | 
|  | "google.golang.org/protobuf/internal/msgfmt" | 
|  | "google.golang.org/protobuf/proto" | 
|  | "google.golang.org/protobuf/reflect/protoreflect" | 
|  | "google.golang.org/protobuf/reflect/protoregistry" | 
|  | "google.golang.org/protobuf/runtime/protoiface" | 
|  | "google.golang.org/protobuf/runtime/protoimpl" | 
|  | ) | 
|  |  | 
|  | var ( | 
|  | enumV2Type    = reflect.TypeOf((*protoreflect.Enum)(nil)).Elem() | 
|  | messageV1Type = reflect.TypeOf((*protoiface.MessageV1)(nil)).Elem() | 
|  | messageV2Type = reflect.TypeOf((*proto.Message)(nil)).Elem() | 
|  | ) | 
|  |  | 
|  | // Enum is a dynamic representation of a protocol buffer enum that is | 
|  | // suitable for [cmp.Equal] and [cmp.Diff] to compare upon. | 
|  | type Enum struct { | 
|  | num protoreflect.EnumNumber | 
|  | ed  protoreflect.EnumDescriptor | 
|  | } | 
|  |  | 
|  | // Descriptor returns the enum descriptor. | 
|  | // It returns nil for a zero Enum value. | 
|  | func (e Enum) Descriptor() protoreflect.EnumDescriptor { | 
|  | return e.ed | 
|  | } | 
|  |  | 
|  | // Number returns the enum value as an integer. | 
|  | func (e Enum) Number() protoreflect.EnumNumber { | 
|  | return e.num | 
|  | } | 
|  |  | 
|  | // Equal reports whether e1 and e2 represent the same enum value. | 
|  | func (e1 Enum) Equal(e2 Enum) bool { | 
|  | if e1.ed.FullName() != e2.ed.FullName() { | 
|  | return false | 
|  | } | 
|  | return e1.num == e2.num | 
|  | } | 
|  |  | 
|  | // String returns the name of the enum value if known (e.g., "ENUM_VALUE"), | 
|  | // otherwise it returns the formatted decimal enum number (e.g., "14"). | 
|  | func (e Enum) String() string { | 
|  | if ev := e.ed.Values().ByNumber(e.num); ev != nil { | 
|  | return string(ev.Name()) | 
|  | } | 
|  | return strconv.Itoa(int(e.num)) | 
|  | } | 
|  |  | 
|  | const ( | 
|  | // messageTypeKey indicates the protobuf message type. | 
|  | // The value type is always messageMeta. | 
|  | // From the public API, it presents itself as only the type, but the | 
|  | // underlying data structure holds arbitrary metadata about the message. | 
|  | messageTypeKey = "@type" | 
|  |  | 
|  | // messageInvalidKey indicates that the message is invalid. | 
|  | // The value is always the boolean "true". | 
|  | messageInvalidKey = "@invalid" | 
|  | ) | 
|  |  | 
|  | type messageMeta struct { | 
|  | m   proto.Message | 
|  | md  protoreflect.MessageDescriptor | 
|  | xds map[string]protoreflect.ExtensionDescriptor | 
|  | } | 
|  |  | 
|  | func (t messageMeta) String() string { | 
|  | return string(t.md.FullName()) | 
|  | } | 
|  |  | 
|  | func (t1 messageMeta) Equal(t2 messageMeta) bool { | 
|  | return t1.md.FullName() == t2.md.FullName() | 
|  | } | 
|  |  | 
|  | // Message is a dynamic representation of a protocol buffer message that is | 
|  | // suitable for [cmp.Equal] and [cmp.Diff] to directly operate upon. | 
|  | // | 
|  | // Every populated known field (excluding extension fields) is stored in the map | 
|  | // with the key being the short name of the field (e.g., "field_name") and | 
|  | // the value determined by the kind and cardinality of the field. | 
|  | // | 
|  | // Singular scalars are represented by the same Go type as [protoreflect.Value], | 
|  | // singular messages are represented by the [Message] type, | 
|  | // singular enums are represented by the [Enum] type, | 
|  | // list fields are represented as a Go slice, and | 
|  | // map fields are represented as a Go map. | 
|  | // | 
|  | // Every populated extension field is stored in the map with the key being the | 
|  | // full name of the field surrounded by brackets (e.g., "[extension.full.name]") | 
|  | // and the value determined according to the same rules as known fields. | 
|  | // | 
|  | // Every unknown field is stored in the map with the key being the field number | 
|  | // encoded as a decimal string (e.g., "132") and the value being the raw bytes | 
|  | // of the encoded field (as the [protoreflect.RawFields] type). | 
|  | // | 
|  | // Message values must not be created by or mutated by users. | 
|  | type Message map[string]any | 
|  |  | 
|  | // Unwrap returns the original message value. | 
|  | // It returns nil if this Message was not constructed from another message. | 
|  | func (m Message) Unwrap() proto.Message { | 
|  | mm, _ := m[messageTypeKey].(messageMeta) | 
|  | return mm.m | 
|  | } | 
|  |  | 
|  | // Descriptor return the message descriptor. | 
|  | // It returns nil for a zero Message value. | 
|  | func (m Message) Descriptor() protoreflect.MessageDescriptor { | 
|  | mm, _ := m[messageTypeKey].(messageMeta) | 
|  | return mm.md | 
|  | } | 
|  |  | 
|  | // ProtoReflect returns a reflective view of m. | 
|  | // It only implements the read-only operations of [protoreflect.Message]. | 
|  | // Calling any mutating operations on m panics. | 
|  | func (m Message) ProtoReflect() protoreflect.Message { | 
|  | return (reflectMessage)(m) | 
|  | } | 
|  |  | 
|  | // ProtoMessage is a marker method from the legacy message interface. | 
|  | func (m Message) ProtoMessage() {} | 
|  |  | 
|  | // Reset is the required Reset method from the legacy message interface. | 
|  | func (m Message) Reset() { | 
|  | panic("invalid mutation of a read-only message") | 
|  | } | 
|  |  | 
|  | // String returns a formatted string for the message. | 
|  | // It is intended for human debugging and has no guarantees about its | 
|  | // exact format or the stability of its output. | 
|  | func (m Message) String() string { | 
|  | switch { | 
|  | case m == nil: | 
|  | return "<nil>" | 
|  | case !m.ProtoReflect().IsValid(): | 
|  | return "<invalid>" | 
|  | default: | 
|  | return msgfmt.Format(m) | 
|  | } | 
|  | } | 
|  |  | 
|  | type transformer struct { | 
|  | resolver protoregistry.MessageTypeResolver | 
|  | } | 
|  |  | 
|  | func newTransformer(opts ...option) *transformer { | 
|  | xf := &transformer{ | 
|  | resolver: protoregistry.GlobalTypes, | 
|  | } | 
|  | for _, opt := range opts { | 
|  | opt(xf) | 
|  | } | 
|  | return xf | 
|  | } | 
|  |  | 
|  | type option func(*transformer) | 
|  |  | 
|  | // MessageTypeResolver overrides the resolver used for messages packed | 
|  | // inside Any. The default is protoregistry.GlobalTypes, which is | 
|  | // sufficient for all compiled-in Protobuf messages. Overriding the | 
|  | // resolver is useful in tests that dynamically create Protobuf | 
|  | // descriptors and messages, e.g. in proxies using dynamicpb. | 
|  | func MessageTypeResolver(r protoregistry.MessageTypeResolver) option { | 
|  | return func(xf *transformer) { | 
|  | xf.resolver = r | 
|  | } | 
|  | } | 
|  |  | 
|  | // Transform returns a [cmp.Option] that converts each [proto.Message] to a [Message]. | 
|  | // The transformation does not mutate nor alias any converted messages. | 
|  | // | 
|  | // The google.protobuf.Any message is automatically unmarshaled such that the | 
|  | // "value" field is a [Message] representing the underlying message value | 
|  | // assuming it could be resolved and properly unmarshaled. | 
|  | // | 
|  | // This does not directly transform higher-order composite Go types. | 
|  | // For example, []*foopb.Message is not transformed into []Message, | 
|  | // but rather the individual message elements of the slice are transformed. | 
|  | func Transform(opts ...option) cmp.Option { | 
|  | xf := newTransformer(opts...) | 
|  |  | 
|  | // addrType returns a pointer to t if t isn't a pointer or interface. | 
|  | addrType := func(t reflect.Type) reflect.Type { | 
|  | if k := t.Kind(); k == reflect.Interface || k == reflect.Ptr { | 
|  | return t | 
|  | } | 
|  | return reflect.PtrTo(t) | 
|  | } | 
|  |  | 
|  | // TODO: Should this transform protoreflect.Enum types to Enum as well? | 
|  | return cmp.FilterPath(func(p cmp.Path) bool { | 
|  | ps := p.Last() | 
|  | if isMessageType(addrType(ps.Type())) { | 
|  | return true | 
|  | } | 
|  |  | 
|  | // Check whether the concrete values of an interface both satisfy | 
|  | // the Message interface. | 
|  | if ps.Type().Kind() == reflect.Interface { | 
|  | vx, vy := ps.Values() | 
|  | if !vx.IsValid() || vx.IsNil() || !vy.IsValid() || vy.IsNil() { | 
|  | return false | 
|  | } | 
|  | return isMessageType(addrType(vx.Elem().Type())) && isMessageType(addrType(vy.Elem().Type())) | 
|  | } | 
|  |  | 
|  | return false | 
|  | }, cmp.Transformer("protocmp.Transform", func(v any) Message { | 
|  | // For user convenience, shallow copy the message value if necessary | 
|  | // in order for it to implement the message interface. | 
|  | if rv := reflect.ValueOf(v); rv.IsValid() && rv.Kind() != reflect.Ptr && !isMessageType(rv.Type()) { | 
|  | pv := reflect.New(rv.Type()) | 
|  | pv.Elem().Set(rv) | 
|  | v = pv.Interface() | 
|  | } | 
|  |  | 
|  | m := protoimpl.X.MessageOf(v) | 
|  | switch { | 
|  | case m == nil: | 
|  | return nil | 
|  | case !m.IsValid(): | 
|  | return Message{messageTypeKey: messageMeta{m: m.Interface(), md: m.Descriptor()}, messageInvalidKey: true} | 
|  | default: | 
|  | return xf.transformMessage(m) | 
|  | } | 
|  | })) | 
|  | } | 
|  |  | 
|  | func isMessageType(t reflect.Type) bool { | 
|  | // Avoid transforming the Message itself. | 
|  | if t == reflect.TypeOf(Message(nil)) || t == reflect.TypeOf((*Message)(nil)) { | 
|  | return false | 
|  | } | 
|  | return t.Implements(messageV1Type) || t.Implements(messageV2Type) | 
|  | } | 
|  |  | 
|  | func (xf *transformer) transformMessage(m protoreflect.Message) Message { | 
|  | mx := Message{} | 
|  | mt := messageMeta{m: m.Interface(), md: m.Descriptor(), xds: make(map[string]protoreflect.FieldDescriptor)} | 
|  |  | 
|  | // Handle known and extension fields. | 
|  | m.Range(func(fd protoreflect.FieldDescriptor, v protoreflect.Value) bool { | 
|  | s := fd.TextName() | 
|  | if fd.IsExtension() { | 
|  | mt.xds[s] = fd | 
|  | } | 
|  | switch { | 
|  | case fd.IsList(): | 
|  | mx[s] = xf.transformList(fd, v.List()) | 
|  | case fd.IsMap(): | 
|  | mx[s] = xf.transformMap(fd, v.Map()) | 
|  | default: | 
|  | mx[s] = xf.transformSingular(fd, v) | 
|  | } | 
|  | return true | 
|  | }) | 
|  |  | 
|  | // Handle unknown fields. | 
|  | for b := m.GetUnknown(); len(b) > 0; { | 
|  | num, _, n := protowire.ConsumeField(b) | 
|  | s := strconv.Itoa(int(num)) | 
|  | b2, _ := mx[s].(protoreflect.RawFields) | 
|  | mx[s] = append(b2, b[:n]...) | 
|  | b = b[n:] | 
|  | } | 
|  |  | 
|  | // Expand Any messages. | 
|  | if mt.md.FullName() == genid.Any_message_fullname { | 
|  | s, _ := mx[string(genid.Any_TypeUrl_field_name)].(string) | 
|  | b, _ := mx[string(genid.Any_Value_field_name)].([]byte) | 
|  | mt, err := xf.resolver.FindMessageByURL(s) | 
|  | if mt != nil && err == nil { | 
|  | m2 := mt.New() | 
|  | err := proto.UnmarshalOptions{AllowPartial: true}.Unmarshal(b, m2.Interface()) | 
|  | if err == nil { | 
|  | mx[string(genid.Any_Value_field_name)] = xf.transformMessage(m2) | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | mx[messageTypeKey] = mt | 
|  | return mx | 
|  | } | 
|  |  | 
|  | func (xf *transformer) transformList(fd protoreflect.FieldDescriptor, lv protoreflect.List) any { | 
|  | t := protoKindToGoType(fd.Kind()) | 
|  | rv := reflect.MakeSlice(reflect.SliceOf(t), lv.Len(), lv.Len()) | 
|  | for i := 0; i < lv.Len(); i++ { | 
|  | v := reflect.ValueOf(xf.transformSingular(fd, lv.Get(i))) | 
|  | rv.Index(i).Set(v) | 
|  | } | 
|  | return rv.Interface() | 
|  | } | 
|  |  | 
|  | func (xf *transformer) transformMap(fd protoreflect.FieldDescriptor, mv protoreflect.Map) any { | 
|  | kfd := fd.MapKey() | 
|  | vfd := fd.MapValue() | 
|  | kt := protoKindToGoType(kfd.Kind()) | 
|  | vt := protoKindToGoType(vfd.Kind()) | 
|  | rv := reflect.MakeMapWithSize(reflect.MapOf(kt, vt), mv.Len()) | 
|  | mv.Range(func(k protoreflect.MapKey, v protoreflect.Value) bool { | 
|  | kv := reflect.ValueOf(xf.transformSingular(kfd, k.Value())) | 
|  | vv := reflect.ValueOf(xf.transformSingular(vfd, v)) | 
|  | rv.SetMapIndex(kv, vv) | 
|  | return true | 
|  | }) | 
|  | return rv.Interface() | 
|  | } | 
|  |  | 
|  | func (xf *transformer) transformSingular(fd protoreflect.FieldDescriptor, v protoreflect.Value) any { | 
|  | switch fd.Kind() { | 
|  | case protoreflect.EnumKind: | 
|  | return Enum{num: v.Enum(), ed: fd.Enum()} | 
|  | case protoreflect.MessageKind, protoreflect.GroupKind: | 
|  | return xf.transformMessage(v.Message()) | 
|  | case protoreflect.BytesKind: | 
|  | // The protoreflect API does not specify whether an empty bytes is | 
|  | // guaranteed to be nil or not. Always return non-nil bytes to avoid | 
|  | // leaking information about the concrete proto.Message implementation. | 
|  | if len(v.Bytes()) == 0 { | 
|  | return []byte{} | 
|  | } | 
|  | return v.Bytes() | 
|  | default: | 
|  | return v.Interface() | 
|  | } | 
|  | } | 
|  |  | 
|  | func protoKindToGoType(k protoreflect.Kind) reflect.Type { | 
|  | switch k { | 
|  | case protoreflect.BoolKind: | 
|  | return reflect.TypeOf(bool(false)) | 
|  | case protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Sfixed32Kind: | 
|  | return reflect.TypeOf(int32(0)) | 
|  | case protoreflect.Int64Kind, protoreflect.Sint64Kind, protoreflect.Sfixed64Kind: | 
|  | return reflect.TypeOf(int64(0)) | 
|  | case protoreflect.Uint32Kind, protoreflect.Fixed32Kind: | 
|  | return reflect.TypeOf(uint32(0)) | 
|  | case protoreflect.Uint64Kind, protoreflect.Fixed64Kind: | 
|  | return reflect.TypeOf(uint64(0)) | 
|  | case protoreflect.FloatKind: | 
|  | return reflect.TypeOf(float32(0)) | 
|  | case protoreflect.DoubleKind: | 
|  | return reflect.TypeOf(float64(0)) | 
|  | case protoreflect.StringKind: | 
|  | return reflect.TypeOf(string("")) | 
|  | case protoreflect.BytesKind: | 
|  | return reflect.TypeOf([]byte(nil)) | 
|  | case protoreflect.EnumKind: | 
|  | return reflect.TypeOf(Enum{}) | 
|  | case protoreflect.MessageKind, protoreflect.GroupKind: | 
|  | return reflect.TypeOf(Message{}) | 
|  | default: | 
|  | panic("invalid kind") | 
|  | } | 
|  | } |