blob: b732f029f4d6fb43adab9ec385c7f8f6a1994c11 [file] [log] [blame]
Herbie Ongcddf8192018-11-28 18:25:20 -08001// 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
Damien Neil5c5b5312019-05-14 12:44:37 -07005package prototext
Herbie Ongcddf8192018-11-28 18:25:20 -08006
7import (
Herbie Ong20a1d312018-12-11 21:08:58 -08008 "fmt"
Herbie Ongcddf8192018-11-28 18:25:20 -08009 "sort"
Herbie Ong21a39742019-04-08 17:32:44 -070010 "unicode/utf8"
Herbie Ongcddf8192018-11-28 18:25:20 -080011
Joe Tsai5ae10aa2019-07-11 18:23:08 -070012 "google.golang.org/protobuf/internal/encoding/messageset"
Damien Neile89e6242019-05-13 23:55:40 -070013 "google.golang.org/protobuf/internal/encoding/text"
14 "google.golang.org/protobuf/internal/encoding/wire"
15 "google.golang.org/protobuf/internal/errors"
16 "google.golang.org/protobuf/internal/fieldnum"
Joe Tsai5ae10aa2019-07-11 18:23:08 -070017 "google.golang.org/protobuf/internal/flags"
Damien Neile89e6242019-05-13 23:55:40 -070018 "google.golang.org/protobuf/internal/mapsort"
19 "google.golang.org/protobuf/internal/pragma"
20 "google.golang.org/protobuf/proto"
21 pref "google.golang.org/protobuf/reflect/protoreflect"
22 "google.golang.org/protobuf/reflect/protoregistry"
Herbie Ongcddf8192018-11-28 18:25:20 -080023)
24
Herbie Ong800c9902018-12-06 15:28:53 -080025// Marshal writes the given proto.Message in textproto format using default options.
Herbie Ongcddf8192018-11-28 18:25:20 -080026func Marshal(m proto.Message) ([]byte, error) {
27 return MarshalOptions{}.Marshal(m)
28}
29
30// MarshalOptions is a configurable text format marshaler.
31type MarshalOptions struct {
32 pragma.NoUnkeyedLiterals
33
Herbie Ong42577ea2019-03-26 16:26:22 -070034 // AllowPartial allows messages that have missing required fields to marshal
35 // without returning an error. If AllowPartial is false (the default),
36 // Marshal will return error if there are any missing required fields.
37 AllowPartial bool
38
Herbie Ong3a385912019-03-20 14:04:24 -070039 // If Indent is a non-empty string, it causes entries for a Message to be
40 // preceded by the indent and trailed by a newline. Indent can only be
41 // composed of space or tab characters.
42 Indent string
Herbie Ongf42b55f2019-01-02 15:46:07 -080043
Joe Tsai1c283042019-05-14 14:28:19 -070044 // Resolver is used for looking up types when expanding google.protobuf.Any
45 // messages. If nil, this defaults to using protoregistry.GlobalTypes.
46 Resolver interface {
Damien Neil95faac22019-06-19 10:03:37 -070047 protoregistry.ExtensionTypeResolver
Joe Tsai1c283042019-05-14 14:28:19 -070048 protoregistry.MessageTypeResolver
49 }
Herbie Ongcddf8192018-11-28 18:25:20 -080050}
51
Herbie Ong800c9902018-12-06 15:28:53 -080052// Marshal writes the given proto.Message in textproto format using options in MarshalOptions object.
Herbie Ongcddf8192018-11-28 18:25:20 -080053func (o MarshalOptions) Marshal(m proto.Message) ([]byte, error) {
Herbie Ongf42b55f2019-01-02 15:46:07 -080054 if o.Resolver == nil {
55 o.Resolver = protoregistry.GlobalTypes
56 }
57
Herbie Ongf5db2df2019-02-07 20:17:45 -080058 v, err := o.marshalMessage(m.ProtoReflect())
Damien Neil8c86fc52019-06-19 09:28:29 -070059 if err != nil {
Herbie Ong800c9902018-12-06 15:28:53 -080060 return nil, err
Herbie Ongcddf8192018-11-28 18:25:20 -080061 }
62
Herbie Ongcddf8192018-11-28 18:25:20 -080063 delims := [2]byte{'{', '}'}
Herbie Ongcddf8192018-11-28 18:25:20 -080064 const outputASCII = false
Herbie Ong3a385912019-03-20 14:04:24 -070065 b, err := text.Marshal(v, o.Indent, delims, outputASCII)
Damien Neil8c86fc52019-06-19 09:28:29 -070066 if err != nil {
Herbie Ongcddf8192018-11-28 18:25:20 -080067 return nil, err
68 }
Damien Neil8c86fc52019-06-19 09:28:29 -070069 if o.AllowPartial {
70 return b, nil
Damien Neil4686e232019-04-05 13:31:40 -070071 }
Damien Neil8c86fc52019-06-19 09:28:29 -070072 return b, proto.IsInitialized(m)
Herbie Ongcddf8192018-11-28 18:25:20 -080073}
74
75// marshalMessage converts a protoreflect.Message to a text.Value.
76func (o MarshalOptions) marshalMessage(m pref.Message) (text.Value, error) {
Joe Tsai0fc49f82019-05-01 12:29:25 -070077 messageDesc := m.Descriptor()
Joe Tsai1799d112019-08-08 13:31:59 -070078 if !flags.ProtoLegacy && messageset.IsMessageSet(messageDesc) {
Joe Tsai5ae10aa2019-07-11 18:23:08 -070079 return text.Value{}, errors.New("no support for proto1 MessageSets")
80 }
Herbie Ongf42b55f2019-01-02 15:46:07 -080081
82 // Handle Any expansion.
Joe Tsai0fc49f82019-05-01 12:29:25 -070083 if messageDesc.FullName() == "google.protobuf.Any" {
Damien Neil8c86fc52019-06-19 09:28:29 -070084 if msg, err := o.marshalAny(m); err == nil {
85 // Return as is if no error.
86 return msg, nil
Herbie Ongf42b55f2019-01-02 15:46:07 -080087 }
Damien Neil8c86fc52019-06-19 09:28:29 -070088 // Otherwise continue on to marshal Any as a regular message.
Herbie Ongf42b55f2019-01-02 15:46:07 -080089 }
Herbie Ongcddf8192018-11-28 18:25:20 -080090
91 // Handle known fields.
Joe Tsai5ae10aa2019-07-11 18:23:08 -070092 var msgFields [][2]text.Value
Joe Tsai0fc49f82019-05-01 12:29:25 -070093 fieldDescs := messageDesc.Fields()
Herbie Ongcddf8192018-11-28 18:25:20 -080094 size := fieldDescs.Len()
95 for i := 0; i < size; i++ {
Herbie Ong800c9902018-12-06 15:28:53 -080096 fd := fieldDescs.Get(i)
Joe Tsai378c1322019-04-25 23:48:08 -070097 if !m.Has(fd) {
Herbie Ongcddf8192018-11-28 18:25:20 -080098 continue
99 }
100
Herbie Ongf5db2df2019-02-07 20:17:45 -0800101 name := text.ValueOf(fd.Name())
102 // Use type name for group field name.
103 if fd.Kind() == pref.GroupKind {
Joe Tsaid24bc722019-04-15 23:39:09 -0700104 name = text.ValueOf(fd.Message().Name())
Herbie Ongf5db2df2019-02-07 20:17:45 -0800105 }
Joe Tsai378c1322019-04-25 23:48:08 -0700106 pval := m.Get(fd)
Herbie Ongcf253082018-12-17 17:13:07 -0800107 var err error
Herbie Ongf5db2df2019-02-07 20:17:45 -0800108 msgFields, err = o.appendField(msgFields, name, pval, fd)
Damien Neil8c86fc52019-06-19 09:28:29 -0700109 if err != nil {
Herbie Ongcf253082018-12-17 17:13:07 -0800110 return text.Value{}, err
Herbie Ongcddf8192018-11-28 18:25:20 -0800111 }
Herbie Ongcddf8192018-11-28 18:25:20 -0800112 }
113
Herbie Ongcf253082018-12-17 17:13:07 -0800114 // Handle extensions.
115 var err error
Joe Tsai378c1322019-04-25 23:48:08 -0700116 msgFields, err = o.appendExtensions(msgFields, m)
Damien Neil8c86fc52019-06-19 09:28:29 -0700117 if err != nil {
Herbie Ongcf253082018-12-17 17:13:07 -0800118 return text.Value{}, err
119 }
120
121 // Handle unknown fields.
Herbie Ong20a1d312018-12-11 21:08:58 -0800122 // TODO: Provide option to exclude or include unknown fields.
Joe Tsai378c1322019-04-25 23:48:08 -0700123 msgFields = appendUnknown(msgFields, m.GetUnknown())
Herbie Ong20a1d312018-12-11 21:08:58 -0800124
Damien Neil8c86fc52019-06-19 09:28:29 -0700125 return text.ValueOf(msgFields), nil
Herbie Ongcddf8192018-11-28 18:25:20 -0800126}
127
Herbie Ongcf253082018-12-17 17:13:07 -0800128// appendField marshals a protoreflect.Value and appends it to the given [][2]text.Value.
Herbie Ongf5db2df2019-02-07 20:17:45 -0800129func (o MarshalOptions) appendField(msgFields [][2]text.Value, name text.Value, pval pref.Value, fd pref.FieldDescriptor) ([][2]text.Value, error) {
Joe Tsaiac31a352019-05-13 14:32:56 -0700130 switch {
131 case fd.IsList():
132 items, err := o.marshalList(pval.List(), fd)
Damien Neil8c86fc52019-06-19 09:28:29 -0700133 if err != nil {
Joe Tsaiac31a352019-05-13 14:32:56 -0700134 return msgFields, err
Herbie Ongcf253082018-12-17 17:13:07 -0800135 }
136
Herbie Ongcf253082018-12-17 17:13:07 -0800137 for _, item := range items {
Herbie Ongf5db2df2019-02-07 20:17:45 -0800138 msgFields = append(msgFields, [2]text.Value{name, item})
Herbie Ongcf253082018-12-17 17:13:07 -0800139 }
Joe Tsaiac31a352019-05-13 14:32:56 -0700140 case fd.IsMap():
141 items, err := o.marshalMap(pval.Map(), fd)
Damien Neil8c86fc52019-06-19 09:28:29 -0700142 if err != nil {
Joe Tsaiac31a352019-05-13 14:32:56 -0700143 return msgFields, err
144 }
145
146 for _, item := range items {
147 msgFields = append(msgFields, [2]text.Value{name, item})
148 }
149 default:
Herbie Ongcf253082018-12-17 17:13:07 -0800150 tval, err := o.marshalSingular(pval, fd)
Damien Neil8c86fc52019-06-19 09:28:29 -0700151 if err != nil {
Herbie Ongcf253082018-12-17 17:13:07 -0800152 return msgFields, err
153 }
Herbie Ongf5db2df2019-02-07 20:17:45 -0800154 msgFields = append(msgFields, [2]text.Value{name, tval})
Herbie Ongcf253082018-12-17 17:13:07 -0800155 }
156
Damien Neil8c86fc52019-06-19 09:28:29 -0700157 return msgFields, nil
Herbie Ongcf253082018-12-17 17:13:07 -0800158}
159
Herbie Ongcddf8192018-11-28 18:25:20 -0800160// marshalSingular converts a non-repeated field value to text.Value.
161// This includes all scalar types, enums, messages, and groups.
162func (o MarshalOptions) marshalSingular(val pref.Value, fd pref.FieldDescriptor) (text.Value, error) {
163 kind := fd.Kind()
164 switch kind {
165 case pref.BoolKind,
166 pref.Int32Kind, pref.Sint32Kind, pref.Uint32Kind,
167 pref.Int64Kind, pref.Sint64Kind, pref.Uint64Kind,
168 pref.Sfixed32Kind, pref.Fixed32Kind,
169 pref.Sfixed64Kind, pref.Fixed64Kind,
170 pref.FloatKind, pref.DoubleKind,
Herbie Ong21a39742019-04-08 17:32:44 -0700171 pref.BytesKind:
Herbie Ongcddf8192018-11-28 18:25:20 -0800172 return text.ValueOf(val.Interface()), nil
173
Herbie Ong21a39742019-04-08 17:32:44 -0700174 case pref.StringKind:
175 s := val.String()
Damien Neil8c86fc52019-06-19 09:28:29 -0700176 if !utf8.ValidString(s) {
177 return text.Value{}, errors.InvalidUTF8(string(fd.FullName()))
Herbie Ong21a39742019-04-08 17:32:44 -0700178 }
Damien Neil8c86fc52019-06-19 09:28:29 -0700179 return text.ValueOf(s), nil
Herbie Ong21a39742019-04-08 17:32:44 -0700180
Herbie Ongcddf8192018-11-28 18:25:20 -0800181 case pref.EnumKind:
182 num := val.Enum()
Joe Tsaid24bc722019-04-15 23:39:09 -0700183 if desc := fd.Enum().Values().ByNumber(num); desc != nil {
Herbie Ongcddf8192018-11-28 18:25:20 -0800184 return text.ValueOf(desc.Name()), nil
185 }
186 // Use numeric value if there is no enum description.
187 return text.ValueOf(int32(num)), nil
188
189 case pref.MessageKind, pref.GroupKind:
190 return o.marshalMessage(val.Message())
191 }
192
Herbie Ong09b28a92019-04-03 15:42:41 -0700193 panic(fmt.Sprintf("%v has unknown kind: %v", fd.FullName(), kind))
Herbie Ongcddf8192018-11-28 18:25:20 -0800194}
195
196// marshalList converts a protoreflect.List to []text.Value.
197func (o MarshalOptions) marshalList(list pref.List, fd pref.FieldDescriptor) ([]text.Value, error) {
Herbie Ongcddf8192018-11-28 18:25:20 -0800198 size := list.Len()
199 values := make([]text.Value, 0, size)
200
201 for i := 0; i < size; i++ {
202 item := list.Get(i)
203 val, err := o.marshalSingular(item, fd)
Damien Neil8c86fc52019-06-19 09:28:29 -0700204 if err != nil {
Herbie Ongcddf8192018-11-28 18:25:20 -0800205 // Return already marshaled values.
206 return values, err
207 }
208 values = append(values, val)
209 }
210
Damien Neil8c86fc52019-06-19 09:28:29 -0700211 return values, nil
Herbie Ongcddf8192018-11-28 18:25:20 -0800212}
213
214var (
215 mapKeyName = text.ValueOf(pref.Name("key"))
216 mapValueName = text.ValueOf(pref.Name("value"))
217)
218
219// marshalMap converts a protoreflect.Map to []text.Value.
220func (o MarshalOptions) marshalMap(mmap pref.Map, fd pref.FieldDescriptor) ([]text.Value, error) {
Herbie Ongcddf8192018-11-28 18:25:20 -0800221 // values is a list of messages.
222 values := make([]text.Value, 0, mmap.Len())
Herbie Ongcddf8192018-11-28 18:25:20 -0800223
Herbie Ong09b28a92019-04-03 15:42:41 -0700224 var err error
Joe Tsaiac31a352019-05-13 14:32:56 -0700225 mapsort.Range(mmap, fd.MapKey().Kind(), func(key pref.MapKey, val pref.Value) bool {
Herbie Ong09b28a92019-04-03 15:42:41 -0700226 var keyTxtVal text.Value
Joe Tsaiac31a352019-05-13 14:32:56 -0700227 keyTxtVal, err = o.marshalSingular(key.Value(), fd.MapKey())
Damien Neil8c86fc52019-06-19 09:28:29 -0700228 if err != nil {
Herbie Ongcddf8192018-11-28 18:25:20 -0800229 return false
230 }
Herbie Ong09b28a92019-04-03 15:42:41 -0700231 var valTxtVal text.Value
Joe Tsaiac31a352019-05-13 14:32:56 -0700232 valTxtVal, err = o.marshalSingular(val, fd.MapValue())
Damien Neil8c86fc52019-06-19 09:28:29 -0700233 if err != nil {
Herbie Ongcddf8192018-11-28 18:25:20 -0800234 return false
235 }
236 // Map entry (message) contains 2 fields, first field for key and second field for value.
237 msg := text.ValueOf([][2]text.Value{
238 {mapKeyName, keyTxtVal},
239 {mapValueName, valTxtVal},
240 })
241 values = append(values, msg)
Herbie Ong09b28a92019-04-03 15:42:41 -0700242 err = nil
Herbie Ongcddf8192018-11-28 18:25:20 -0800243 return true
244 })
Herbie Ong09b28a92019-04-03 15:42:41 -0700245 if err != nil {
246 return nil, err
247 }
Herbie Ongcddf8192018-11-28 18:25:20 -0800248
Damien Neil8c86fc52019-06-19 09:28:29 -0700249 return values, nil
Herbie Ongcddf8192018-11-28 18:25:20 -0800250}
251
Herbie Ongcf253082018-12-17 17:13:07 -0800252// appendExtensions marshals extension fields and appends them to the given [][2]text.Value.
Joe Tsai378c1322019-04-25 23:48:08 -0700253func (o MarshalOptions) appendExtensions(msgFields [][2]text.Value, m pref.Message) ([][2]text.Value, error) {
Herbie Ongcf253082018-12-17 17:13:07 -0800254 var err error
Joe Tsai378c1322019-04-25 23:48:08 -0700255 var entries [][2]text.Value
256 m.Range(func(fd pref.FieldDescriptor, v pref.Value) bool {
257 if !fd.IsExtension() {
258 return true
259 }
Joe Tsai378c1322019-04-25 23:48:08 -0700260
Joe Tsai5ae10aa2019-07-11 18:23:08 -0700261 // For MessageSet extensions, the name used is the parent message.
Joe Tsaid4211502019-07-02 14:58:02 -0700262 name := fd.FullName()
Joe Tsai5ae10aa2019-07-11 18:23:08 -0700263 if messageset.IsMessageSetExtension(fd) {
264 name = name.Parent()
Herbie Ong6470ea62019-01-07 18:56:57 -0800265 }
Herbie Ongcf253082018-12-17 17:13:07 -0800266
Joe Tsai378c1322019-04-25 23:48:08 -0700267 // Use string type to produce [name] format.
268 tname := text.ValueOf(string(name))
Joe Tsaid4211502019-07-02 14:58:02 -0700269 entries, err = o.appendField(entries, tname, v, fd)
Damien Neil8c86fc52019-06-19 09:28:29 -0700270 if err != nil {
Joe Tsai378c1322019-04-25 23:48:08 -0700271 return false
Herbie Ongcf253082018-12-17 17:13:07 -0800272 }
Joe Tsai378c1322019-04-25 23:48:08 -0700273 err = nil
Herbie Ongcf253082018-12-17 17:13:07 -0800274 return true
275 })
Herbie Ong09b28a92019-04-03 15:42:41 -0700276 if err != nil {
Herbie Ongcf253082018-12-17 17:13:07 -0800277 return msgFields, err
278 }
279
280 // Sort extensions lexicographically and append to output.
Joe Tsai378c1322019-04-25 23:48:08 -0700281 sort.SliceStable(entries, func(i, j int) bool {
282 return entries[i][0].String() < entries[j][0].String()
Herbie Ongcf253082018-12-17 17:13:07 -0800283 })
Damien Neil8c86fc52019-06-19 09:28:29 -0700284 return append(msgFields, entries...), nil
Herbie Ongcf253082018-12-17 17:13:07 -0800285}
286
Herbie Ong20a1d312018-12-11 21:08:58 -0800287// appendUnknown parses the given []byte and appends field(s) into the given fields slice.
288// This function assumes proper encoding in the given []byte.
289func appendUnknown(fields [][2]text.Value, b []byte) [][2]text.Value {
290 for len(b) > 0 {
291 var value interface{}
292 num, wtype, n := wire.ConsumeTag(b)
293 b = b[n:]
294
295 switch wtype {
296 case wire.VarintType:
297 value, n = wire.ConsumeVarint(b)
298 case wire.Fixed32Type:
299 value, n = wire.ConsumeFixed32(b)
300 case wire.Fixed64Type:
301 value, n = wire.ConsumeFixed64(b)
302 case wire.BytesType:
303 value, n = wire.ConsumeBytes(b)
304 case wire.StartGroupType:
305 var v []byte
306 v, n = wire.ConsumeGroup(num, b)
307 var msg [][2]text.Value
308 value = appendUnknown(msg, v)
309 default:
310 panic(fmt.Sprintf("error parsing unknown field wire type: %v", wtype))
311 }
312
313 fields = append(fields, [2]text.Value{text.ValueOf(uint32(num)), text.ValueOf(value)})
314 b = b[n:]
315 }
316 return fields
317}
Herbie Ongf42b55f2019-01-02 15:46:07 -0800318
319// marshalAny converts a google.protobuf.Any protoreflect.Message to a text.Value.
320func (o MarshalOptions) marshalAny(m pref.Message) (text.Value, error) {
Joe Tsai378c1322019-04-25 23:48:08 -0700321 fds := m.Descriptor().Fields()
322 fdType := fds.ByNumber(fieldnum.Any_TypeUrl)
323 fdValue := fds.ByNumber(fieldnum.Any_Value)
Herbie Ongf42b55f2019-01-02 15:46:07 -0800324
Joe Tsai378c1322019-04-25 23:48:08 -0700325 typeURL := m.Get(fdType).String()
326 value := m.Get(fdValue)
327
Herbie Ong66c365c2019-01-04 14:08:41 -0800328 emt, err := o.Resolver.FindMessageByURL(typeURL)
Damien Neil8c86fc52019-06-19 09:28:29 -0700329 if err != nil {
Herbie Ongf42b55f2019-01-02 15:46:07 -0800330 return text.Value{}, err
331 }
Joe Tsai3bc7d6f2019-01-09 02:57:13 -0800332 em := emt.New().Interface()
Damien Neil96c229a2019-04-03 12:17:24 -0700333 err = proto.UnmarshalOptions{
Damien Neil0c9f0a92019-06-19 10:41:09 -0700334 AllowPartial: true,
Damien Neil95faac22019-06-19 10:03:37 -0700335 Resolver: o.Resolver,
Damien Neil96c229a2019-04-03 12:17:24 -0700336 }.Unmarshal(value.Bytes(), em)
Damien Neil8c86fc52019-06-19 09:28:29 -0700337 if err != nil {
Herbie Ongf42b55f2019-01-02 15:46:07 -0800338 return text.Value{}, err
339 }
340
341 msg, err := o.marshalMessage(em.ProtoReflect())
Damien Neil8c86fc52019-06-19 09:28:29 -0700342 if err != nil {
Herbie Ongf42b55f2019-01-02 15:46:07 -0800343 return text.Value{}, err
344 }
Herbie Ong66c365c2019-01-04 14:08:41 -0800345 // Expanded Any field value contains only a single field with the type_url field value as the
346 // field name in [] and a text marshaled field value of the embedded message.
Herbie Ongf42b55f2019-01-02 15:46:07 -0800347 msgFields := [][2]text.Value{
348 {
Herbie Ong66c365c2019-01-04 14:08:41 -0800349 text.ValueOf(typeURL),
Herbie Ongf42b55f2019-01-02 15:46:07 -0800350 msg,
351 },
352 }
Damien Neil8c86fc52019-06-19 09:28:29 -0700353 return text.ValueOf(msgFields), nil
Herbie Ongf42b55f2019-01-02 15:46:07 -0800354}