xref: /aosp_15_r20/external/golang-protobuf/internal/msgfmt/format.go (revision 1c12ee1efe575feb122dbf939ff15148a3b3e8f2)
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