protobuf-go/internal/msgfmt/format.go

262 lines
7.5 KiB
Go
Raw Normal View History

internal/msgfmt: use msgfmt package to format messages Port message formatting logic in testing/protocmp to internal/msgfmt and improve upon its output. This formatter is optimized for humanly readable output. It takes the best parts of both the JSON and proto text formats. The good of prototext: * It supports emitting unknown fields (useful for debugging). * It is relatively concise in the common-case since keys do not need to be represented as quoted strings (e.g., "key" vs key). The bad of prototext: * Requires relatively large dependency on encoding/prototext. * Our implementation lacks support for serializing packed lists. * Lacks support for readable maps. * Lacks support for readable Timestamp and Duration. The good of protojson: * First-class support for readable maps. * First-class support for readable Timestamp and Duration. The bad of protojson: * Requires relatively large dependency on encoding/protojson. * Lacks support for emitting unknown fields. * More verbose in the common-case as keys are quoted strings. The msgfmt package has the benefits of both protojson and prototext, but none of the detriments. It is a relatively simple implementation. This output is only intended for human consumption with no associated deserialization implementation. To avoid any illusion that this is identical to either the proto text or JSON formats, this always emits surrounding "{}" for top-level messages and the keys are not quoted strings. This CL does not use this format for generated Message.String methods as there is concerns about being inconsistent with the String methods as implemented in other languages. Having it be a seperate package makes it trivial to switch over to this if desired. Change-Id: I8b3581904d1624e84bf1b1954d2f01e5774b7f87 Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/223752 Reviewed-by: Damien Neil <dneil@google.com>
2020-02-29 02:37:16 +00:00
// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package msgfmt implements a text marshaler combining the desirable features
// of both the JSON and proto text formats.
// It is optimized for human readability and has no associated deserializer.
package msgfmt
import (
"bytes"
"fmt"
"reflect"
"sort"
"strconv"
"strings"
"time"
"google.golang.org/protobuf/encoding/protowire"
"google.golang.org/protobuf/internal/detrand"
"google.golang.org/protobuf/internal/genid"
"google.golang.org/protobuf/internal/order"
internal/msgfmt: use msgfmt package to format messages Port message formatting logic in testing/protocmp to internal/msgfmt and improve upon its output. This formatter is optimized for humanly readable output. It takes the best parts of both the JSON and proto text formats. The good of prototext: * It supports emitting unknown fields (useful for debugging). * It is relatively concise in the common-case since keys do not need to be represented as quoted strings (e.g., "key" vs key). The bad of prototext: * Requires relatively large dependency on encoding/prototext. * Our implementation lacks support for serializing packed lists. * Lacks support for readable maps. * Lacks support for readable Timestamp and Duration. The good of protojson: * First-class support for readable maps. * First-class support for readable Timestamp and Duration. The bad of protojson: * Requires relatively large dependency on encoding/protojson. * Lacks support for emitting unknown fields. * More verbose in the common-case as keys are quoted strings. The msgfmt package has the benefits of both protojson and prototext, but none of the detriments. It is a relatively simple implementation. This output is only intended for human consumption with no associated deserialization implementation. To avoid any illusion that this is identical to either the proto text or JSON formats, this always emits surrounding "{}" for top-level messages and the keys are not quoted strings. This CL does not use this format for generated Message.String methods as there is concerns about being inconsistent with the String methods as implemented in other languages. Having it be a seperate package makes it trivial to switch over to this if desired. Change-Id: I8b3581904d1624e84bf1b1954d2f01e5774b7f87 Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/223752 Reviewed-by: Damien Neil <dneil@google.com>
2020-02-29 02:37:16 +00:00
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/reflect/protoreflect"
"google.golang.org/protobuf/reflect/protoregistry"
)
// Format returns a formatted string for the message.
func Format(m proto.Message) string {
return string(appendMessage(nil, m.ProtoReflect()))
}
reflect: add protopath and protorange packages The protopath package provides a means to programmatically represent a sequence of protobuf reflection operations. The protorange package traverses through a message and calls a user-provided function as it iterates. This feature sets the groundwork for the often requested feature of being able to exclude certain fields when merging or serializing. package protopath type Path []Step type Step struct{ ... } func Root(protoreflect.MessageDescriptor) Step func FieldAccess(protoreflect.FieldDescriptor) Step func UnknownAccess() Step func ListIndex(int) Step func MapIndex(protoreflect.MapKey) Step func AnyExpand(protoreflect.MessageDescriptor) Step func (Step) Kind() StepKind func (Step) FieldDescriptor() protoreflect.FieldDescriptor func (Step) MessageDescriptor() protoreflect.MessageDescriptor func (Step) ListIndex() int func (Step) MapIndex() protoreflect.MapKey func (Step) String() string type StepKind int const RootStep StepKind const FieldAccessStep StepKind const UnknownAccessStep StepKind const ListIndexStep StepKind const MapIndexStep StepKind const AnyExpandStep StepKind type Values struct { Path Path Values []protoreflect.Value } func (Values) Index(int) (out struct{ ... }) func (Values) Len() int func (Values) String() string package protorange var Break error var Terminate error func Range(protoreflect.Message, func(protopath.Values) error) error type Options struct { Stable bool Resolver interface { ... } } func (Options) Range(m protoreflect.Message, push, pop func(protopath.Values) error) error Change-Id: I29cbd5142fe169d78367d54a95d37801888b64f4 Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/236540 Trust: Joe Tsai <joetsai@digital-static.net> Reviewed-by: Damien Neil <dneil@google.com>
2020-05-12 00:47:32 +00:00
// FormatValue returns a formatted string for an arbitrary value.
func FormatValue(v protoreflect.Value, fd protoreflect.FieldDescriptor) string {
return string(appendValue(nil, v, fd))
}
internal/msgfmt: use msgfmt package to format messages Port message formatting logic in testing/protocmp to internal/msgfmt and improve upon its output. This formatter is optimized for humanly readable output. It takes the best parts of both the JSON and proto text formats. The good of prototext: * It supports emitting unknown fields (useful for debugging). * It is relatively concise in the common-case since keys do not need to be represented as quoted strings (e.g., "key" vs key). The bad of prototext: * Requires relatively large dependency on encoding/prototext. * Our implementation lacks support for serializing packed lists. * Lacks support for readable maps. * Lacks support for readable Timestamp and Duration. The good of protojson: * First-class support for readable maps. * First-class support for readable Timestamp and Duration. The bad of protojson: * Requires relatively large dependency on encoding/protojson. * Lacks support for emitting unknown fields. * More verbose in the common-case as keys are quoted strings. The msgfmt package has the benefits of both protojson and prototext, but none of the detriments. It is a relatively simple implementation. This output is only intended for human consumption with no associated deserialization implementation. To avoid any illusion that this is identical to either the proto text or JSON formats, this always emits surrounding "{}" for top-level messages and the keys are not quoted strings. This CL does not use this format for generated Message.String methods as there is concerns about being inconsistent with the String methods as implemented in other languages. Having it be a seperate package makes it trivial to switch over to this if desired. Change-Id: I8b3581904d1624e84bf1b1954d2f01e5774b7f87 Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/223752 Reviewed-by: Damien Neil <dneil@google.com>
2020-02-29 02:37:16 +00:00
func appendValue(b []byte, v protoreflect.Value, fd protoreflect.FieldDescriptor) []byte {
switch v := v.Interface().(type) {
reflect: add protopath and protorange packages The protopath package provides a means to programmatically represent a sequence of protobuf reflection operations. The protorange package traverses through a message and calls a user-provided function as it iterates. This feature sets the groundwork for the often requested feature of being able to exclude certain fields when merging or serializing. package protopath type Path []Step type Step struct{ ... } func Root(protoreflect.MessageDescriptor) Step func FieldAccess(protoreflect.FieldDescriptor) Step func UnknownAccess() Step func ListIndex(int) Step func MapIndex(protoreflect.MapKey) Step func AnyExpand(protoreflect.MessageDescriptor) Step func (Step) Kind() StepKind func (Step) FieldDescriptor() protoreflect.FieldDescriptor func (Step) MessageDescriptor() protoreflect.MessageDescriptor func (Step) ListIndex() int func (Step) MapIndex() protoreflect.MapKey func (Step) String() string type StepKind int const RootStep StepKind const FieldAccessStep StepKind const UnknownAccessStep StepKind const ListIndexStep StepKind const MapIndexStep StepKind const AnyExpandStep StepKind type Values struct { Path Path Values []protoreflect.Value } func (Values) Index(int) (out struct{ ... }) func (Values) Len() int func (Values) String() string package protorange var Break error var Terminate error func Range(protoreflect.Message, func(protopath.Values) error) error type Options struct { Stable bool Resolver interface { ... } } func (Options) Range(m protoreflect.Message, push, pop func(protopath.Values) error) error Change-Id: I29cbd5142fe169d78367d54a95d37801888b64f4 Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/236540 Trust: Joe Tsai <joetsai@digital-static.net> Reviewed-by: Damien Neil <dneil@google.com>
2020-05-12 00:47:32 +00:00
case nil:
return append(b, "<invalid>"...)
internal/msgfmt: use msgfmt package to format messages Port message formatting logic in testing/protocmp to internal/msgfmt and improve upon its output. This formatter is optimized for humanly readable output. It takes the best parts of both the JSON and proto text formats. The good of prototext: * It supports emitting unknown fields (useful for debugging). * It is relatively concise in the common-case since keys do not need to be represented as quoted strings (e.g., "key" vs key). The bad of prototext: * Requires relatively large dependency on encoding/prototext. * Our implementation lacks support for serializing packed lists. * Lacks support for readable maps. * Lacks support for readable Timestamp and Duration. The good of protojson: * First-class support for readable maps. * First-class support for readable Timestamp and Duration. The bad of protojson: * Requires relatively large dependency on encoding/protojson. * Lacks support for emitting unknown fields. * More verbose in the common-case as keys are quoted strings. The msgfmt package has the benefits of both protojson and prototext, but none of the detriments. It is a relatively simple implementation. This output is only intended for human consumption with no associated deserialization implementation. To avoid any illusion that this is identical to either the proto text or JSON formats, this always emits surrounding "{}" for top-level messages and the keys are not quoted strings. This CL does not use this format for generated Message.String methods as there is concerns about being inconsistent with the String methods as implemented in other languages. Having it be a seperate package makes it trivial to switch over to this if desired. Change-Id: I8b3581904d1624e84bf1b1954d2f01e5774b7f87 Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/223752 Reviewed-by: Damien Neil <dneil@google.com>
2020-02-29 02:37:16 +00:00
case bool, int32, int64, uint32, uint64, float32, float64:
return append(b, fmt.Sprint(v)...)
case string:
return append(b, strconv.Quote(string(v))...)
case []byte:
return append(b, strconv.Quote(string(v))...)
case protoreflect.EnumNumber:
reflect: add protopath and protorange packages The protopath package provides a means to programmatically represent a sequence of protobuf reflection operations. The protorange package traverses through a message and calls a user-provided function as it iterates. This feature sets the groundwork for the often requested feature of being able to exclude certain fields when merging or serializing. package protopath type Path []Step type Step struct{ ... } func Root(protoreflect.MessageDescriptor) Step func FieldAccess(protoreflect.FieldDescriptor) Step func UnknownAccess() Step func ListIndex(int) Step func MapIndex(protoreflect.MapKey) Step func AnyExpand(protoreflect.MessageDescriptor) Step func (Step) Kind() StepKind func (Step) FieldDescriptor() protoreflect.FieldDescriptor func (Step) MessageDescriptor() protoreflect.MessageDescriptor func (Step) ListIndex() int func (Step) MapIndex() protoreflect.MapKey func (Step) String() string type StepKind int const RootStep StepKind const FieldAccessStep StepKind const UnknownAccessStep StepKind const ListIndexStep StepKind const MapIndexStep StepKind const AnyExpandStep StepKind type Values struct { Path Path Values []protoreflect.Value } func (Values) Index(int) (out struct{ ... }) func (Values) Len() int func (Values) String() string package protorange var Break error var Terminate error func Range(protoreflect.Message, func(protopath.Values) error) error type Options struct { Stable bool Resolver interface { ... } } func (Options) Range(m protoreflect.Message, push, pop func(protopath.Values) error) error Change-Id: I29cbd5142fe169d78367d54a95d37801888b64f4 Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/236540 Trust: Joe Tsai <joetsai@digital-static.net> Reviewed-by: Damien Neil <dneil@google.com>
2020-05-12 00:47:32 +00:00
return appendEnum(b, v, fd)
internal/msgfmt: use msgfmt package to format messages Port message formatting logic in testing/protocmp to internal/msgfmt and improve upon its output. This formatter is optimized for humanly readable output. It takes the best parts of both the JSON and proto text formats. The good of prototext: * It supports emitting unknown fields (useful for debugging). * It is relatively concise in the common-case since keys do not need to be represented as quoted strings (e.g., "key" vs key). The bad of prototext: * Requires relatively large dependency on encoding/prototext. * Our implementation lacks support for serializing packed lists. * Lacks support for readable maps. * Lacks support for readable Timestamp and Duration. The good of protojson: * First-class support for readable maps. * First-class support for readable Timestamp and Duration. The bad of protojson: * Requires relatively large dependency on encoding/protojson. * Lacks support for emitting unknown fields. * More verbose in the common-case as keys are quoted strings. The msgfmt package has the benefits of both protojson and prototext, but none of the detriments. It is a relatively simple implementation. This output is only intended for human consumption with no associated deserialization implementation. To avoid any illusion that this is identical to either the proto text or JSON formats, this always emits surrounding "{}" for top-level messages and the keys are not quoted strings. This CL does not use this format for generated Message.String methods as there is concerns about being inconsistent with the String methods as implemented in other languages. Having it be a seperate package makes it trivial to switch over to this if desired. Change-Id: I8b3581904d1624e84bf1b1954d2f01e5774b7f87 Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/223752 Reviewed-by: Damien Neil <dneil@google.com>
2020-02-29 02:37:16 +00:00
case protoreflect.Message:
return appendMessage(b, v)
case protoreflect.List:
return appendList(b, v, fd)
case protoreflect.Map:
return appendMap(b, v, fd)
default:
panic(fmt.Sprintf("invalid type: %T", v))
}
}
reflect: add protopath and protorange packages The protopath package provides a means to programmatically represent a sequence of protobuf reflection operations. The protorange package traverses through a message and calls a user-provided function as it iterates. This feature sets the groundwork for the often requested feature of being able to exclude certain fields when merging or serializing. package protopath type Path []Step type Step struct{ ... } func Root(protoreflect.MessageDescriptor) Step func FieldAccess(protoreflect.FieldDescriptor) Step func UnknownAccess() Step func ListIndex(int) Step func MapIndex(protoreflect.MapKey) Step func AnyExpand(protoreflect.MessageDescriptor) Step func (Step) Kind() StepKind func (Step) FieldDescriptor() protoreflect.FieldDescriptor func (Step) MessageDescriptor() protoreflect.MessageDescriptor func (Step) ListIndex() int func (Step) MapIndex() protoreflect.MapKey func (Step) String() string type StepKind int const RootStep StepKind const FieldAccessStep StepKind const UnknownAccessStep StepKind const ListIndexStep StepKind const MapIndexStep StepKind const AnyExpandStep StepKind type Values struct { Path Path Values []protoreflect.Value } func (Values) Index(int) (out struct{ ... }) func (Values) Len() int func (Values) String() string package protorange var Break error var Terminate error func Range(protoreflect.Message, func(protopath.Values) error) error type Options struct { Stable bool Resolver interface { ... } } func (Options) Range(m protoreflect.Message, push, pop func(protopath.Values) error) error Change-Id: I29cbd5142fe169d78367d54a95d37801888b64f4 Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/236540 Trust: Joe Tsai <joetsai@digital-static.net> Reviewed-by: Damien Neil <dneil@google.com>
2020-05-12 00:47:32 +00:00
func appendEnum(b []byte, v protoreflect.EnumNumber, fd protoreflect.FieldDescriptor) []byte {
if fd != nil {
if ev := fd.Enum().Values().ByNumber(v); ev != nil {
return append(b, ev.Name()...)
}
internal/msgfmt: use msgfmt package to format messages Port message formatting logic in testing/protocmp to internal/msgfmt and improve upon its output. This formatter is optimized for humanly readable output. It takes the best parts of both the JSON and proto text formats. The good of prototext: * It supports emitting unknown fields (useful for debugging). * It is relatively concise in the common-case since keys do not need to be represented as quoted strings (e.g., "key" vs key). The bad of prototext: * Requires relatively large dependency on encoding/prototext. * Our implementation lacks support for serializing packed lists. * Lacks support for readable maps. * Lacks support for readable Timestamp and Duration. The good of protojson: * First-class support for readable maps. * First-class support for readable Timestamp and Duration. The bad of protojson: * Requires relatively large dependency on encoding/protojson. * Lacks support for emitting unknown fields. * More verbose in the common-case as keys are quoted strings. The msgfmt package has the benefits of both protojson and prototext, but none of the detriments. It is a relatively simple implementation. This output is only intended for human consumption with no associated deserialization implementation. To avoid any illusion that this is identical to either the proto text or JSON formats, this always emits surrounding "{}" for top-level messages and the keys are not quoted strings. This CL does not use this format for generated Message.String methods as there is concerns about being inconsistent with the String methods as implemented in other languages. Having it be a seperate package makes it trivial to switch over to this if desired. Change-Id: I8b3581904d1624e84bf1b1954d2f01e5774b7f87 Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/223752 Reviewed-by: Damien Neil <dneil@google.com>
2020-02-29 02:37:16 +00:00
}
return strconv.AppendInt(b, int64(v), 10)
}
func appendMessage(b []byte, m protoreflect.Message) []byte {
if b2 := appendKnownMessage(b, m); b2 != nil {
return b2
}
b = append(b, '{')
order.RangeFields(m, order.IndexNameFieldOrder, func(fd protoreflect.FieldDescriptor, v protoreflect.Value) bool {
b = append(b, fd.TextName()...)
internal/msgfmt: use msgfmt package to format messages Port message formatting logic in testing/protocmp to internal/msgfmt and improve upon its output. This formatter is optimized for humanly readable output. It takes the best parts of both the JSON and proto text formats. The good of prototext: * It supports emitting unknown fields (useful for debugging). * It is relatively concise in the common-case since keys do not need to be represented as quoted strings (e.g., "key" vs key). The bad of prototext: * Requires relatively large dependency on encoding/prototext. * Our implementation lacks support for serializing packed lists. * Lacks support for readable maps. * Lacks support for readable Timestamp and Duration. The good of protojson: * First-class support for readable maps. * First-class support for readable Timestamp and Duration. The bad of protojson: * Requires relatively large dependency on encoding/protojson. * Lacks support for emitting unknown fields. * More verbose in the common-case as keys are quoted strings. The msgfmt package has the benefits of both protojson and prototext, but none of the detriments. It is a relatively simple implementation. This output is only intended for human consumption with no associated deserialization implementation. To avoid any illusion that this is identical to either the proto text or JSON formats, this always emits surrounding "{}" for top-level messages and the keys are not quoted strings. This CL does not use this format for generated Message.String methods as there is concerns about being inconsistent with the String methods as implemented in other languages. Having it be a seperate package makes it trivial to switch over to this if desired. Change-Id: I8b3581904d1624e84bf1b1954d2f01e5774b7f87 Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/223752 Reviewed-by: Damien Neil <dneil@google.com>
2020-02-29 02:37:16 +00:00
b = append(b, ':')
b = appendValue(b, v, fd)
internal/msgfmt: use msgfmt package to format messages Port message formatting logic in testing/protocmp to internal/msgfmt and improve upon its output. This formatter is optimized for humanly readable output. It takes the best parts of both the JSON and proto text formats. The good of prototext: * It supports emitting unknown fields (useful for debugging). * It is relatively concise in the common-case since keys do not need to be represented as quoted strings (e.g., "key" vs key). The bad of prototext: * Requires relatively large dependency on encoding/prototext. * Our implementation lacks support for serializing packed lists. * Lacks support for readable maps. * Lacks support for readable Timestamp and Duration. The good of protojson: * First-class support for readable maps. * First-class support for readable Timestamp and Duration. The bad of protojson: * Requires relatively large dependency on encoding/protojson. * Lacks support for emitting unknown fields. * More verbose in the common-case as keys are quoted strings. The msgfmt package has the benefits of both protojson and prototext, but none of the detriments. It is a relatively simple implementation. This output is only intended for human consumption with no associated deserialization implementation. To avoid any illusion that this is identical to either the proto text or JSON formats, this always emits surrounding "{}" for top-level messages and the keys are not quoted strings. This CL does not use this format for generated Message.String methods as there is concerns about being inconsistent with the String methods as implemented in other languages. Having it be a seperate package makes it trivial to switch over to this if desired. Change-Id: I8b3581904d1624e84bf1b1954d2f01e5774b7f87 Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/223752 Reviewed-by: Damien Neil <dneil@google.com>
2020-02-29 02:37:16 +00:00
b = append(b, delim()...)
return true
})
internal/msgfmt: use msgfmt package to format messages Port message formatting logic in testing/protocmp to internal/msgfmt and improve upon its output. This formatter is optimized for humanly readable output. It takes the best parts of both the JSON and proto text formats. The good of prototext: * It supports emitting unknown fields (useful for debugging). * It is relatively concise in the common-case since keys do not need to be represented as quoted strings (e.g., "key" vs key). The bad of prototext: * Requires relatively large dependency on encoding/prototext. * Our implementation lacks support for serializing packed lists. * Lacks support for readable maps. * Lacks support for readable Timestamp and Duration. The good of protojson: * First-class support for readable maps. * First-class support for readable Timestamp and Duration. The bad of protojson: * Requires relatively large dependency on encoding/protojson. * Lacks support for emitting unknown fields. * More verbose in the common-case as keys are quoted strings. The msgfmt package has the benefits of both protojson and prototext, but none of the detriments. It is a relatively simple implementation. This output is only intended for human consumption with no associated deserialization implementation. To avoid any illusion that this is identical to either the proto text or JSON formats, this always emits surrounding "{}" for top-level messages and the keys are not quoted strings. This CL does not use this format for generated Message.String methods as there is concerns about being inconsistent with the String methods as implemented in other languages. Having it be a seperate package makes it trivial to switch over to this if desired. Change-Id: I8b3581904d1624e84bf1b1954d2f01e5774b7f87 Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/223752 Reviewed-by: Damien Neil <dneil@google.com>
2020-02-29 02:37:16 +00:00
b = appendUnknown(b, m.GetUnknown())
b = bytes.TrimRight(b, delim())
b = append(b, '}')
return b
}
var protocmpMessageType = reflect.TypeOf(map[string]any(nil))
internal/msgfmt: use msgfmt package to format messages Port message formatting logic in testing/protocmp to internal/msgfmt and improve upon its output. This formatter is optimized for humanly readable output. It takes the best parts of both the JSON and proto text formats. The good of prototext: * It supports emitting unknown fields (useful for debugging). * It is relatively concise in the common-case since keys do not need to be represented as quoted strings (e.g., "key" vs key). The bad of prototext: * Requires relatively large dependency on encoding/prototext. * Our implementation lacks support for serializing packed lists. * Lacks support for readable maps. * Lacks support for readable Timestamp and Duration. The good of protojson: * First-class support for readable maps. * First-class support for readable Timestamp and Duration. The bad of protojson: * Requires relatively large dependency on encoding/protojson. * Lacks support for emitting unknown fields. * More verbose in the common-case as keys are quoted strings. The msgfmt package has the benefits of both protojson and prototext, but none of the detriments. It is a relatively simple implementation. This output is only intended for human consumption with no associated deserialization implementation. To avoid any illusion that this is identical to either the proto text or JSON formats, this always emits surrounding "{}" for top-level messages and the keys are not quoted strings. This CL does not use this format for generated Message.String methods as there is concerns about being inconsistent with the String methods as implemented in other languages. Having it be a seperate package makes it trivial to switch over to this if desired. Change-Id: I8b3581904d1624e84bf1b1954d2f01e5774b7f87 Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/223752 Reviewed-by: Damien Neil <dneil@google.com>
2020-02-29 02:37:16 +00:00
func appendKnownMessage(b []byte, m protoreflect.Message) []byte {
md := m.Descriptor()
fds := md.Fields()
switch md.FullName() {
case genid.Any_message_fullname:
internal/msgfmt: use msgfmt package to format messages Port message formatting logic in testing/protocmp to internal/msgfmt and improve upon its output. This formatter is optimized for humanly readable output. It takes the best parts of both the JSON and proto text formats. The good of prototext: * It supports emitting unknown fields (useful for debugging). * It is relatively concise in the common-case since keys do not need to be represented as quoted strings (e.g., "key" vs key). The bad of prototext: * Requires relatively large dependency on encoding/prototext. * Our implementation lacks support for serializing packed lists. * Lacks support for readable maps. * Lacks support for readable Timestamp and Duration. The good of protojson: * First-class support for readable maps. * First-class support for readable Timestamp and Duration. The bad of protojson: * Requires relatively large dependency on encoding/protojson. * Lacks support for emitting unknown fields. * More verbose in the common-case as keys are quoted strings. The msgfmt package has the benefits of both protojson and prototext, but none of the detriments. It is a relatively simple implementation. This output is only intended for human consumption with no associated deserialization implementation. To avoid any illusion that this is identical to either the proto text or JSON formats, this always emits surrounding "{}" for top-level messages and the keys are not quoted strings. This CL does not use this format for generated Message.String methods as there is concerns about being inconsistent with the String methods as implemented in other languages. Having it be a seperate package makes it trivial to switch over to this if desired. Change-Id: I8b3581904d1624e84bf1b1954d2f01e5774b7f87 Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/223752 Reviewed-by: Damien Neil <dneil@google.com>
2020-02-29 02:37:16 +00:00
var msgVal protoreflect.Message
url := m.Get(fds.ByNumber(genid.Any_TypeUrl_field_number)).String()
internal/msgfmt: use msgfmt package to format messages Port message formatting logic in testing/protocmp to internal/msgfmt and improve upon its output. This formatter is optimized for humanly readable output. It takes the best parts of both the JSON and proto text formats. The good of prototext: * It supports emitting unknown fields (useful for debugging). * It is relatively concise in the common-case since keys do not need to be represented as quoted strings (e.g., "key" vs key). The bad of prototext: * Requires relatively large dependency on encoding/prototext. * Our implementation lacks support for serializing packed lists. * Lacks support for readable maps. * Lacks support for readable Timestamp and Duration. The good of protojson: * First-class support for readable maps. * First-class support for readable Timestamp and Duration. The bad of protojson: * Requires relatively large dependency on encoding/protojson. * Lacks support for emitting unknown fields. * More verbose in the common-case as keys are quoted strings. The msgfmt package has the benefits of both protojson and prototext, but none of the detriments. It is a relatively simple implementation. This output is only intended for human consumption with no associated deserialization implementation. To avoid any illusion that this is identical to either the proto text or JSON formats, this always emits surrounding "{}" for top-level messages and the keys are not quoted strings. This CL does not use this format for generated Message.String methods as there is concerns about being inconsistent with the String methods as implemented in other languages. Having it be a seperate package makes it trivial to switch over to this if desired. Change-Id: I8b3581904d1624e84bf1b1954d2f01e5774b7f87 Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/223752 Reviewed-by: Damien Neil <dneil@google.com>
2020-02-29 02:37:16 +00:00
if v := reflect.ValueOf(m); v.Type().ConvertibleTo(protocmpMessageType) {
// For protocmp.Message, directly obtain the sub-message value
// which is stored in structured form, rather than as raw bytes.
m2 := v.Convert(protocmpMessageType).Interface().(map[string]any)
v, ok := m2[string(genid.Any_Value_field_name)].(proto.Message)
internal/msgfmt: use msgfmt package to format messages Port message formatting logic in testing/protocmp to internal/msgfmt and improve upon its output. This formatter is optimized for humanly readable output. It takes the best parts of both the JSON and proto text formats. The good of prototext: * It supports emitting unknown fields (useful for debugging). * It is relatively concise in the common-case since keys do not need to be represented as quoted strings (e.g., "key" vs key). The bad of prototext: * Requires relatively large dependency on encoding/prototext. * Our implementation lacks support for serializing packed lists. * Lacks support for readable maps. * Lacks support for readable Timestamp and Duration. The good of protojson: * First-class support for readable maps. * First-class support for readable Timestamp and Duration. The bad of protojson: * Requires relatively large dependency on encoding/protojson. * Lacks support for emitting unknown fields. * More verbose in the common-case as keys are quoted strings. The msgfmt package has the benefits of both protojson and prototext, but none of the detriments. It is a relatively simple implementation. This output is only intended for human consumption with no associated deserialization implementation. To avoid any illusion that this is identical to either the proto text or JSON formats, this always emits surrounding "{}" for top-level messages and the keys are not quoted strings. This CL does not use this format for generated Message.String methods as there is concerns about being inconsistent with the String methods as implemented in other languages. Having it be a seperate package makes it trivial to switch over to this if desired. Change-Id: I8b3581904d1624e84bf1b1954d2f01e5774b7f87 Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/223752 Reviewed-by: Damien Neil <dneil@google.com>
2020-02-29 02:37:16 +00:00
if !ok {
return nil
}
msgVal = v.ProtoReflect()
} else {
val := m.Get(fds.ByNumber(genid.Any_Value_field_number)).Bytes()
internal/msgfmt: use msgfmt package to format messages Port message formatting logic in testing/protocmp to internal/msgfmt and improve upon its output. This formatter is optimized for humanly readable output. It takes the best parts of both the JSON and proto text formats. The good of prototext: * It supports emitting unknown fields (useful for debugging). * It is relatively concise in the common-case since keys do not need to be represented as quoted strings (e.g., "key" vs key). The bad of prototext: * Requires relatively large dependency on encoding/prototext. * Our implementation lacks support for serializing packed lists. * Lacks support for readable maps. * Lacks support for readable Timestamp and Duration. The good of protojson: * First-class support for readable maps. * First-class support for readable Timestamp and Duration. The bad of protojson: * Requires relatively large dependency on encoding/protojson. * Lacks support for emitting unknown fields. * More verbose in the common-case as keys are quoted strings. The msgfmt package has the benefits of both protojson and prototext, but none of the detriments. It is a relatively simple implementation. This output is only intended for human consumption with no associated deserialization implementation. To avoid any illusion that this is identical to either the proto text or JSON formats, this always emits surrounding "{}" for top-level messages and the keys are not quoted strings. This CL does not use this format for generated Message.String methods as there is concerns about being inconsistent with the String methods as implemented in other languages. Having it be a seperate package makes it trivial to switch over to this if desired. Change-Id: I8b3581904d1624e84bf1b1954d2f01e5774b7f87 Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/223752 Reviewed-by: Damien Neil <dneil@google.com>
2020-02-29 02:37:16 +00:00
mt, err := protoregistry.GlobalTypes.FindMessageByURL(url)
if err != nil {
return nil
}
msgVal = mt.New()
err = proto.UnmarshalOptions{AllowPartial: true}.Unmarshal(val, msgVal.Interface())
if err != nil {
return nil
}
}
b = append(b, '{')
b = append(b, "["+url+"]"...)
b = append(b, ':')
b = appendMessage(b, msgVal)
b = append(b, '}')
return b
case genid.Timestamp_message_fullname:
secs := m.Get(fds.ByNumber(genid.Timestamp_Seconds_field_number)).Int()
nanos := m.Get(fds.ByNumber(genid.Timestamp_Nanos_field_number)).Int()
if nanos < 0 || nanos >= 1e9 {
internal/msgfmt: use msgfmt package to format messages Port message formatting logic in testing/protocmp to internal/msgfmt and improve upon its output. This formatter is optimized for humanly readable output. It takes the best parts of both the JSON and proto text formats. The good of prototext: * It supports emitting unknown fields (useful for debugging). * It is relatively concise in the common-case since keys do not need to be represented as quoted strings (e.g., "key" vs key). The bad of prototext: * Requires relatively large dependency on encoding/prototext. * Our implementation lacks support for serializing packed lists. * Lacks support for readable maps. * Lacks support for readable Timestamp and Duration. The good of protojson: * First-class support for readable maps. * First-class support for readable Timestamp and Duration. The bad of protojson: * Requires relatively large dependency on encoding/protojson. * Lacks support for emitting unknown fields. * More verbose in the common-case as keys are quoted strings. The msgfmt package has the benefits of both protojson and prototext, but none of the detriments. It is a relatively simple implementation. This output is only intended for human consumption with no associated deserialization implementation. To avoid any illusion that this is identical to either the proto text or JSON formats, this always emits surrounding "{}" for top-level messages and the keys are not quoted strings. This CL does not use this format for generated Message.String methods as there is concerns about being inconsistent with the String methods as implemented in other languages. Having it be a seperate package makes it trivial to switch over to this if desired. Change-Id: I8b3581904d1624e84bf1b1954d2f01e5774b7f87 Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/223752 Reviewed-by: Damien Neil <dneil@google.com>
2020-02-29 02:37:16 +00:00
return nil
}
t := time.Unix(secs, nanos).UTC()
x := t.Format("2006-01-02T15:04:05.000000000") // RFC 3339
x = strings.TrimSuffix(x, "000")
x = strings.TrimSuffix(x, "000")
x = strings.TrimSuffix(x, ".000")
return append(b, x+"Z"...)
case genid.Duration_message_fullname:
sign := ""
secs := m.Get(fds.ByNumber(genid.Duration_Seconds_field_number)).Int()
nanos := m.Get(fds.ByNumber(genid.Duration_Nanos_field_number)).Int()
if nanos <= -1e9 || nanos >= 1e9 || (secs > 0 && nanos < 0) || (secs < 0 && nanos > 0) {
internal/msgfmt: use msgfmt package to format messages Port message formatting logic in testing/protocmp to internal/msgfmt and improve upon its output. This formatter is optimized for humanly readable output. It takes the best parts of both the JSON and proto text formats. The good of prototext: * It supports emitting unknown fields (useful for debugging). * It is relatively concise in the common-case since keys do not need to be represented as quoted strings (e.g., "key" vs key). The bad of prototext: * Requires relatively large dependency on encoding/prototext. * Our implementation lacks support for serializing packed lists. * Lacks support for readable maps. * Lacks support for readable Timestamp and Duration. The good of protojson: * First-class support for readable maps. * First-class support for readable Timestamp and Duration. The bad of protojson: * Requires relatively large dependency on encoding/protojson. * Lacks support for emitting unknown fields. * More verbose in the common-case as keys are quoted strings. The msgfmt package has the benefits of both protojson and prototext, but none of the detriments. It is a relatively simple implementation. This output is only intended for human consumption with no associated deserialization implementation. To avoid any illusion that this is identical to either the proto text or JSON formats, this always emits surrounding "{}" for top-level messages and the keys are not quoted strings. This CL does not use this format for generated Message.String methods as there is concerns about being inconsistent with the String methods as implemented in other languages. Having it be a seperate package makes it trivial to switch over to this if desired. Change-Id: I8b3581904d1624e84bf1b1954d2f01e5774b7f87 Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/223752 Reviewed-by: Damien Neil <dneil@google.com>
2020-02-29 02:37:16 +00:00
return nil
}
if secs < 0 || nanos < 0 {
sign, secs, nanos = "-", -1*secs, -1*nanos
}
x := fmt.Sprintf("%s%d.%09d", sign, secs, nanos)
internal/msgfmt: use msgfmt package to format messages Port message formatting logic in testing/protocmp to internal/msgfmt and improve upon its output. This formatter is optimized for humanly readable output. It takes the best parts of both the JSON and proto text formats. The good of prototext: * It supports emitting unknown fields (useful for debugging). * It is relatively concise in the common-case since keys do not need to be represented as quoted strings (e.g., "key" vs key). The bad of prototext: * Requires relatively large dependency on encoding/prototext. * Our implementation lacks support for serializing packed lists. * Lacks support for readable maps. * Lacks support for readable Timestamp and Duration. The good of protojson: * First-class support for readable maps. * First-class support for readable Timestamp and Duration. The bad of protojson: * Requires relatively large dependency on encoding/protojson. * Lacks support for emitting unknown fields. * More verbose in the common-case as keys are quoted strings. The msgfmt package has the benefits of both protojson and prototext, but none of the detriments. It is a relatively simple implementation. This output is only intended for human consumption with no associated deserialization implementation. To avoid any illusion that this is identical to either the proto text or JSON formats, this always emits surrounding "{}" for top-level messages and the keys are not quoted strings. This CL does not use this format for generated Message.String methods as there is concerns about being inconsistent with the String methods as implemented in other languages. Having it be a seperate package makes it trivial to switch over to this if desired. Change-Id: I8b3581904d1624e84bf1b1954d2f01e5774b7f87 Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/223752 Reviewed-by: Damien Neil <dneil@google.com>
2020-02-29 02:37:16 +00:00
x = strings.TrimSuffix(x, "000")
x = strings.TrimSuffix(x, "000")
x = strings.TrimSuffix(x, ".000")
return append(b, x+"s"...)
case genid.BoolValue_message_fullname,
genid.Int32Value_message_fullname,
genid.Int64Value_message_fullname,
genid.UInt32Value_message_fullname,
genid.UInt64Value_message_fullname,
genid.FloatValue_message_fullname,
genid.DoubleValue_message_fullname,
genid.StringValue_message_fullname,
genid.BytesValue_message_fullname:
fd := fds.ByNumber(genid.WrapperValue_Value_field_number)
internal/msgfmt: use msgfmt package to format messages Port message formatting logic in testing/protocmp to internal/msgfmt and improve upon its output. This formatter is optimized for humanly readable output. It takes the best parts of both the JSON and proto text formats. The good of prototext: * It supports emitting unknown fields (useful for debugging). * It is relatively concise in the common-case since keys do not need to be represented as quoted strings (e.g., "key" vs key). The bad of prototext: * Requires relatively large dependency on encoding/prototext. * Our implementation lacks support for serializing packed lists. * Lacks support for readable maps. * Lacks support for readable Timestamp and Duration. The good of protojson: * First-class support for readable maps. * First-class support for readable Timestamp and Duration. The bad of protojson: * Requires relatively large dependency on encoding/protojson. * Lacks support for emitting unknown fields. * More verbose in the common-case as keys are quoted strings. The msgfmt package has the benefits of both protojson and prototext, but none of the detriments. It is a relatively simple implementation. This output is only intended for human consumption with no associated deserialization implementation. To avoid any illusion that this is identical to either the proto text or JSON formats, this always emits surrounding "{}" for top-level messages and the keys are not quoted strings. This CL does not use this format for generated Message.String methods as there is concerns about being inconsistent with the String methods as implemented in other languages. Having it be a seperate package makes it trivial to switch over to this if desired. Change-Id: I8b3581904d1624e84bf1b1954d2f01e5774b7f87 Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/223752 Reviewed-by: Damien Neil <dneil@google.com>
2020-02-29 02:37:16 +00:00
return appendValue(b, m.Get(fd), fd)
}
return nil
}
func appendUnknown(b []byte, raw protoreflect.RawFields) []byte {
rs := make(map[protoreflect.FieldNumber][]protoreflect.RawFields)
for len(raw) > 0 {
num, _, n := protowire.ConsumeField(raw)
rs[num] = append(rs[num], raw[:n])
raw = raw[n:]
}
var ns []protoreflect.FieldNumber
for n := range rs {
ns = append(ns, n)
}
sort.Slice(ns, func(i, j int) bool { return ns[i] < ns[j] })
for _, n := range ns {
var leftBracket, rightBracket string
if len(rs[n]) > 1 {
leftBracket, rightBracket = "[", "]"
}
b = strconv.AppendInt(b, int64(n), 10)
b = append(b, ':')
b = append(b, leftBracket...)
for _, r := range rs[n] {
num, typ, n := protowire.ConsumeTag(r)
r = r[n:]
switch typ {
case protowire.VarintType:
v, _ := protowire.ConsumeVarint(r)
b = strconv.AppendInt(b, int64(v), 10)
case protowire.Fixed32Type:
v, _ := protowire.ConsumeFixed32(r)
b = append(b, fmt.Sprintf("0x%08x", v)...)
case protowire.Fixed64Type:
v, _ := protowire.ConsumeFixed64(r)
b = append(b, fmt.Sprintf("0x%016x", v)...)
case protowire.BytesType:
v, _ := protowire.ConsumeBytes(r)
b = strconv.AppendQuote(b, string(v))
case protowire.StartGroupType:
v, _ := protowire.ConsumeGroup(num, r)
b = append(b, '{')
b = appendUnknown(b, v)
b = bytes.TrimRight(b, delim())
b = append(b, '}')
default:
panic(fmt.Sprintf("invalid type: %v", typ))
}
b = append(b, delim()...)
}
b = bytes.TrimRight(b, delim())
b = append(b, rightBracket...)
b = append(b, delim()...)
}
return b
}
func appendList(b []byte, v protoreflect.List, fd protoreflect.FieldDescriptor) []byte {
b = append(b, '[')
for i := 0; i < v.Len(); i++ {
b = appendValue(b, v.Get(i), fd)
b = append(b, delim()...)
}
b = bytes.TrimRight(b, delim())
b = append(b, ']')
return b
}
func appendMap(b []byte, v protoreflect.Map, fd protoreflect.FieldDescriptor) []byte {
b = append(b, '{')
order.RangeEntries(v, order.GenericKeyOrder, func(k protoreflect.MapKey, v protoreflect.Value) bool {
internal/msgfmt: use msgfmt package to format messages Port message formatting logic in testing/protocmp to internal/msgfmt and improve upon its output. This formatter is optimized for humanly readable output. It takes the best parts of both the JSON and proto text formats. The good of prototext: * It supports emitting unknown fields (useful for debugging). * It is relatively concise in the common-case since keys do not need to be represented as quoted strings (e.g., "key" vs key). The bad of prototext: * Requires relatively large dependency on encoding/prototext. * Our implementation lacks support for serializing packed lists. * Lacks support for readable maps. * Lacks support for readable Timestamp and Duration. The good of protojson: * First-class support for readable maps. * First-class support for readable Timestamp and Duration. The bad of protojson: * Requires relatively large dependency on encoding/protojson. * Lacks support for emitting unknown fields. * More verbose in the common-case as keys are quoted strings. The msgfmt package has the benefits of both protojson and prototext, but none of the detriments. It is a relatively simple implementation. This output is only intended for human consumption with no associated deserialization implementation. To avoid any illusion that this is identical to either the proto text or JSON formats, this always emits surrounding "{}" for top-level messages and the keys are not quoted strings. This CL does not use this format for generated Message.String methods as there is concerns about being inconsistent with the String methods as implemented in other languages. Having it be a seperate package makes it trivial to switch over to this if desired. Change-Id: I8b3581904d1624e84bf1b1954d2f01e5774b7f87 Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/223752 Reviewed-by: Damien Neil <dneil@google.com>
2020-02-29 02:37:16 +00:00
b = appendValue(b, k.Value(), fd.MapKey())
b = append(b, ':')
b = appendValue(b, v, fd.MapValue())
internal/msgfmt: use msgfmt package to format messages Port message formatting logic in testing/protocmp to internal/msgfmt and improve upon its output. This formatter is optimized for humanly readable output. It takes the best parts of both the JSON and proto text formats. The good of prototext: * It supports emitting unknown fields (useful for debugging). * It is relatively concise in the common-case since keys do not need to be represented as quoted strings (e.g., "key" vs key). The bad of prototext: * Requires relatively large dependency on encoding/prototext. * Our implementation lacks support for serializing packed lists. * Lacks support for readable maps. * Lacks support for readable Timestamp and Duration. The good of protojson: * First-class support for readable maps. * First-class support for readable Timestamp and Duration. The bad of protojson: * Requires relatively large dependency on encoding/protojson. * Lacks support for emitting unknown fields. * More verbose in the common-case as keys are quoted strings. The msgfmt package has the benefits of both protojson and prototext, but none of the detriments. It is a relatively simple implementation. This output is only intended for human consumption with no associated deserialization implementation. To avoid any illusion that this is identical to either the proto text or JSON formats, this always emits surrounding "{}" for top-level messages and the keys are not quoted strings. This CL does not use this format for generated Message.String methods as there is concerns about being inconsistent with the String methods as implemented in other languages. Having it be a seperate package makes it trivial to switch over to this if desired. Change-Id: I8b3581904d1624e84bf1b1954d2f01e5774b7f87 Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/223752 Reviewed-by: Damien Neil <dneil@google.com>
2020-02-29 02:37:16 +00:00
b = append(b, delim()...)
return true
})
internal/msgfmt: use msgfmt package to format messages Port message formatting logic in testing/protocmp to internal/msgfmt and improve upon its output. This formatter is optimized for humanly readable output. It takes the best parts of both the JSON and proto text formats. The good of prototext: * It supports emitting unknown fields (useful for debugging). * It is relatively concise in the common-case since keys do not need to be represented as quoted strings (e.g., "key" vs key). The bad of prototext: * Requires relatively large dependency on encoding/prototext. * Our implementation lacks support for serializing packed lists. * Lacks support for readable maps. * Lacks support for readable Timestamp and Duration. The good of protojson: * First-class support for readable maps. * First-class support for readable Timestamp and Duration. The bad of protojson: * Requires relatively large dependency on encoding/protojson. * Lacks support for emitting unknown fields. * More verbose in the common-case as keys are quoted strings. The msgfmt package has the benefits of both protojson and prototext, but none of the detriments. It is a relatively simple implementation. This output is only intended for human consumption with no associated deserialization implementation. To avoid any illusion that this is identical to either the proto text or JSON formats, this always emits surrounding "{}" for top-level messages and the keys are not quoted strings. This CL does not use this format for generated Message.String methods as there is concerns about being inconsistent with the String methods as implemented in other languages. Having it be a seperate package makes it trivial to switch over to this if desired. Change-Id: I8b3581904d1624e84bf1b1954d2f01e5774b7f87 Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/223752 Reviewed-by: Damien Neil <dneil@google.com>
2020-02-29 02:37:16 +00:00
b = bytes.TrimRight(b, delim())
b = append(b, '}')
return b
}
func delim() string {
// Deliberately introduce instability into the message string to
// discourage users from depending on it.
if detrand.Bool() {
return " "
}
return ", "
}