1 | // 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 |
|
---|
5 | package prototext
|
---|
6 |
|
---|
7 | import (
|
---|
8 | "fmt"
|
---|
9 | "strconv"
|
---|
10 | "unicode/utf8"
|
---|
11 |
|
---|
12 | "google.golang.org/protobuf/encoding/protowire"
|
---|
13 | "google.golang.org/protobuf/internal/encoding/messageset"
|
---|
14 | "google.golang.org/protobuf/internal/encoding/text"
|
---|
15 | "google.golang.org/protobuf/internal/errors"
|
---|
16 | "google.golang.org/protobuf/internal/flags"
|
---|
17 | "google.golang.org/protobuf/internal/genid"
|
---|
18 | "google.golang.org/protobuf/internal/order"
|
---|
19 | "google.golang.org/protobuf/internal/pragma"
|
---|
20 | "google.golang.org/protobuf/internal/strs"
|
---|
21 | "google.golang.org/protobuf/proto"
|
---|
22 | "google.golang.org/protobuf/reflect/protoreflect"
|
---|
23 | "google.golang.org/protobuf/reflect/protoregistry"
|
---|
24 | )
|
---|
25 |
|
---|
26 | const 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.
|
---|
32 | func 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.
|
---|
39 | func Marshal(m proto.Message) ([]byte, error) {
|
---|
40 | return MarshalOptions{}.Marshal(m)
|
---|
41 | }
|
---|
42 |
|
---|
43 | // MarshalOptions is a configurable text format marshaler.
|
---|
44 | type 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 | // EmitASCII specifies whether to format strings and bytes as ASCII only
|
---|
59 | // as opposed to using UTF-8 encoding when possible.
|
---|
60 | EmitASCII bool
|
---|
61 |
|
---|
62 | // allowInvalidUTF8 specifies whether to permit the encoding of strings
|
---|
63 | // with invalid UTF-8. This is unexported as it is intended to only
|
---|
64 | // be specified by the Format method.
|
---|
65 | allowInvalidUTF8 bool
|
---|
66 |
|
---|
67 | // AllowPartial allows messages that have missing required fields to marshal
|
---|
68 | // without returning an error. If AllowPartial is false (the default),
|
---|
69 | // Marshal will return error if there are any missing required fields.
|
---|
70 | AllowPartial bool
|
---|
71 |
|
---|
72 | // EmitUnknown specifies whether to emit unknown fields in the output.
|
---|
73 | // If specified, the unmarshaler may be unable to parse the output.
|
---|
74 | // The default is to exclude unknown fields.
|
---|
75 | EmitUnknown bool
|
---|
76 |
|
---|
77 | // Resolver is used for looking up types when expanding google.protobuf.Any
|
---|
78 | // messages. If nil, this defaults to using protoregistry.GlobalTypes.
|
---|
79 | Resolver interface {
|
---|
80 | protoregistry.ExtensionTypeResolver
|
---|
81 | protoregistry.MessageTypeResolver
|
---|
82 | }
|
---|
83 | }
|
---|
84 |
|
---|
85 | // Format formats the message as a string.
|
---|
86 | // This method is only intended for human consumption and ignores errors.
|
---|
87 | // Do not depend on the output being stable. It may change over time across
|
---|
88 | // different versions of the program.
|
---|
89 | func (o MarshalOptions) Format(m proto.Message) string {
|
---|
90 | if m == nil || !m.ProtoReflect().IsValid() {
|
---|
91 | return "<nil>" // invalid syntax, but okay since this is for debugging
|
---|
92 | }
|
---|
93 | o.allowInvalidUTF8 = true
|
---|
94 | o.AllowPartial = true
|
---|
95 | o.EmitUnknown = true
|
---|
96 | b, _ := o.Marshal(m)
|
---|
97 | return string(b)
|
---|
98 | }
|
---|
99 |
|
---|
100 | // Marshal writes the given proto.Message in textproto format using options in
|
---|
101 | // MarshalOptions object. Do not depend on the output being stable. It may
|
---|
102 | // change over time across different versions of the program.
|
---|
103 | func (o MarshalOptions) Marshal(m proto.Message) ([]byte, error) {
|
---|
104 | return o.marshal(m)
|
---|
105 | }
|
---|
106 |
|
---|
107 | // marshal is a centralized function that all marshal operations go through.
|
---|
108 | // For profiling purposes, avoid changing the name of this function or
|
---|
109 | // introducing other code paths for marshal that do not go through this.
|
---|
110 | func (o MarshalOptions) marshal(m proto.Message) ([]byte, error) {
|
---|
111 | var delims = [2]byte{'{', '}'}
|
---|
112 |
|
---|
113 | if o.Multiline && o.Indent == "" {
|
---|
114 | o.Indent = defaultIndent
|
---|
115 | }
|
---|
116 | if o.Resolver == nil {
|
---|
117 | o.Resolver = protoregistry.GlobalTypes
|
---|
118 | }
|
---|
119 |
|
---|
120 | internalEnc, err := text.NewEncoder(o.Indent, delims, o.EmitASCII)
|
---|
121 | if err != nil {
|
---|
122 | return nil, err
|
---|
123 | }
|
---|
124 |
|
---|
125 | // Treat nil message interface as an empty message,
|
---|
126 | // in which case there is nothing to output.
|
---|
127 | if m == nil {
|
---|
128 | return []byte{}, nil
|
---|
129 | }
|
---|
130 |
|
---|
131 | enc := encoder{internalEnc, o}
|
---|
132 | err = enc.marshalMessage(m.ProtoReflect(), false)
|
---|
133 | if err != nil {
|
---|
134 | return nil, err
|
---|
135 | }
|
---|
136 | out := enc.Bytes()
|
---|
137 | if len(o.Indent) > 0 && len(out) > 0 {
|
---|
138 | out = append(out, '\n')
|
---|
139 | }
|
---|
140 | if o.AllowPartial {
|
---|
141 | return out, nil
|
---|
142 | }
|
---|
143 | return out, proto.CheckInitialized(m)
|
---|
144 | }
|
---|
145 |
|
---|
146 | type encoder struct {
|
---|
147 | *text.Encoder
|
---|
148 | opts MarshalOptions
|
---|
149 | }
|
---|
150 |
|
---|
151 | // marshalMessage marshals the given protoreflect.Message.
|
---|
152 | func (e encoder) marshalMessage(m protoreflect.Message, inclDelims bool) error {
|
---|
153 | messageDesc := m.Descriptor()
|
---|
154 | if !flags.ProtoLegacy && messageset.IsMessageSet(messageDesc) {
|
---|
155 | return errors.New("no support for proto1 MessageSets")
|
---|
156 | }
|
---|
157 |
|
---|
158 | if inclDelims {
|
---|
159 | e.StartMessage()
|
---|
160 | defer e.EndMessage()
|
---|
161 | }
|
---|
162 |
|
---|
163 | // Handle Any expansion.
|
---|
164 | if messageDesc.FullName() == genid.Any_message_fullname {
|
---|
165 | if e.marshalAny(m) {
|
---|
166 | return nil
|
---|
167 | }
|
---|
168 | // If unable to expand, continue on to marshal Any as a regular message.
|
---|
169 | }
|
---|
170 |
|
---|
171 | // Marshal fields.
|
---|
172 | var err error
|
---|
173 | order.RangeFields(m, order.IndexNameFieldOrder, func(fd protoreflect.FieldDescriptor, v protoreflect.Value) bool {
|
---|
174 | if err = e.marshalField(fd.TextName(), v, fd); err != nil {
|
---|
175 | return false
|
---|
176 | }
|
---|
177 | return true
|
---|
178 | })
|
---|
179 | if err != nil {
|
---|
180 | return err
|
---|
181 | }
|
---|
182 |
|
---|
183 | // Marshal unknown fields.
|
---|
184 | if e.opts.EmitUnknown {
|
---|
185 | e.marshalUnknown(m.GetUnknown())
|
---|
186 | }
|
---|
187 |
|
---|
188 | return nil
|
---|
189 | }
|
---|
190 |
|
---|
191 | // marshalField marshals the given field with protoreflect.Value.
|
---|
192 | func (e encoder) marshalField(name string, val protoreflect.Value, fd protoreflect.FieldDescriptor) error {
|
---|
193 | switch {
|
---|
194 | case fd.IsList():
|
---|
195 | return e.marshalList(name, val.List(), fd)
|
---|
196 | case fd.IsMap():
|
---|
197 | return e.marshalMap(name, val.Map(), fd)
|
---|
198 | default:
|
---|
199 | e.WriteName(name)
|
---|
200 | return e.marshalSingular(val, fd)
|
---|
201 | }
|
---|
202 | }
|
---|
203 |
|
---|
204 | // marshalSingular marshals the given non-repeated field value. This includes
|
---|
205 | // all scalar types, enums, messages, and groups.
|
---|
206 | func (e encoder) marshalSingular(val protoreflect.Value, fd protoreflect.FieldDescriptor) error {
|
---|
207 | kind := fd.Kind()
|
---|
208 | switch kind {
|
---|
209 | case protoreflect.BoolKind:
|
---|
210 | e.WriteBool(val.Bool())
|
---|
211 |
|
---|
212 | case protoreflect.StringKind:
|
---|
213 | s := val.String()
|
---|
214 | if !e.opts.allowInvalidUTF8 && strs.EnforceUTF8(fd) && !utf8.ValidString(s) {
|
---|
215 | return errors.InvalidUTF8(string(fd.FullName()))
|
---|
216 | }
|
---|
217 | e.WriteString(s)
|
---|
218 |
|
---|
219 | case protoreflect.Int32Kind, protoreflect.Int64Kind,
|
---|
220 | protoreflect.Sint32Kind, protoreflect.Sint64Kind,
|
---|
221 | protoreflect.Sfixed32Kind, protoreflect.Sfixed64Kind:
|
---|
222 | e.WriteInt(val.Int())
|
---|
223 |
|
---|
224 | case protoreflect.Uint32Kind, protoreflect.Uint64Kind,
|
---|
225 | protoreflect.Fixed32Kind, protoreflect.Fixed64Kind:
|
---|
226 | e.WriteUint(val.Uint())
|
---|
227 |
|
---|
228 | case protoreflect.FloatKind:
|
---|
229 | // Encoder.WriteFloat handles the special numbers NaN and infinites.
|
---|
230 | e.WriteFloat(val.Float(), 32)
|
---|
231 |
|
---|
232 | case protoreflect.DoubleKind:
|
---|
233 | // Encoder.WriteFloat handles the special numbers NaN and infinites.
|
---|
234 | e.WriteFloat(val.Float(), 64)
|
---|
235 |
|
---|
236 | case protoreflect.BytesKind:
|
---|
237 | e.WriteString(string(val.Bytes()))
|
---|
238 |
|
---|
239 | case protoreflect.EnumKind:
|
---|
240 | num := val.Enum()
|
---|
241 | if desc := fd.Enum().Values().ByNumber(num); desc != nil {
|
---|
242 | e.WriteLiteral(string(desc.Name()))
|
---|
243 | } else {
|
---|
244 | // Use numeric value if there is no enum description.
|
---|
245 | e.WriteInt(int64(num))
|
---|
246 | }
|
---|
247 |
|
---|
248 | case protoreflect.MessageKind, protoreflect.GroupKind:
|
---|
249 | return e.marshalMessage(val.Message(), true)
|
---|
250 |
|
---|
251 | default:
|
---|
252 | panic(fmt.Sprintf("%v has unknown kind: %v", fd.FullName(), kind))
|
---|
253 | }
|
---|
254 | return nil
|
---|
255 | }
|
---|
256 |
|
---|
257 | // marshalList marshals the given protoreflect.List as multiple name-value fields.
|
---|
258 | func (e encoder) marshalList(name string, list protoreflect.List, fd protoreflect.FieldDescriptor) error {
|
---|
259 | size := list.Len()
|
---|
260 | for i := 0; i < size; i++ {
|
---|
261 | e.WriteName(name)
|
---|
262 | if err := e.marshalSingular(list.Get(i), fd); err != nil {
|
---|
263 | return err
|
---|
264 | }
|
---|
265 | }
|
---|
266 | return nil
|
---|
267 | }
|
---|
268 |
|
---|
269 | // marshalMap marshals the given protoreflect.Map as multiple name-value fields.
|
---|
270 | func (e encoder) marshalMap(name string, mmap protoreflect.Map, fd protoreflect.FieldDescriptor) error {
|
---|
271 | var err error
|
---|
272 | order.RangeEntries(mmap, order.GenericKeyOrder, func(key protoreflect.MapKey, val protoreflect.Value) bool {
|
---|
273 | e.WriteName(name)
|
---|
274 | e.StartMessage()
|
---|
275 | defer e.EndMessage()
|
---|
276 |
|
---|
277 | e.WriteName(string(genid.MapEntry_Key_field_name))
|
---|
278 | err = e.marshalSingular(key.Value(), fd.MapKey())
|
---|
279 | if err != nil {
|
---|
280 | return false
|
---|
281 | }
|
---|
282 |
|
---|
283 | e.WriteName(string(genid.MapEntry_Value_field_name))
|
---|
284 | err = e.marshalSingular(val, fd.MapValue())
|
---|
285 | if err != nil {
|
---|
286 | return false
|
---|
287 | }
|
---|
288 | return true
|
---|
289 | })
|
---|
290 | return err
|
---|
291 | }
|
---|
292 |
|
---|
293 | // marshalUnknown parses the given []byte and marshals fields out.
|
---|
294 | // This function assumes proper encoding in the given []byte.
|
---|
295 | func (e encoder) marshalUnknown(b []byte) {
|
---|
296 | const dec = 10
|
---|
297 | const hex = 16
|
---|
298 | for len(b) > 0 {
|
---|
299 | num, wtype, n := protowire.ConsumeTag(b)
|
---|
300 | b = b[n:]
|
---|
301 | e.WriteName(strconv.FormatInt(int64(num), dec))
|
---|
302 |
|
---|
303 | switch wtype {
|
---|
304 | case protowire.VarintType:
|
---|
305 | var v uint64
|
---|
306 | v, n = protowire.ConsumeVarint(b)
|
---|
307 | e.WriteUint(v)
|
---|
308 | case protowire.Fixed32Type:
|
---|
309 | var v uint32
|
---|
310 | v, n = protowire.ConsumeFixed32(b)
|
---|
311 | e.WriteLiteral("0x" + strconv.FormatUint(uint64(v), hex))
|
---|
312 | case protowire.Fixed64Type:
|
---|
313 | var v uint64
|
---|
314 | v, n = protowire.ConsumeFixed64(b)
|
---|
315 | e.WriteLiteral("0x" + strconv.FormatUint(v, hex))
|
---|
316 | case protowire.BytesType:
|
---|
317 | var v []byte
|
---|
318 | v, n = protowire.ConsumeBytes(b)
|
---|
319 | e.WriteString(string(v))
|
---|
320 | case protowire.StartGroupType:
|
---|
321 | e.StartMessage()
|
---|
322 | var v []byte
|
---|
323 | v, n = protowire.ConsumeGroup(num, b)
|
---|
324 | e.marshalUnknown(v)
|
---|
325 | e.EndMessage()
|
---|
326 | default:
|
---|
327 | panic(fmt.Sprintf("prototext: error parsing unknown field wire type: %v", wtype))
|
---|
328 | }
|
---|
329 |
|
---|
330 | b = b[n:]
|
---|
331 | }
|
---|
332 | }
|
---|
333 |
|
---|
334 | // marshalAny marshals the given google.protobuf.Any message in expanded form.
|
---|
335 | // It returns true if it was able to marshal, else false.
|
---|
336 | func (e encoder) marshalAny(any protoreflect.Message) bool {
|
---|
337 | // Construct the embedded message.
|
---|
338 | fds := any.Descriptor().Fields()
|
---|
339 | fdType := fds.ByNumber(genid.Any_TypeUrl_field_number)
|
---|
340 | typeURL := any.Get(fdType).String()
|
---|
341 | mt, err := e.opts.Resolver.FindMessageByURL(typeURL)
|
---|
342 | if err != nil {
|
---|
343 | return false
|
---|
344 | }
|
---|
345 | m := mt.New().Interface()
|
---|
346 |
|
---|
347 | // Unmarshal bytes into embedded message.
|
---|
348 | fdValue := fds.ByNumber(genid.Any_Value_field_number)
|
---|
349 | value := any.Get(fdValue)
|
---|
350 | err = proto.UnmarshalOptions{
|
---|
351 | AllowPartial: true,
|
---|
352 | Resolver: e.opts.Resolver,
|
---|
353 | }.Unmarshal(value.Bytes(), m)
|
---|
354 | if err != nil {
|
---|
355 | return false
|
---|
356 | }
|
---|
357 |
|
---|
358 | // Get current encoder position. If marshaling fails, reset encoder output
|
---|
359 | // back to this position.
|
---|
360 | pos := e.Snapshot()
|
---|
361 |
|
---|
362 | // Field name is the proto field name enclosed in [].
|
---|
363 | e.WriteName("[" + typeURL + "]")
|
---|
364 | err = e.marshalMessage(m.ProtoReflect(), true)
|
---|
365 | if err != nil {
|
---|
366 | e.Reset(pos)
|
---|
367 | return false
|
---|
368 | }
|
---|
369 | return true
|
---|
370 | }
|
---|