blob: 83b65a66615193bdcf8003aaaecf6a94355f6133 [file] [log] [blame]
Scott Baker105df152020-04-13 15:55:14 -07001// Copyright 2018 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package prototext
6
7import (
8 "fmt"
9 "sort"
10 "strconv"
11 "unicode/utf8"
12
13 "google.golang.org/protobuf/encoding/protowire"
14 "google.golang.org/protobuf/internal/encoding/messageset"
15 "google.golang.org/protobuf/internal/encoding/text"
16 "google.golang.org/protobuf/internal/errors"
17 "google.golang.org/protobuf/internal/fieldnum"
18 "google.golang.org/protobuf/internal/flags"
19 "google.golang.org/protobuf/internal/mapsort"
20 "google.golang.org/protobuf/internal/pragma"
21 "google.golang.org/protobuf/proto"
22 pref "google.golang.org/protobuf/reflect/protoreflect"
23 "google.golang.org/protobuf/reflect/protoregistry"
24)
25
26const defaultIndent = " "
27
28// Format formats the message as a multiline string.
29// This function is only intended for human consumption and ignores errors.
30// Do not depend on the output being stable. It may change over time across
31// different versions of the program.
32func Format(m proto.Message) string {
33 return MarshalOptions{Multiline: true}.Format(m)
34}
35
36// Marshal writes the given proto.Message in textproto format using default
37// options. Do not depend on the output being stable. It may change over time
38// across different versions of the program.
39func Marshal(m proto.Message) ([]byte, error) {
40 return MarshalOptions{}.Marshal(m)
41}
42
43// MarshalOptions is a configurable text format marshaler.
44type MarshalOptions struct {
45 pragma.NoUnkeyedLiterals
46
47 // Multiline specifies whether the marshaler should format the output in
48 // indented-form with every textual element on a new line.
49 // If Indent is an empty string, then an arbitrary indent is chosen.
50 Multiline bool
51
52 // Indent specifies the set of indentation characters to use in a multiline
53 // formatted output such that every entry is preceded by Indent and
54 // terminated by a newline. If non-empty, then Multiline is treated as true.
55 // Indent can only be composed of space or tab characters.
56 Indent string
57
58 // AllowPartial allows messages that have missing required fields to marshal
59 // without returning an error. If AllowPartial is false (the default),
60 // Marshal will return error if there are any missing required fields.
61 AllowPartial bool
62
63 // EmitUnknown specifies whether to emit unknown fields in the output.
64 // If specified, the unmarshaler may be unable to parse the output.
65 // The default is to exclude unknown fields.
66 EmitUnknown bool
67
68 // Resolver is used for looking up types when expanding google.protobuf.Any
69 // messages. If nil, this defaults to using protoregistry.GlobalTypes.
70 Resolver interface {
71 protoregistry.ExtensionTypeResolver
72 protoregistry.MessageTypeResolver
73 }
74}
75
76// Format formats the message as a string.
77// This method is only intended for human consumption and ignores errors.
78// Do not depend on the output being stable. It may change over time across
79// different versions of the program.
80func (o MarshalOptions) Format(m proto.Message) string {
81 if m == nil || !m.ProtoReflect().IsValid() {
82 return "<nil>" // invalid syntax, but okay since this is for debugging
83 }
84 o.AllowPartial = true
85 o.EmitUnknown = true
86 b, _ := o.Marshal(m)
87 return string(b)
88}
89
90// Marshal writes the given proto.Message in textproto format using options in
91// MarshalOptions object. Do not depend on the output being stable. It may
92// change over time across different versions of the program.
93func (o MarshalOptions) Marshal(m proto.Message) ([]byte, error) {
94 const outputASCII = false
95 var delims = [2]byte{'{', '}'}
96
97 if o.Multiline && o.Indent == "" {
98 o.Indent = defaultIndent
99 }
100 if o.Resolver == nil {
101 o.Resolver = protoregistry.GlobalTypes
102 }
103
104 internalEnc, err := text.NewEncoder(o.Indent, delims, outputASCII)
105 if err != nil {
106 return nil, err
107 }
108
109 enc := encoder{internalEnc, o}
110 err = enc.marshalMessage(m.ProtoReflect(), false)
111 if err != nil {
112 return nil, err
113 }
114 out := enc.Bytes()
115 if len(o.Indent) > 0 && len(out) > 0 {
116 out = append(out, '\n')
117 }
118 if o.AllowPartial {
119 return out, nil
120 }
121 return out, proto.CheckInitialized(m)
122}
123
124type encoder struct {
125 *text.Encoder
126 opts MarshalOptions
127}
128
129// marshalMessage marshals the given protoreflect.Message.
130func (e encoder) marshalMessage(m pref.Message, inclDelims bool) error {
131 messageDesc := m.Descriptor()
132 if !flags.ProtoLegacy && messageset.IsMessageSet(messageDesc) {
133 return errors.New("no support for proto1 MessageSets")
134 }
135
136 if inclDelims {
137 e.StartMessage()
138 defer e.EndMessage()
139 }
140
141 // Handle Any expansion.
142 if messageDesc.FullName() == "google.protobuf.Any" {
143 if e.marshalAny(m) {
144 return nil
145 }
146 // If unable to expand, continue on to marshal Any as a regular message.
147 }
148
149 // Marshal known fields.
150 fieldDescs := messageDesc.Fields()
151 size := fieldDescs.Len()
152 for i := 0; i < size; {
153 fd := fieldDescs.Get(i)
154 if od := fd.ContainingOneof(); od != nil {
155 fd = m.WhichOneof(od)
156 i += od.Fields().Len()
157 } else {
158 i++
159 }
160
161 if fd == nil || !m.Has(fd) {
162 continue
163 }
164
165 name := fd.Name()
166 // Use type name for group field name.
167 if fd.Kind() == pref.GroupKind {
168 name = fd.Message().Name()
169 }
170 val := m.Get(fd)
171 if err := e.marshalField(string(name), val, fd); err != nil {
172 return err
173 }
174 }
175
176 // Marshal extensions.
177 if err := e.marshalExtensions(m); err != nil {
178 return err
179 }
180
181 // Marshal unknown fields.
182 if e.opts.EmitUnknown {
183 e.marshalUnknown(m.GetUnknown())
184 }
185
186 return nil
187}
188
189// marshalField marshals the given field with protoreflect.Value.
190func (e encoder) marshalField(name string, val pref.Value, fd pref.FieldDescriptor) error {
191 switch {
192 case fd.IsList():
193 return e.marshalList(name, val.List(), fd)
194 case fd.IsMap():
195 return e.marshalMap(name, val.Map(), fd)
196 default:
197 e.WriteName(name)
198 return e.marshalSingular(val, fd)
199 }
200}
201
202// marshalSingular marshals the given non-repeated field value. This includes
203// all scalar types, enums, messages, and groups.
204func (e encoder) marshalSingular(val pref.Value, fd pref.FieldDescriptor) error {
205 kind := fd.Kind()
206 switch kind {
207 case pref.BoolKind:
208 e.WriteBool(val.Bool())
209
210 case pref.StringKind:
211 s := val.String()
212 if !utf8.ValidString(s) {
213 return errors.InvalidUTF8(string(fd.FullName()))
214 }
215 e.WriteString(s)
216
217 case pref.Int32Kind, pref.Int64Kind,
218 pref.Sint32Kind, pref.Sint64Kind,
219 pref.Sfixed32Kind, pref.Sfixed64Kind:
220 e.WriteInt(val.Int())
221
222 case pref.Uint32Kind, pref.Uint64Kind,
223 pref.Fixed32Kind, pref.Fixed64Kind:
224 e.WriteUint(val.Uint())
225
226 case pref.FloatKind:
227 // Encoder.WriteFloat handles the special numbers NaN and infinites.
228 e.WriteFloat(val.Float(), 32)
229
230 case pref.DoubleKind:
231 // Encoder.WriteFloat handles the special numbers NaN and infinites.
232 e.WriteFloat(val.Float(), 64)
233
234 case pref.BytesKind:
235 e.WriteString(string(val.Bytes()))
236
237 case pref.EnumKind:
238 num := val.Enum()
239 if desc := fd.Enum().Values().ByNumber(num); desc != nil {
240 e.WriteLiteral(string(desc.Name()))
241 } else {
242 // Use numeric value if there is no enum description.
243 e.WriteInt(int64(num))
244 }
245
246 case pref.MessageKind, pref.GroupKind:
247 return e.marshalMessage(val.Message(), true)
248
249 default:
250 panic(fmt.Sprintf("%v has unknown kind: %v", fd.FullName(), kind))
251 }
252 return nil
253}
254
255// marshalList marshals the given protoreflect.List as multiple name-value fields.
256func (e encoder) marshalList(name string, list pref.List, fd pref.FieldDescriptor) error {
257 size := list.Len()
258 for i := 0; i < size; i++ {
259 e.WriteName(name)
260 if err := e.marshalSingular(list.Get(i), fd); err != nil {
261 return err
262 }
263 }
264 return nil
265}
266
267// marshalMap marshals the given protoreflect.Map as multiple name-value fields.
268func (e encoder) marshalMap(name string, mmap pref.Map, fd pref.FieldDescriptor) error {
269 var err error
270 mapsort.Range(mmap, fd.MapKey().Kind(), func(key pref.MapKey, val pref.Value) bool {
271 e.WriteName(name)
272 e.StartMessage()
273 defer e.EndMessage()
274
275 e.WriteName("key")
276 err = e.marshalSingular(key.Value(), fd.MapKey())
277 if err != nil {
278 return false
279 }
280
281 e.WriteName("value")
282 err = e.marshalSingular(val, fd.MapValue())
283 if err != nil {
284 return false
285 }
286 return true
287 })
288 return err
289}
290
291// marshalExtensions marshals extension fields.
292func (e encoder) marshalExtensions(m pref.Message) error {
293 type entry struct {
294 key string
295 value pref.Value
296 desc pref.FieldDescriptor
297 }
298
299 // Get a sorted list based on field key first.
300 var entries []entry
301 m.Range(func(fd pref.FieldDescriptor, v pref.Value) bool {
302 if !fd.IsExtension() {
303 return true
304 }
305 // For MessageSet extensions, the name used is the parent message.
306 name := fd.FullName()
307 if messageset.IsMessageSetExtension(fd) {
308 name = name.Parent()
309 }
310 entries = append(entries, entry{
311 key: string(name),
312 value: v,
313 desc: fd,
314 })
315 return true
316 })
317 // Sort extensions lexicographically.
318 sort.Slice(entries, func(i, j int) bool {
319 return entries[i].key < entries[j].key
320 })
321
322 // Write out sorted list.
323 for _, entry := range entries {
324 // Extension field name is the proto field name enclosed in [].
325 name := "[" + entry.key + "]"
326 if err := e.marshalField(name, entry.value, entry.desc); err != nil {
327 return err
328 }
329 }
330 return nil
331}
332
333// marshalUnknown parses the given []byte and marshals fields out.
334// This function assumes proper encoding in the given []byte.
335func (e encoder) marshalUnknown(b []byte) {
336 const dec = 10
337 const hex = 16
338 for len(b) > 0 {
339 num, wtype, n := protowire.ConsumeTag(b)
340 b = b[n:]
341 e.WriteName(strconv.FormatInt(int64(num), dec))
342
343 switch wtype {
344 case protowire.VarintType:
345 var v uint64
346 v, n = protowire.ConsumeVarint(b)
347 e.WriteUint(v)
348 case protowire.Fixed32Type:
349 var v uint32
350 v, n = protowire.ConsumeFixed32(b)
351 e.WriteLiteral("0x" + strconv.FormatUint(uint64(v), hex))
352 case protowire.Fixed64Type:
353 var v uint64
354 v, n = protowire.ConsumeFixed64(b)
355 e.WriteLiteral("0x" + strconv.FormatUint(v, hex))
356 case protowire.BytesType:
357 var v []byte
358 v, n = protowire.ConsumeBytes(b)
359 e.WriteString(string(v))
360 case protowire.StartGroupType:
361 e.StartMessage()
362 var v []byte
363 v, n = protowire.ConsumeGroup(num, b)
364 e.marshalUnknown(v)
365 e.EndMessage()
366 default:
367 panic(fmt.Sprintf("prototext: error parsing unknown field wire type: %v", wtype))
368 }
369
370 b = b[n:]
371 }
372}
373
374// marshalAny marshals the given google.protobuf.Any message in expanded form.
375// It returns true if it was able to marshal, else false.
376func (e encoder) marshalAny(any pref.Message) bool {
377 // Construct the embedded message.
378 fds := any.Descriptor().Fields()
379 fdType := fds.ByNumber(fieldnum.Any_TypeUrl)
380 typeURL := any.Get(fdType).String()
381 mt, err := e.opts.Resolver.FindMessageByURL(typeURL)
382 if err != nil {
383 return false
384 }
385 m := mt.New().Interface()
386
387 // Unmarshal bytes into embedded message.
388 fdValue := fds.ByNumber(fieldnum.Any_Value)
389 value := any.Get(fdValue)
390 err = proto.UnmarshalOptions{
391 AllowPartial: true,
392 Resolver: e.opts.Resolver,
393 }.Unmarshal(value.Bytes(), m)
394 if err != nil {
395 return false
396 }
397
398 // Get current encoder position. If marshaling fails, reset encoder output
399 // back to this position.
400 pos := e.Snapshot()
401
402 // Field name is the proto field name enclosed in [].
403 e.WriteName("[" + typeURL + "]")
404 err = e.marshalMessage(m.ProtoReflect(), true)
405 if err != nil {
406 e.Reset(pos)
407 return false
408 }
409 return true
410}