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