blob: 1b11e66589d985387954193757a8b32107e10134 [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 (
8 "sort"
9
10 "github.com/golang/protobuf/v2/internal/encoding/text"
11 "github.com/golang/protobuf/v2/internal/errors"
12 "github.com/golang/protobuf/v2/internal/pragma"
13 "github.com/golang/protobuf/v2/proto"
14 pref "github.com/golang/protobuf/v2/reflect/protoreflect"
15)
16
17// Marshal marshals a proto.Message in text format using default options.
18// TODO: may want to describe when Marshal returns error.
19func Marshal(m proto.Message) ([]byte, error) {
20 return MarshalOptions{}.Marshal(m)
21}
22
23// MarshalOptions is a configurable text format marshaler.
24type MarshalOptions struct {
25 pragma.NoUnkeyedLiterals
26
27 // Set Compact to true to have output in a single line with no line breaks.
28 Compact bool
29}
30
31// Marshal returns the given proto.Message in text format using options in MarshalOptions object.
32func (o MarshalOptions) Marshal(m proto.Message) ([]byte, error) {
33 var nerr errors.NonFatal
34 var v text.Value
35
36 if m == nil {
37 // TODO: Make sure this is consistent with jsonpb and binary serialization.
38 v = text.ValueOf([][2]text.Value{})
39 } else {
40 var err error
41 v, err = o.marshalMessage(m.ProtoReflect())
42 if !nerr.Merge(err) {
43 return nil, err
44 }
45 }
46
47 indent := " "
48 if o.Compact {
49 indent = ""
50 }
51 delims := [2]byte{'{', '}'}
52
53 const outputASCII = false
54 b, err := text.Marshal(v, indent, delims, outputASCII)
55 if !nerr.Merge(err) {
56 return nil, err
57 }
58 return b, nerr.E
59}
60
61// marshalMessage converts a protoreflect.Message to a text.Value.
62func (o MarshalOptions) marshalMessage(m pref.Message) (text.Value, error) {
63 var nerr errors.NonFatal
64 var msgFields [][2]text.Value
65
66 // Handle known fields.
67 msgType := m.Type()
68 fieldDescs := msgType.Fields()
69 knownFields := m.KnownFields()
70 size := fieldDescs.Len()
71 for i := 0; i < size; i++ {
72 fieldDesc := fieldDescs.Get(i)
73 fieldNum := fieldDesc.Number()
74
75 if !knownFields.Has(fieldNum) {
76 if fieldDesc.Cardinality() == pref.Required {
77 // Treat unset required fields as a non-fatal error.
78 nerr.AppendRequiredNotSet(string(fieldDesc.FullName()))
79 }
80 continue
81 }
82
83 txtName := text.ValueOf(fieldDesc.Name())
84 value := knownFields.Get(fieldNum)
85
86 if fieldDesc.Cardinality() == pref.Repeated {
87 // Map or repeated fields.
88 var items []text.Value
89 var err error
90 if fieldDesc.IsMap() {
91 items, err = o.marshalMap(value.Map(), fieldDesc)
92 if !nerr.Merge(err) {
93 return text.Value{}, err
94 }
95 } else {
96 items, err = o.marshalList(value.List(), fieldDesc)
97 if !nerr.Merge(err) {
98 return text.Value{}, err
99 }
100 }
101
102 // Add each item as key: value field.
103 for _, item := range items {
104 msgFields = append(msgFields, [2]text.Value{txtName, item})
105 }
106 } else {
107 // Required or optional fields.
108 txtValue, err := o.marshalSingular(value, fieldDesc)
109 if !nerr.Merge(err) {
110 return text.Value{}, err
111 }
112 msgFields = append(msgFields, [2]text.Value{txtName, txtValue})
113 }
114
115 }
116
117 // TODO: Handle extensions, unknowns and Any.
118
119 return text.ValueOf(msgFields), nerr.E
120}
121
122// marshalSingular converts a non-repeated field value to text.Value.
123// This includes all scalar types, enums, messages, and groups.
124func (o MarshalOptions) marshalSingular(val pref.Value, fd pref.FieldDescriptor) (text.Value, error) {
125 kind := fd.Kind()
126 switch kind {
127 case pref.BoolKind,
128 pref.Int32Kind, pref.Sint32Kind, pref.Uint32Kind,
129 pref.Int64Kind, pref.Sint64Kind, pref.Uint64Kind,
130 pref.Sfixed32Kind, pref.Fixed32Kind,
131 pref.Sfixed64Kind, pref.Fixed64Kind,
132 pref.FloatKind, pref.DoubleKind,
133 pref.StringKind, pref.BytesKind:
134 return text.ValueOf(val.Interface()), nil
135
136 case pref.EnumKind:
137 num := val.Enum()
138 if desc := fd.EnumType().Values().ByNumber(num); desc != nil {
139 return text.ValueOf(desc.Name()), nil
140 }
141 // Use numeric value if there is no enum description.
142 return text.ValueOf(int32(num)), nil
143
144 case pref.MessageKind, pref.GroupKind:
145 return o.marshalMessage(val.Message())
146 }
147
148 return text.Value{}, errors.New("%v has unknown kind: %v", fd.FullName(), kind)
149}
150
151// marshalList converts a protoreflect.List to []text.Value.
152func (o MarshalOptions) marshalList(list pref.List, fd pref.FieldDescriptor) ([]text.Value, error) {
153 var nerr errors.NonFatal
154 size := list.Len()
155 values := make([]text.Value, 0, size)
156
157 for i := 0; i < size; i++ {
158 item := list.Get(i)
159 val, err := o.marshalSingular(item, fd)
160 if !nerr.Merge(err) {
161 // Return already marshaled values.
162 return values, err
163 }
164 values = append(values, val)
165 }
166
167 return values, nerr.E
168}
169
170var (
171 mapKeyName = text.ValueOf(pref.Name("key"))
172 mapValueName = text.ValueOf(pref.Name("value"))
173)
174
175// marshalMap converts a protoreflect.Map to []text.Value.
176func (o MarshalOptions) marshalMap(mmap pref.Map, fd pref.FieldDescriptor) ([]text.Value, error) {
177 var nerr errors.NonFatal
178 // values is a list of messages.
179 values := make([]text.Value, 0, mmap.Len())
180 msgFields := fd.MessageType().Fields()
181 keyType := msgFields.ByNumber(1)
182 valType := msgFields.ByNumber(2)
183
184 mmap.Range(func(key pref.MapKey, val pref.Value) bool {
185 keyTxtVal, err := o.marshalSingular(key.Value(), keyType)
186 if !nerr.Merge(err) {
187 return false
188 }
189 valTxtVal, err := o.marshalSingular(val, valType)
190 if !nerr.Merge(err) {
191 return false
192 }
193 // Map entry (message) contains 2 fields, first field for key and second field for value.
194 msg := text.ValueOf([][2]text.Value{
195 {mapKeyName, keyTxtVal},
196 {mapValueName, valTxtVal},
197 })
198 values = append(values, msg)
199 return true
200 })
201
202 sortMap(keyType.Kind(), values)
203 return values, nerr.E
204}
205
206// sortMap orders list based on value of key field for deterministic output.
207// TODO: Improve sort comparison of text.Value for map keys.
208func sortMap(keyKind pref.Kind, values []text.Value) {
209 less := func(i, j int) bool {
210 mi := values[i].Message()
211 mj := values[j].Message()
212 return mi[0][1].String() < mj[0][1].String()
213 }
214 switch keyKind {
215 case pref.Int32Kind, pref.Sint32Kind, pref.Sfixed32Kind:
216 less = func(i, j int) bool {
217 mi := values[i].Message()
218 mj := values[j].Message()
219 ni, _ := mi[0][1].Int(false)
220 nj, _ := mj[0][1].Int(false)
221 return ni < nj
222 }
223 case pref.Int64Kind, pref.Sint64Kind, pref.Sfixed64Kind:
224 less = func(i, j int) bool {
225 mi := values[i].Message()
226 mj := values[j].Message()
227 ni, _ := mi[0][1].Int(true)
228 nj, _ := mj[0][1].Int(true)
229 return ni < nj
230 }
231
232 case pref.Uint32Kind, pref.Fixed32Kind:
233 less = func(i, j int) bool {
234 mi := values[i].Message()
235 mj := values[j].Message()
236 ni, _ := mi[0][1].Uint(false)
237 nj, _ := mj[0][1].Uint(false)
238 return ni < nj
239 }
240 case pref.Uint64Kind, pref.Fixed64Kind:
241 less = func(i, j int) bool {
242 mi := values[i].Message()
243 mj := values[j].Message()
244 ni, _ := mi[0][1].Uint(true)
245 nj, _ := mj[0][1].Uint(true)
246 return ni < nj
247 }
248 }
249 sort.Slice(values, less)
250}