blob: 8d4b9cbe70d072ea8d085c06c4f316ae25fbe594 [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
5package textpb
6
7import (
Herbie Ong20a1d312018-12-11 21:08:58 -08008 "fmt"
Herbie Ongcddf8192018-11-28 18:25:20 -08009 "sort"
10
11 "github.com/golang/protobuf/v2/internal/encoding/text"
Herbie Ong20a1d312018-12-11 21:08:58 -080012 "github.com/golang/protobuf/v2/internal/encoding/wire"
Herbie Ongcddf8192018-11-28 18:25:20 -080013 "github.com/golang/protobuf/v2/internal/errors"
14 "github.com/golang/protobuf/v2/internal/pragma"
15 "github.com/golang/protobuf/v2/proto"
16 pref "github.com/golang/protobuf/v2/reflect/protoreflect"
17)
18
Herbie Ong800c9902018-12-06 15:28:53 -080019// Marshal writes the given proto.Message in textproto format using default options.
Herbie Ongcddf8192018-11-28 18:25:20 -080020// TODO: may want to describe when Marshal returns error.
21func Marshal(m proto.Message) ([]byte, error) {
22 return MarshalOptions{}.Marshal(m)
23}
24
25// MarshalOptions is a configurable text format marshaler.
26type MarshalOptions struct {
27 pragma.NoUnkeyedLiterals
28
29 // Set Compact to true to have output in a single line with no line breaks.
30 Compact bool
31}
32
Herbie Ong800c9902018-12-06 15:28:53 -080033// Marshal writes the given proto.Message in textproto format using options in MarshalOptions object.
Herbie Ongcddf8192018-11-28 18:25:20 -080034func (o MarshalOptions) Marshal(m proto.Message) ([]byte, error) {
35 var nerr errors.NonFatal
36 var v text.Value
37
Herbie Ong800c9902018-12-06 15:28:53 -080038 var err error
39 v, err = o.marshalMessage(m.ProtoReflect())
40 if !nerr.Merge(err) {
41 return nil, err
Herbie Ongcddf8192018-11-28 18:25:20 -080042 }
43
44 indent := " "
45 if o.Compact {
46 indent = ""
47 }
48 delims := [2]byte{'{', '}'}
49
50 const outputASCII = false
51 b, err := text.Marshal(v, indent, delims, outputASCII)
52 if !nerr.Merge(err) {
53 return nil, err
54 }
55 return b, nerr.E
56}
57
58// marshalMessage converts a protoreflect.Message to a text.Value.
59func (o MarshalOptions) marshalMessage(m pref.Message) (text.Value, error) {
60 var nerr errors.NonFatal
61 var msgFields [][2]text.Value
62
63 // Handle known fields.
64 msgType := m.Type()
65 fieldDescs := msgType.Fields()
66 knownFields := m.KnownFields()
67 size := fieldDescs.Len()
68 for i := 0; i < size; i++ {
Herbie Ong800c9902018-12-06 15:28:53 -080069 fd := fieldDescs.Get(i)
70 num := fd.Number()
Herbie Ongcddf8192018-11-28 18:25:20 -080071
Herbie Ong800c9902018-12-06 15:28:53 -080072 if !knownFields.Has(num) {
73 if fd.Cardinality() == pref.Required {
Herbie Ongcddf8192018-11-28 18:25:20 -080074 // Treat unset required fields as a non-fatal error.
Herbie Ong800c9902018-12-06 15:28:53 -080075 nerr.AppendRequiredNotSet(string(fd.FullName()))
Herbie Ongcddf8192018-11-28 18:25:20 -080076 }
77 continue
78 }
79
Herbie Ong800c9902018-12-06 15:28:53 -080080 tname := text.ValueOf(fd.Name())
81 pval := knownFields.Get(num)
Herbie Ongcddf8192018-11-28 18:25:20 -080082
Herbie Ong800c9902018-12-06 15:28:53 -080083 if fd.Cardinality() == pref.Repeated {
Herbie Ongcddf8192018-11-28 18:25:20 -080084 // Map or repeated fields.
85 var items []text.Value
86 var err error
Herbie Ong800c9902018-12-06 15:28:53 -080087 if fd.IsMap() {
88 items, err = o.marshalMap(pval.Map(), fd)
Herbie Ongcddf8192018-11-28 18:25:20 -080089 if !nerr.Merge(err) {
90 return text.Value{}, err
91 }
92 } else {
Herbie Ong800c9902018-12-06 15:28:53 -080093 items, err = o.marshalList(pval.List(), fd)
Herbie Ongcddf8192018-11-28 18:25:20 -080094 if !nerr.Merge(err) {
95 return text.Value{}, err
96 }
97 }
98
99 // Add each item as key: value field.
100 for _, item := range items {
Herbie Ong800c9902018-12-06 15:28:53 -0800101 msgFields = append(msgFields, [2]text.Value{tname, item})
Herbie Ongcddf8192018-11-28 18:25:20 -0800102 }
103 } else {
104 // Required or optional fields.
Herbie Ong800c9902018-12-06 15:28:53 -0800105 tval, err := o.marshalSingular(pval, fd)
Herbie Ongcddf8192018-11-28 18:25:20 -0800106 if !nerr.Merge(err) {
107 return text.Value{}, err
108 }
Herbie Ong800c9902018-12-06 15:28:53 -0800109 msgFields = append(msgFields, [2]text.Value{tname, tval})
Herbie Ongcddf8192018-11-28 18:25:20 -0800110 }
Herbie Ongcddf8192018-11-28 18:25:20 -0800111 }
112
Herbie Ong20a1d312018-12-11 21:08:58 -0800113 // Marshal out unknown fields.
114 // TODO: Provide option to exclude or include unknown fields.
115 m.UnknownFields().Range(func(_ pref.FieldNumber, raw pref.RawFields) bool {
116 msgFields = appendUnknown(msgFields, raw)
117 return true
118 })
119
120 // TODO: Handle extensions and Any expansion.
Herbie Ongcddf8192018-11-28 18:25:20 -0800121
122 return text.ValueOf(msgFields), nerr.E
123}
124
125// marshalSingular converts a non-repeated field value to text.Value.
126// This includes all scalar types, enums, messages, and groups.
127func (o MarshalOptions) marshalSingular(val pref.Value, fd pref.FieldDescriptor) (text.Value, error) {
128 kind := fd.Kind()
129 switch kind {
130 case pref.BoolKind,
131 pref.Int32Kind, pref.Sint32Kind, pref.Uint32Kind,
132 pref.Int64Kind, pref.Sint64Kind, pref.Uint64Kind,
133 pref.Sfixed32Kind, pref.Fixed32Kind,
134 pref.Sfixed64Kind, pref.Fixed64Kind,
135 pref.FloatKind, pref.DoubleKind,
136 pref.StringKind, pref.BytesKind:
137 return text.ValueOf(val.Interface()), nil
138
139 case pref.EnumKind:
140 num := val.Enum()
141 if desc := fd.EnumType().Values().ByNumber(num); desc != nil {
142 return text.ValueOf(desc.Name()), nil
143 }
144 // Use numeric value if there is no enum description.
145 return text.ValueOf(int32(num)), nil
146
147 case pref.MessageKind, pref.GroupKind:
148 return o.marshalMessage(val.Message())
149 }
150
151 return text.Value{}, errors.New("%v has unknown kind: %v", fd.FullName(), kind)
152}
153
154// marshalList converts a protoreflect.List to []text.Value.
155func (o MarshalOptions) marshalList(list pref.List, fd pref.FieldDescriptor) ([]text.Value, error) {
156 var nerr errors.NonFatal
157 size := list.Len()
158 values := make([]text.Value, 0, size)
159
160 for i := 0; i < size; i++ {
161 item := list.Get(i)
162 val, err := o.marshalSingular(item, fd)
163 if !nerr.Merge(err) {
164 // Return already marshaled values.
165 return values, err
166 }
167 values = append(values, val)
168 }
169
170 return values, nerr.E
171}
172
173var (
174 mapKeyName = text.ValueOf(pref.Name("key"))
175 mapValueName = text.ValueOf(pref.Name("value"))
176)
177
178// marshalMap converts a protoreflect.Map to []text.Value.
179func (o MarshalOptions) marshalMap(mmap pref.Map, fd pref.FieldDescriptor) ([]text.Value, error) {
180 var nerr errors.NonFatal
181 // values is a list of messages.
182 values := make([]text.Value, 0, mmap.Len())
183 msgFields := fd.MessageType().Fields()
184 keyType := msgFields.ByNumber(1)
185 valType := msgFields.ByNumber(2)
186
187 mmap.Range(func(key pref.MapKey, val pref.Value) bool {
188 keyTxtVal, err := o.marshalSingular(key.Value(), keyType)
189 if !nerr.Merge(err) {
190 return false
191 }
192 valTxtVal, err := o.marshalSingular(val, valType)
193 if !nerr.Merge(err) {
194 return false
195 }
196 // Map entry (message) contains 2 fields, first field for key and second field for value.
197 msg := text.ValueOf([][2]text.Value{
198 {mapKeyName, keyTxtVal},
199 {mapValueName, valTxtVal},
200 })
201 values = append(values, msg)
202 return true
203 })
204
205 sortMap(keyType.Kind(), values)
206 return values, nerr.E
207}
208
209// sortMap orders list based on value of key field for deterministic output.
210// TODO: Improve sort comparison of text.Value for map keys.
211func sortMap(keyKind pref.Kind, values []text.Value) {
212 less := func(i, j int) bool {
213 mi := values[i].Message()
214 mj := values[j].Message()
215 return mi[0][1].String() < mj[0][1].String()
216 }
217 switch keyKind {
218 case pref.Int32Kind, pref.Sint32Kind, pref.Sfixed32Kind:
219 less = func(i, j int) bool {
220 mi := values[i].Message()
221 mj := values[j].Message()
222 ni, _ := mi[0][1].Int(false)
223 nj, _ := mj[0][1].Int(false)
224 return ni < nj
225 }
226 case pref.Int64Kind, pref.Sint64Kind, pref.Sfixed64Kind:
227 less = func(i, j int) bool {
228 mi := values[i].Message()
229 mj := values[j].Message()
230 ni, _ := mi[0][1].Int(true)
231 nj, _ := mj[0][1].Int(true)
232 return ni < nj
233 }
234
235 case pref.Uint32Kind, pref.Fixed32Kind:
236 less = func(i, j int) bool {
237 mi := values[i].Message()
238 mj := values[j].Message()
239 ni, _ := mi[0][1].Uint(false)
240 nj, _ := mj[0][1].Uint(false)
241 return ni < nj
242 }
243 case pref.Uint64Kind, pref.Fixed64Kind:
244 less = func(i, j int) bool {
245 mi := values[i].Message()
246 mj := values[j].Message()
247 ni, _ := mi[0][1].Uint(true)
248 nj, _ := mj[0][1].Uint(true)
249 return ni < nj
250 }
251 }
252 sort.Slice(values, less)
253}
Herbie Ong20a1d312018-12-11 21:08:58 -0800254
255// appendUnknown parses the given []byte and appends field(s) into the given fields slice.
256// This function assumes proper encoding in the given []byte.
257func appendUnknown(fields [][2]text.Value, b []byte) [][2]text.Value {
258 for len(b) > 0 {
259 var value interface{}
260 num, wtype, n := wire.ConsumeTag(b)
261 b = b[n:]
262
263 switch wtype {
264 case wire.VarintType:
265 value, n = wire.ConsumeVarint(b)
266 case wire.Fixed32Type:
267 value, n = wire.ConsumeFixed32(b)
268 case wire.Fixed64Type:
269 value, n = wire.ConsumeFixed64(b)
270 case wire.BytesType:
271 value, n = wire.ConsumeBytes(b)
272 case wire.StartGroupType:
273 var v []byte
274 v, n = wire.ConsumeGroup(num, b)
275 var msg [][2]text.Value
276 value = appendUnknown(msg, v)
277 default:
278 panic(fmt.Sprintf("error parsing unknown field wire type: %v", wtype))
279 }
280
281 fields = append(fields, [2]text.Value{text.ValueOf(uint32(num)), text.ValueOf(value)})
282 b = b[n:]
283 }
284 return fields
285}