1*1c12ee1eSDan Willemsen// Copyright 2019 The Go Authors. All rights reserved. 2*1c12ee1eSDan Willemsen// Use of this source code is governed by a BSD-style 3*1c12ee1eSDan Willemsen// license that can be found in the LICENSE file. 4*1c12ee1eSDan Willemsen 5*1c12ee1eSDan Willemsen// Package msgfmt implements a text marshaler combining the desirable features 6*1c12ee1eSDan Willemsen// of both the JSON and proto text formats. 7*1c12ee1eSDan Willemsen// It is optimized for human readability and has no associated deserializer. 8*1c12ee1eSDan Willemsenpackage msgfmt 9*1c12ee1eSDan Willemsen 10*1c12ee1eSDan Willemsenimport ( 11*1c12ee1eSDan Willemsen "bytes" 12*1c12ee1eSDan Willemsen "fmt" 13*1c12ee1eSDan Willemsen "reflect" 14*1c12ee1eSDan Willemsen "sort" 15*1c12ee1eSDan Willemsen "strconv" 16*1c12ee1eSDan Willemsen "strings" 17*1c12ee1eSDan Willemsen "time" 18*1c12ee1eSDan Willemsen 19*1c12ee1eSDan Willemsen "google.golang.org/protobuf/encoding/protowire" 20*1c12ee1eSDan Willemsen "google.golang.org/protobuf/internal/detrand" 21*1c12ee1eSDan Willemsen "google.golang.org/protobuf/internal/genid" 22*1c12ee1eSDan Willemsen "google.golang.org/protobuf/internal/order" 23*1c12ee1eSDan Willemsen "google.golang.org/protobuf/proto" 24*1c12ee1eSDan Willemsen "google.golang.org/protobuf/reflect/protoreflect" 25*1c12ee1eSDan Willemsen "google.golang.org/protobuf/reflect/protoregistry" 26*1c12ee1eSDan Willemsen) 27*1c12ee1eSDan Willemsen 28*1c12ee1eSDan Willemsen// Format returns a formatted string for the message. 29*1c12ee1eSDan Willemsenfunc Format(m proto.Message) string { 30*1c12ee1eSDan Willemsen return string(appendMessage(nil, m.ProtoReflect())) 31*1c12ee1eSDan Willemsen} 32*1c12ee1eSDan Willemsen 33*1c12ee1eSDan Willemsen// FormatValue returns a formatted string for an arbitrary value. 34*1c12ee1eSDan Willemsenfunc FormatValue(v protoreflect.Value, fd protoreflect.FieldDescriptor) string { 35*1c12ee1eSDan Willemsen return string(appendValue(nil, v, fd)) 36*1c12ee1eSDan Willemsen} 37*1c12ee1eSDan Willemsen 38*1c12ee1eSDan Willemsenfunc appendValue(b []byte, v protoreflect.Value, fd protoreflect.FieldDescriptor) []byte { 39*1c12ee1eSDan Willemsen switch v := v.Interface().(type) { 40*1c12ee1eSDan Willemsen case nil: 41*1c12ee1eSDan Willemsen return append(b, "<invalid>"...) 42*1c12ee1eSDan Willemsen case bool, int32, int64, uint32, uint64, float32, float64: 43*1c12ee1eSDan Willemsen return append(b, fmt.Sprint(v)...) 44*1c12ee1eSDan Willemsen case string: 45*1c12ee1eSDan Willemsen return append(b, strconv.Quote(string(v))...) 46*1c12ee1eSDan Willemsen case []byte: 47*1c12ee1eSDan Willemsen return append(b, strconv.Quote(string(v))...) 48*1c12ee1eSDan Willemsen case protoreflect.EnumNumber: 49*1c12ee1eSDan Willemsen return appendEnum(b, v, fd) 50*1c12ee1eSDan Willemsen case protoreflect.Message: 51*1c12ee1eSDan Willemsen return appendMessage(b, v) 52*1c12ee1eSDan Willemsen case protoreflect.List: 53*1c12ee1eSDan Willemsen return appendList(b, v, fd) 54*1c12ee1eSDan Willemsen case protoreflect.Map: 55*1c12ee1eSDan Willemsen return appendMap(b, v, fd) 56*1c12ee1eSDan Willemsen default: 57*1c12ee1eSDan Willemsen panic(fmt.Sprintf("invalid type: %T", v)) 58*1c12ee1eSDan Willemsen } 59*1c12ee1eSDan Willemsen} 60*1c12ee1eSDan Willemsen 61*1c12ee1eSDan Willemsenfunc appendEnum(b []byte, v protoreflect.EnumNumber, fd protoreflect.FieldDescriptor) []byte { 62*1c12ee1eSDan Willemsen if fd != nil { 63*1c12ee1eSDan Willemsen if ev := fd.Enum().Values().ByNumber(v); ev != nil { 64*1c12ee1eSDan Willemsen return append(b, ev.Name()...) 65*1c12ee1eSDan Willemsen } 66*1c12ee1eSDan Willemsen } 67*1c12ee1eSDan Willemsen return strconv.AppendInt(b, int64(v), 10) 68*1c12ee1eSDan Willemsen} 69*1c12ee1eSDan Willemsen 70*1c12ee1eSDan Willemsenfunc appendMessage(b []byte, m protoreflect.Message) []byte { 71*1c12ee1eSDan Willemsen if b2 := appendKnownMessage(b, m); b2 != nil { 72*1c12ee1eSDan Willemsen return b2 73*1c12ee1eSDan Willemsen } 74*1c12ee1eSDan Willemsen 75*1c12ee1eSDan Willemsen b = append(b, '{') 76*1c12ee1eSDan Willemsen order.RangeFields(m, order.IndexNameFieldOrder, func(fd protoreflect.FieldDescriptor, v protoreflect.Value) bool { 77*1c12ee1eSDan Willemsen b = append(b, fd.TextName()...) 78*1c12ee1eSDan Willemsen b = append(b, ':') 79*1c12ee1eSDan Willemsen b = appendValue(b, v, fd) 80*1c12ee1eSDan Willemsen b = append(b, delim()...) 81*1c12ee1eSDan Willemsen return true 82*1c12ee1eSDan Willemsen }) 83*1c12ee1eSDan Willemsen b = appendUnknown(b, m.GetUnknown()) 84*1c12ee1eSDan Willemsen b = bytes.TrimRight(b, delim()) 85*1c12ee1eSDan Willemsen b = append(b, '}') 86*1c12ee1eSDan Willemsen return b 87*1c12ee1eSDan Willemsen} 88*1c12ee1eSDan Willemsen 89*1c12ee1eSDan Willemsenvar protocmpMessageType = reflect.TypeOf(map[string]interface{}(nil)) 90*1c12ee1eSDan Willemsen 91*1c12ee1eSDan Willemsenfunc appendKnownMessage(b []byte, m protoreflect.Message) []byte { 92*1c12ee1eSDan Willemsen md := m.Descriptor() 93*1c12ee1eSDan Willemsen fds := md.Fields() 94*1c12ee1eSDan Willemsen switch md.FullName() { 95*1c12ee1eSDan Willemsen case genid.Any_message_fullname: 96*1c12ee1eSDan Willemsen var msgVal protoreflect.Message 97*1c12ee1eSDan Willemsen url := m.Get(fds.ByNumber(genid.Any_TypeUrl_field_number)).String() 98*1c12ee1eSDan Willemsen if v := reflect.ValueOf(m); v.Type().ConvertibleTo(protocmpMessageType) { 99*1c12ee1eSDan Willemsen // For protocmp.Message, directly obtain the sub-message value 100*1c12ee1eSDan Willemsen // which is stored in structured form, rather than as raw bytes. 101*1c12ee1eSDan Willemsen m2 := v.Convert(protocmpMessageType).Interface().(map[string]interface{}) 102*1c12ee1eSDan Willemsen v, ok := m2[string(genid.Any_Value_field_name)].(proto.Message) 103*1c12ee1eSDan Willemsen if !ok { 104*1c12ee1eSDan Willemsen return nil 105*1c12ee1eSDan Willemsen } 106*1c12ee1eSDan Willemsen msgVal = v.ProtoReflect() 107*1c12ee1eSDan Willemsen } else { 108*1c12ee1eSDan Willemsen val := m.Get(fds.ByNumber(genid.Any_Value_field_number)).Bytes() 109*1c12ee1eSDan Willemsen mt, err := protoregistry.GlobalTypes.FindMessageByURL(url) 110*1c12ee1eSDan Willemsen if err != nil { 111*1c12ee1eSDan Willemsen return nil 112*1c12ee1eSDan Willemsen } 113*1c12ee1eSDan Willemsen msgVal = mt.New() 114*1c12ee1eSDan Willemsen err = proto.UnmarshalOptions{AllowPartial: true}.Unmarshal(val, msgVal.Interface()) 115*1c12ee1eSDan Willemsen if err != nil { 116*1c12ee1eSDan Willemsen return nil 117*1c12ee1eSDan Willemsen } 118*1c12ee1eSDan Willemsen } 119*1c12ee1eSDan Willemsen 120*1c12ee1eSDan Willemsen b = append(b, '{') 121*1c12ee1eSDan Willemsen b = append(b, "["+url+"]"...) 122*1c12ee1eSDan Willemsen b = append(b, ':') 123*1c12ee1eSDan Willemsen b = appendMessage(b, msgVal) 124*1c12ee1eSDan Willemsen b = append(b, '}') 125*1c12ee1eSDan Willemsen return b 126*1c12ee1eSDan Willemsen 127*1c12ee1eSDan Willemsen case genid.Timestamp_message_fullname: 128*1c12ee1eSDan Willemsen secs := m.Get(fds.ByNumber(genid.Timestamp_Seconds_field_number)).Int() 129*1c12ee1eSDan Willemsen nanos := m.Get(fds.ByNumber(genid.Timestamp_Nanos_field_number)).Int() 130*1c12ee1eSDan Willemsen if nanos < 0 || nanos >= 1e9 { 131*1c12ee1eSDan Willemsen return nil 132*1c12ee1eSDan Willemsen } 133*1c12ee1eSDan Willemsen t := time.Unix(secs, nanos).UTC() 134*1c12ee1eSDan Willemsen x := t.Format("2006-01-02T15:04:05.000000000") // RFC 3339 135*1c12ee1eSDan Willemsen x = strings.TrimSuffix(x, "000") 136*1c12ee1eSDan Willemsen x = strings.TrimSuffix(x, "000") 137*1c12ee1eSDan Willemsen x = strings.TrimSuffix(x, ".000") 138*1c12ee1eSDan Willemsen return append(b, x+"Z"...) 139*1c12ee1eSDan Willemsen 140*1c12ee1eSDan Willemsen case genid.Duration_message_fullname: 141*1c12ee1eSDan Willemsen sign := "" 142*1c12ee1eSDan Willemsen secs := m.Get(fds.ByNumber(genid.Duration_Seconds_field_number)).Int() 143*1c12ee1eSDan Willemsen nanos := m.Get(fds.ByNumber(genid.Duration_Nanos_field_number)).Int() 144*1c12ee1eSDan Willemsen if nanos <= -1e9 || nanos >= 1e9 || (secs > 0 && nanos < 0) || (secs < 0 && nanos > 0) { 145*1c12ee1eSDan Willemsen return nil 146*1c12ee1eSDan Willemsen } 147*1c12ee1eSDan Willemsen if secs < 0 || nanos < 0 { 148*1c12ee1eSDan Willemsen sign, secs, nanos = "-", -1*secs, -1*nanos 149*1c12ee1eSDan Willemsen } 150*1c12ee1eSDan Willemsen x := fmt.Sprintf("%s%d.%09d", sign, secs, nanos) 151*1c12ee1eSDan Willemsen x = strings.TrimSuffix(x, "000") 152*1c12ee1eSDan Willemsen x = strings.TrimSuffix(x, "000") 153*1c12ee1eSDan Willemsen x = strings.TrimSuffix(x, ".000") 154*1c12ee1eSDan Willemsen return append(b, x+"s"...) 155*1c12ee1eSDan Willemsen 156*1c12ee1eSDan Willemsen case genid.BoolValue_message_fullname, 157*1c12ee1eSDan Willemsen genid.Int32Value_message_fullname, 158*1c12ee1eSDan Willemsen genid.Int64Value_message_fullname, 159*1c12ee1eSDan Willemsen genid.UInt32Value_message_fullname, 160*1c12ee1eSDan Willemsen genid.UInt64Value_message_fullname, 161*1c12ee1eSDan Willemsen genid.FloatValue_message_fullname, 162*1c12ee1eSDan Willemsen genid.DoubleValue_message_fullname, 163*1c12ee1eSDan Willemsen genid.StringValue_message_fullname, 164*1c12ee1eSDan Willemsen genid.BytesValue_message_fullname: 165*1c12ee1eSDan Willemsen fd := fds.ByNumber(genid.WrapperValue_Value_field_number) 166*1c12ee1eSDan Willemsen return appendValue(b, m.Get(fd), fd) 167*1c12ee1eSDan Willemsen } 168*1c12ee1eSDan Willemsen 169*1c12ee1eSDan Willemsen return nil 170*1c12ee1eSDan Willemsen} 171*1c12ee1eSDan Willemsen 172*1c12ee1eSDan Willemsenfunc appendUnknown(b []byte, raw protoreflect.RawFields) []byte { 173*1c12ee1eSDan Willemsen rs := make(map[protoreflect.FieldNumber][]protoreflect.RawFields) 174*1c12ee1eSDan Willemsen for len(raw) > 0 { 175*1c12ee1eSDan Willemsen num, _, n := protowire.ConsumeField(raw) 176*1c12ee1eSDan Willemsen rs[num] = append(rs[num], raw[:n]) 177*1c12ee1eSDan Willemsen raw = raw[n:] 178*1c12ee1eSDan Willemsen } 179*1c12ee1eSDan Willemsen 180*1c12ee1eSDan Willemsen var ns []protoreflect.FieldNumber 181*1c12ee1eSDan Willemsen for n := range rs { 182*1c12ee1eSDan Willemsen ns = append(ns, n) 183*1c12ee1eSDan Willemsen } 184*1c12ee1eSDan Willemsen sort.Slice(ns, func(i, j int) bool { return ns[i] < ns[j] }) 185*1c12ee1eSDan Willemsen 186*1c12ee1eSDan Willemsen for _, n := range ns { 187*1c12ee1eSDan Willemsen var leftBracket, rightBracket string 188*1c12ee1eSDan Willemsen if len(rs[n]) > 1 { 189*1c12ee1eSDan Willemsen leftBracket, rightBracket = "[", "]" 190*1c12ee1eSDan Willemsen } 191*1c12ee1eSDan Willemsen 192*1c12ee1eSDan Willemsen b = strconv.AppendInt(b, int64(n), 10) 193*1c12ee1eSDan Willemsen b = append(b, ':') 194*1c12ee1eSDan Willemsen b = append(b, leftBracket...) 195*1c12ee1eSDan Willemsen for _, r := range rs[n] { 196*1c12ee1eSDan Willemsen num, typ, n := protowire.ConsumeTag(r) 197*1c12ee1eSDan Willemsen r = r[n:] 198*1c12ee1eSDan Willemsen switch typ { 199*1c12ee1eSDan Willemsen case protowire.VarintType: 200*1c12ee1eSDan Willemsen v, _ := protowire.ConsumeVarint(r) 201*1c12ee1eSDan Willemsen b = strconv.AppendInt(b, int64(v), 10) 202*1c12ee1eSDan Willemsen case protowire.Fixed32Type: 203*1c12ee1eSDan Willemsen v, _ := protowire.ConsumeFixed32(r) 204*1c12ee1eSDan Willemsen b = append(b, fmt.Sprintf("0x%08x", v)...) 205*1c12ee1eSDan Willemsen case protowire.Fixed64Type: 206*1c12ee1eSDan Willemsen v, _ := protowire.ConsumeFixed64(r) 207*1c12ee1eSDan Willemsen b = append(b, fmt.Sprintf("0x%016x", v)...) 208*1c12ee1eSDan Willemsen case protowire.BytesType: 209*1c12ee1eSDan Willemsen v, _ := protowire.ConsumeBytes(r) 210*1c12ee1eSDan Willemsen b = strconv.AppendQuote(b, string(v)) 211*1c12ee1eSDan Willemsen case protowire.StartGroupType: 212*1c12ee1eSDan Willemsen v, _ := protowire.ConsumeGroup(num, r) 213*1c12ee1eSDan Willemsen b = append(b, '{') 214*1c12ee1eSDan Willemsen b = appendUnknown(b, v) 215*1c12ee1eSDan Willemsen b = bytes.TrimRight(b, delim()) 216*1c12ee1eSDan Willemsen b = append(b, '}') 217*1c12ee1eSDan Willemsen default: 218*1c12ee1eSDan Willemsen panic(fmt.Sprintf("invalid type: %v", typ)) 219*1c12ee1eSDan Willemsen } 220*1c12ee1eSDan Willemsen b = append(b, delim()...) 221*1c12ee1eSDan Willemsen } 222*1c12ee1eSDan Willemsen b = bytes.TrimRight(b, delim()) 223*1c12ee1eSDan Willemsen b = append(b, rightBracket...) 224*1c12ee1eSDan Willemsen b = append(b, delim()...) 225*1c12ee1eSDan Willemsen } 226*1c12ee1eSDan Willemsen return b 227*1c12ee1eSDan Willemsen} 228*1c12ee1eSDan Willemsen 229*1c12ee1eSDan Willemsenfunc appendList(b []byte, v protoreflect.List, fd protoreflect.FieldDescriptor) []byte { 230*1c12ee1eSDan Willemsen b = append(b, '[') 231*1c12ee1eSDan Willemsen for i := 0; i < v.Len(); i++ { 232*1c12ee1eSDan Willemsen b = appendValue(b, v.Get(i), fd) 233*1c12ee1eSDan Willemsen b = append(b, delim()...) 234*1c12ee1eSDan Willemsen } 235*1c12ee1eSDan Willemsen b = bytes.TrimRight(b, delim()) 236*1c12ee1eSDan Willemsen b = append(b, ']') 237*1c12ee1eSDan Willemsen return b 238*1c12ee1eSDan Willemsen} 239*1c12ee1eSDan Willemsen 240*1c12ee1eSDan Willemsenfunc appendMap(b []byte, v protoreflect.Map, fd protoreflect.FieldDescriptor) []byte { 241*1c12ee1eSDan Willemsen b = append(b, '{') 242*1c12ee1eSDan Willemsen order.RangeEntries(v, order.GenericKeyOrder, func(k protoreflect.MapKey, v protoreflect.Value) bool { 243*1c12ee1eSDan Willemsen b = appendValue(b, k.Value(), fd.MapKey()) 244*1c12ee1eSDan Willemsen b = append(b, ':') 245*1c12ee1eSDan Willemsen b = appendValue(b, v, fd.MapValue()) 246*1c12ee1eSDan Willemsen b = append(b, delim()...) 247*1c12ee1eSDan Willemsen return true 248*1c12ee1eSDan Willemsen }) 249*1c12ee1eSDan Willemsen b = bytes.TrimRight(b, delim()) 250*1c12ee1eSDan Willemsen b = append(b, '}') 251*1c12ee1eSDan Willemsen return b 252*1c12ee1eSDan Willemsen} 253*1c12ee1eSDan Willemsen 254*1c12ee1eSDan Willemsenfunc delim() string { 255*1c12ee1eSDan Willemsen // Deliberately introduce instability into the message string to 256*1c12ee1eSDan Willemsen // discourage users from depending on it. 257*1c12ee1eSDan Willemsen if detrand.Bool() { 258*1c12ee1eSDan Willemsen return " " 259*1c12ee1eSDan Willemsen } 260*1c12ee1eSDan Willemsen return ", " 261*1c12ee1eSDan Willemsen} 262