github.com/jhump/protoreflect@v1.16.0/desc/protoprint/message_literal.go (about) 1 package protoprint 2 3 import ( 4 "bytes" 5 "fmt" 6 "sort" 7 "strings" 8 9 "google.golang.org/protobuf/proto" 10 "google.golang.org/protobuf/reflect/protoreflect" 11 "google.golang.org/protobuf/reflect/protoregistry" 12 "google.golang.org/protobuf/types/dynamicpb" 13 "google.golang.org/protobuf/types/known/anypb" 14 ) 15 16 func (p *Printer) printMessageLiteralCompact(msg protoreflect.Message, res *protoregistry.Types, pkg, scope string) string { 17 var buf bytes.Buffer 18 p.printMessageLiteralToBuffer(&buf, msg, res, pkg, scope, 0, -1) 19 return buf.String() 20 } 21 22 func (p *Printer) printMessageLiteral(msg protoreflect.Message, res *protoregistry.Types, pkg, scope string, threshold, indent int) string { 23 var buf bytes.Buffer 24 p.printMessageLiteralToBuffer(&buf, msg, res, pkg, scope, threshold, indent) 25 return buf.String() 26 } 27 28 var ( 29 anyTypeName = (&anypb.Any{}).ProtoReflect().Descriptor().FullName() 30 ) 31 32 const ( 33 anyTypeUrlTag = 1 34 anyValueTag = 2 35 ) 36 37 func (p *Printer) printMessageLiteralToBuffer(buf *bytes.Buffer, msg protoreflect.Message, res *protoregistry.Types, pkg, scope string, threshold, indent int) { 38 if p.maybePrintAnyMessageToBuffer(buf, msg, res, pkg, scope, threshold, indent) { 39 return 40 } 41 42 buf.WriteRune('{') 43 if indent >= 0 { 44 indent++ 45 } 46 47 type fieldVal struct { 48 fld protoreflect.FieldDescriptor 49 val protoreflect.Value 50 } 51 var fields []fieldVal 52 msg.Range(func(fld protoreflect.FieldDescriptor, val protoreflect.Value) bool { 53 fields = append(fields, fieldVal{fld: fld, val: val}) 54 return true 55 }) 56 sort.Slice(fields, func(i, j int) bool { 57 return fields[i].fld.Number() < fields[j].fld.Number() 58 }) 59 60 for i, fldVal := range fields { 61 fld, val := fldVal.fld, fldVal.val 62 if i > 0 { 63 buf.WriteRune(',') 64 } 65 p.maybeNewline(buf, indent) 66 if fld.IsExtension() { 67 buf.WriteRune('[') 68 buf.WriteString(p.qualifyExtensionLiteralName(pkg, scope, string(fld.FullName()))) 69 buf.WriteRune(']') 70 } else { 71 buf.WriteString(string(fld.Name())) 72 } 73 buf.WriteString(": ") 74 switch { 75 case fld.IsList(): 76 p.printArrayLiteralToBufferMaybeCompact(buf, fld, val.List(), res, pkg, scope, threshold, indent) 77 case fld.IsMap(): 78 p.printMapLiteralToBufferMaybeCompact(buf, fld, val.Map(), res, pkg, scope, threshold, indent) 79 case fld.Kind() == protoreflect.MessageKind || fld.Kind() == protoreflect.GroupKind: 80 p.printMessageLiteralToBufferMaybeCompact(buf, val.Message(), res, pkg, scope, threshold, indent) 81 default: 82 p.printValueLiteralToBuffer(buf, fld, val.Interface()) 83 } 84 } 85 86 if indent >= 0 { 87 indent-- 88 } 89 p.maybeNewline(buf, indent) 90 buf.WriteRune('}') 91 } 92 93 func (p *Printer) printMessageLiteralToBufferMaybeCompact(buf *bytes.Buffer, msg protoreflect.Message, res *protoregistry.Types, pkg, scope string, threshold, indent int) { 94 if indent >= 0 { 95 // first see if the message is compact enough to fit on one line 96 str := p.printMessageLiteralCompact(msg, res, pkg, scope) 97 fieldCount := strings.Count(str, ",") 98 nestedCount := strings.Count(str, "{") - 1 99 if fieldCount <= 1 && nestedCount == 0 { 100 // can't expand 101 buf.WriteString(str) 102 return 103 } 104 if len(str) <= threshold { 105 // no need to expand 106 buf.WriteString(str) 107 return 108 } 109 } 110 p.printMessageLiteralToBuffer(buf, msg, res, pkg, scope, threshold, indent) 111 } 112 113 func (p *Printer) maybePrintAnyMessageToBuffer(buf *bytes.Buffer, msg protoreflect.Message, res *protoregistry.Types, pkg, scope string, threshold, indent int) bool { 114 md := msg.Descriptor() 115 if md.FullName() != anyTypeName { 116 return false 117 } 118 typeUrlFld := md.Fields().ByNumber(anyTypeUrlTag) 119 if typeUrlFld == nil || typeUrlFld.Kind() != protoreflect.StringKind || typeUrlFld.Cardinality() == protoreflect.Repeated { 120 return false 121 } 122 valueFld := md.Fields().ByNumber(anyValueTag) 123 if valueFld == nil || valueFld.Kind() != protoreflect.BytesKind || valueFld.Cardinality() == protoreflect.Repeated { 124 return false 125 } 126 typeUrl := msg.Get(typeUrlFld).String() 127 if typeUrl == "" { 128 return false 129 } 130 mt, err := res.FindMessageByURL(typeUrl) 131 if err != nil { 132 return false 133 } 134 valueMsg := mt.New() 135 valueBytes := msg.Get(valueFld).Bytes() 136 if err := (proto.UnmarshalOptions{Resolver: res}).Unmarshal(valueBytes, valueMsg.Interface()); err != nil { 137 return false 138 } 139 140 buf.WriteRune('{') 141 if indent >= 0 { 142 indent++ 143 } 144 p.maybeNewline(buf, indent) 145 146 buf.WriteRune('[') 147 buf.WriteString(typeUrl) 148 buf.WriteString("]: ") 149 p.printMessageLiteralToBufferMaybeCompact(buf, valueMsg, res, pkg, scope, threshold, indent) 150 151 if indent >= 0 { 152 indent-- 153 } 154 p.maybeNewline(buf, indent) 155 buf.WriteRune('}') 156 157 return true 158 } 159 160 func (p *Printer) printValueLiteralToBuffer(buf *bytes.Buffer, fld protoreflect.FieldDescriptor, value interface{}) { 161 switch val := value.(type) { 162 case protoreflect.EnumNumber: 163 ev := fld.Enum().Values().ByNumber(val) 164 if ev == nil { 165 _, _ = fmt.Fprintf(buf, "%v", value) 166 } else { 167 buf.WriteString(string(ev.Name())) 168 } 169 case string: 170 buf.WriteString(quotedString(val)) 171 case []byte: 172 buf.WriteString(quotedBytes(string(val))) 173 case int32, uint32, int64, uint64: 174 _, _ = fmt.Fprintf(buf, "%d", val) 175 case float32, float64: 176 _, _ = fmt.Fprintf(buf, "%f", val) 177 default: 178 _, _ = fmt.Fprintf(buf, "%v", val) 179 } 180 } 181 182 func (p *Printer) maybeNewline(buf *bytes.Buffer, indent int) { 183 if indent < 0 { 184 // compact form 185 buf.WriteRune(' ') 186 return 187 } 188 buf.WriteRune('\n') 189 p.indent(buf, indent) 190 } 191 192 func (p *Printer) printArrayLiteralToBufferMaybeCompact(buf *bytes.Buffer, fld protoreflect.FieldDescriptor, val protoreflect.List, res *protoregistry.Types, pkg, scope string, threshold, indent int) { 193 if indent >= 0 { 194 // first see if the array is compact enough to fit on one line 195 str := p.printArrayLiteralCompact(fld, val, res, pkg, scope) 196 elementCount := strings.Count(str, ",") 197 nestedCount := strings.Count(str, "{") - 1 198 if elementCount <= 1 && nestedCount == 0 { 199 // can't expand 200 buf.WriteString(str) 201 return 202 } 203 if len(str) <= threshold { 204 // no need to expand 205 buf.WriteString(str) 206 return 207 } 208 } 209 p.printArrayLiteralToBuffer(buf, fld, val, res, pkg, scope, threshold, indent) 210 } 211 212 func (p *Printer) printArrayLiteralCompact(fld protoreflect.FieldDescriptor, val protoreflect.List, res *protoregistry.Types, pkg, scope string) string { 213 var buf bytes.Buffer 214 p.printArrayLiteralToBuffer(&buf, fld, val, res, pkg, scope, 0, -1) 215 return buf.String() 216 } 217 218 func (p *Printer) printArrayLiteralToBuffer(buf *bytes.Buffer, fld protoreflect.FieldDescriptor, val protoreflect.List, res *protoregistry.Types, pkg, scope string, threshold, indent int) { 219 buf.WriteRune('[') 220 if indent >= 0 { 221 indent++ 222 } 223 224 for i := 0; i < val.Len(); i++ { 225 if i > 0 { 226 buf.WriteRune(',') 227 } 228 p.maybeNewline(buf, indent) 229 if fld.Kind() == protoreflect.MessageKind || fld.Kind() == protoreflect.GroupKind { 230 p.printMessageLiteralToBufferMaybeCompact(buf, val.Get(i).Message(), res, pkg, scope, threshold, indent) 231 } else { 232 p.printValueLiteralToBuffer(buf, fld, val.Get(i).Interface()) 233 } 234 } 235 236 if indent >= 0 { 237 indent-- 238 } 239 p.maybeNewline(buf, indent) 240 buf.WriteRune(']') 241 } 242 243 func (p *Printer) printMapLiteralToBufferMaybeCompact(buf *bytes.Buffer, fld protoreflect.FieldDescriptor, val protoreflect.Map, res *protoregistry.Types, pkg, scope string, threshold, indent int) { 244 if indent >= 0 { 245 // first see if the map is compact enough to fit on one line 246 str := p.printMapLiteralCompact(fld, val, res, pkg, scope) 247 if len(str) <= threshold { 248 buf.WriteString(str) 249 return 250 } 251 } 252 p.printMapLiteralToBuffer(buf, fld, val, res, pkg, scope, threshold, indent) 253 } 254 255 func (p *Printer) printMapLiteralCompact(fld protoreflect.FieldDescriptor, val protoreflect.Map, res *protoregistry.Types, pkg, scope string) string { 256 var buf bytes.Buffer 257 p.printMapLiteralToBuffer(&buf, fld, val, res, pkg, scope, 0, -1) 258 return buf.String() 259 } 260 261 func (p *Printer) printMapLiteralToBuffer(buf *bytes.Buffer, fld protoreflect.FieldDescriptor, val protoreflect.Map, res *protoregistry.Types, pkg, scope string, threshold, indent int) { 262 keys := sortKeys(val) 263 l := &mapAsList{ 264 m: val, 265 entry: dynamicpb.NewMessageType(fld.Message()), 266 keyFld: fld.MapKey(), 267 valFld: fld.MapValue(), 268 keys: keys, 269 } 270 p.printArrayLiteralToBuffer(buf, fld, l, res, pkg, scope, threshold, indent) 271 } 272 273 type mapAsList struct { 274 m protoreflect.Map 275 entry protoreflect.MessageType 276 keyFld, valFld protoreflect.FieldDescriptor 277 keys []protoreflect.MapKey 278 } 279 280 func (m *mapAsList) Len() int { 281 return len(m.keys) 282 } 283 284 func (m *mapAsList) Get(i int) protoreflect.Value { 285 msg := m.entry.New() 286 key := m.keys[i] 287 msg.Set(m.keyFld, key.Value()) 288 val := m.m.Get(key) 289 msg.Set(m.valFld, val) 290 return protoreflect.ValueOfMessage(msg) 291 } 292 293 func (m *mapAsList) Set(_i int, _ protoreflect.Value) { 294 panic("Set is not implemented") 295 } 296 297 func (m *mapAsList) Append(_ protoreflect.Value) { 298 panic("Append is not implemented") 299 } 300 301 func (m *mapAsList) AppendMutable() protoreflect.Value { 302 panic("AppendMutable is not implemented") 303 } 304 305 func (m *mapAsList) Truncate(_ int) { 306 panic("Truncate is not implemented") 307 } 308 309 func (m *mapAsList) NewElement() protoreflect.Value { 310 panic("NewElement is not implemented") 311 } 312 313 func (m *mapAsList) IsValid() bool { 314 return true 315 }