...

Source file src/google.golang.org/protobuf/internal/msgfmt/format.go

Documentation: google.golang.org/protobuf/internal/msgfmt

     1  // Copyright 2019 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  
     5  // Package msgfmt implements a text marshaler combining the desirable features
     6  // of both the JSON and proto text formats.
     7  // It is optimized for human readability and has no associated deserializer.
     8  package msgfmt
     9  
    10  import (
    11  	"bytes"
    12  	"fmt"
    13  	"reflect"
    14  	"sort"
    15  	"strconv"
    16  	"strings"
    17  	"time"
    18  
    19  	"google.golang.org/protobuf/encoding/protowire"
    20  	"google.golang.org/protobuf/internal/detrand"
    21  	"google.golang.org/protobuf/internal/genid"
    22  	"google.golang.org/protobuf/internal/order"
    23  	"google.golang.org/protobuf/proto"
    24  	"google.golang.org/protobuf/reflect/protoreflect"
    25  	"google.golang.org/protobuf/reflect/protoregistry"
    26  )
    27  
    28  // Format returns a formatted string for the message.
    29  func Format(m proto.Message) string {
    30  	return string(appendMessage(nil, m.ProtoReflect()))
    31  }
    32  
    33  // FormatValue returns a formatted string for an arbitrary value.
    34  func FormatValue(v protoreflect.Value, fd protoreflect.FieldDescriptor) string {
    35  	return string(appendValue(nil, v, fd))
    36  }
    37  
    38  func appendValue(b []byte, v protoreflect.Value, fd protoreflect.FieldDescriptor) []byte {
    39  	switch v := v.Interface().(type) {
    40  	case nil:
    41  		return append(b, "<invalid>"...)
    42  	case bool, int32, int64, uint32, uint64, float32, float64:
    43  		return append(b, fmt.Sprint(v)...)
    44  	case string:
    45  		return append(b, strconv.Quote(string(v))...)
    46  	case []byte:
    47  		return append(b, strconv.Quote(string(v))...)
    48  	case protoreflect.EnumNumber:
    49  		return appendEnum(b, v, fd)
    50  	case protoreflect.Message:
    51  		return appendMessage(b, v)
    52  	case protoreflect.List:
    53  		return appendList(b, v, fd)
    54  	case protoreflect.Map:
    55  		return appendMap(b, v, fd)
    56  	default:
    57  		panic(fmt.Sprintf("invalid type: %T", v))
    58  	}
    59  }
    60  
    61  func appendEnum(b []byte, v protoreflect.EnumNumber, fd protoreflect.FieldDescriptor) []byte {
    62  	if fd != nil {
    63  		if ev := fd.Enum().Values().ByNumber(v); ev != nil {
    64  			return append(b, ev.Name()...)
    65  		}
    66  	}
    67  	return strconv.AppendInt(b, int64(v), 10)
    68  }
    69  
    70  func appendMessage(b []byte, m protoreflect.Message) []byte {
    71  	if b2 := appendKnownMessage(b, m); b2 != nil {
    72  		return b2
    73  	}
    74  
    75  	b = append(b, '{')
    76  	order.RangeFields(m, order.IndexNameFieldOrder, func(fd protoreflect.FieldDescriptor, v protoreflect.Value) bool {
    77  		b = append(b, fd.TextName()...)
    78  		b = append(b, ':')
    79  		b = appendValue(b, v, fd)
    80  		b = append(b, delim()...)
    81  		return true
    82  	})
    83  	b = appendUnknown(b, m.GetUnknown())
    84  	b = bytes.TrimRight(b, delim())
    85  	b = append(b, '}')
    86  	return b
    87  }
    88  
    89  var protocmpMessageType = reflect.TypeOf(map[string]interface{}(nil))
    90  
    91  func appendKnownMessage(b []byte, m protoreflect.Message) []byte {
    92  	md := m.Descriptor()
    93  	fds := md.Fields()
    94  	switch md.FullName() {
    95  	case genid.Any_message_fullname:
    96  		var msgVal protoreflect.Message
    97  		url := m.Get(fds.ByNumber(genid.Any_TypeUrl_field_number)).String()
    98  		if v := reflect.ValueOf(m); v.Type().ConvertibleTo(protocmpMessageType) {
    99  			// For protocmp.Message, directly obtain the sub-message value
   100  			// which is stored in structured form, rather than as raw bytes.
   101  			m2 := v.Convert(protocmpMessageType).Interface().(map[string]interface{})
   102  			v, ok := m2[string(genid.Any_Value_field_name)].(proto.Message)
   103  			if !ok {
   104  				return nil
   105  			}
   106  			msgVal = v.ProtoReflect()
   107  		} else {
   108  			val := m.Get(fds.ByNumber(genid.Any_Value_field_number)).Bytes()
   109  			mt, err := protoregistry.GlobalTypes.FindMessageByURL(url)
   110  			if err != nil {
   111  				return nil
   112  			}
   113  			msgVal = mt.New()
   114  			err = proto.UnmarshalOptions{AllowPartial: true}.Unmarshal(val, msgVal.Interface())
   115  			if err != nil {
   116  				return nil
   117  			}
   118  		}
   119  
   120  		b = append(b, '{')
   121  		b = append(b, "["+url+"]"...)
   122  		b = append(b, ':')
   123  		b = appendMessage(b, msgVal)
   124  		b = append(b, '}')
   125  		return b
   126  
   127  	case genid.Timestamp_message_fullname:
   128  		secs := m.Get(fds.ByNumber(genid.Timestamp_Seconds_field_number)).Int()
   129  		nanos := m.Get(fds.ByNumber(genid.Timestamp_Nanos_field_number)).Int()
   130  		if nanos < 0 || nanos >= 1e9 {
   131  			return nil
   132  		}
   133  		t := time.Unix(secs, nanos).UTC()
   134  		x := t.Format("2006-01-02T15:04:05.000000000") // RFC 3339
   135  		x = strings.TrimSuffix(x, "000")
   136  		x = strings.TrimSuffix(x, "000")
   137  		x = strings.TrimSuffix(x, ".000")
   138  		return append(b, x+"Z"...)
   139  
   140  	case genid.Duration_message_fullname:
   141  		sign := ""
   142  		secs := m.Get(fds.ByNumber(genid.Duration_Seconds_field_number)).Int()
   143  		nanos := m.Get(fds.ByNumber(genid.Duration_Nanos_field_number)).Int()
   144  		if nanos <= -1e9 || nanos >= 1e9 || (secs > 0 && nanos < 0) || (secs < 0 && nanos > 0) {
   145  			return nil
   146  		}
   147  		if secs < 0 || nanos < 0 {
   148  			sign, secs, nanos = "-", -1*secs, -1*nanos
   149  		}
   150  		x := fmt.Sprintf("%s%d.%09d", sign, secs, nanos)
   151  		x = strings.TrimSuffix(x, "000")
   152  		x = strings.TrimSuffix(x, "000")
   153  		x = strings.TrimSuffix(x, ".000")
   154  		return append(b, x+"s"...)
   155  
   156  	case genid.BoolValue_message_fullname,
   157  		genid.Int32Value_message_fullname,
   158  		genid.Int64Value_message_fullname,
   159  		genid.UInt32Value_message_fullname,
   160  		genid.UInt64Value_message_fullname,
   161  		genid.FloatValue_message_fullname,
   162  		genid.DoubleValue_message_fullname,
   163  		genid.StringValue_message_fullname,
   164  		genid.BytesValue_message_fullname:
   165  		fd := fds.ByNumber(genid.WrapperValue_Value_field_number)
   166  		return appendValue(b, m.Get(fd), fd)
   167  	}
   168  
   169  	return nil
   170  }
   171  
   172  func appendUnknown(b []byte, raw protoreflect.RawFields) []byte {
   173  	rs := make(map[protoreflect.FieldNumber][]protoreflect.RawFields)
   174  	for len(raw) > 0 {
   175  		num, _, n := protowire.ConsumeField(raw)
   176  		rs[num] = append(rs[num], raw[:n])
   177  		raw = raw[n:]
   178  	}
   179  
   180  	var ns []protoreflect.FieldNumber
   181  	for n := range rs {
   182  		ns = append(ns, n)
   183  	}
   184  	sort.Slice(ns, func(i, j int) bool { return ns[i] < ns[j] })
   185  
   186  	for _, n := range ns {
   187  		var leftBracket, rightBracket string
   188  		if len(rs[n]) > 1 {
   189  			leftBracket, rightBracket = "[", "]"
   190  		}
   191  
   192  		b = strconv.AppendInt(b, int64(n), 10)
   193  		b = append(b, ':')
   194  		b = append(b, leftBracket...)
   195  		for _, r := range rs[n] {
   196  			num, typ, n := protowire.ConsumeTag(r)
   197  			r = r[n:]
   198  			switch typ {
   199  			case protowire.VarintType:
   200  				v, _ := protowire.ConsumeVarint(r)
   201  				b = strconv.AppendInt(b, int64(v), 10)
   202  			case protowire.Fixed32Type:
   203  				v, _ := protowire.ConsumeFixed32(r)
   204  				b = append(b, fmt.Sprintf("0x%08x", v)...)
   205  			case protowire.Fixed64Type:
   206  				v, _ := protowire.ConsumeFixed64(r)
   207  				b = append(b, fmt.Sprintf("0x%016x", v)...)
   208  			case protowire.BytesType:
   209  				v, _ := protowire.ConsumeBytes(r)
   210  				b = strconv.AppendQuote(b, string(v))
   211  			case protowire.StartGroupType:
   212  				v, _ := protowire.ConsumeGroup(num, r)
   213  				b = append(b, '{')
   214  				b = appendUnknown(b, v)
   215  				b = bytes.TrimRight(b, delim())
   216  				b = append(b, '}')
   217  			default:
   218  				panic(fmt.Sprintf("invalid type: %v", typ))
   219  			}
   220  			b = append(b, delim()...)
   221  		}
   222  		b = bytes.TrimRight(b, delim())
   223  		b = append(b, rightBracket...)
   224  		b = append(b, delim()...)
   225  	}
   226  	return b
   227  }
   228  
   229  func appendList(b []byte, v protoreflect.List, fd protoreflect.FieldDescriptor) []byte {
   230  	b = append(b, '[')
   231  	for i := 0; i < v.Len(); i++ {
   232  		b = appendValue(b, v.Get(i), fd)
   233  		b = append(b, delim()...)
   234  	}
   235  	b = bytes.TrimRight(b, delim())
   236  	b = append(b, ']')
   237  	return b
   238  }
   239  
   240  func appendMap(b []byte, v protoreflect.Map, fd protoreflect.FieldDescriptor) []byte {
   241  	b = append(b, '{')
   242  	order.RangeEntries(v, order.GenericKeyOrder, func(k protoreflect.MapKey, v protoreflect.Value) bool {
   243  		b = appendValue(b, k.Value(), fd.MapKey())
   244  		b = append(b, ':')
   245  		b = appendValue(b, v, fd.MapValue())
   246  		b = append(b, delim()...)
   247  		return true
   248  	})
   249  	b = bytes.TrimRight(b, delim())
   250  	b = append(b, '}')
   251  	return b
   252  }
   253  
   254  func delim() string {
   255  	// Deliberately introduce instability into the message string to
   256  	// discourage users from depending on it.
   257  	if detrand.Bool() {
   258  		return "  "
   259  	}
   260  	return ", "
   261  }
   262  

View as plain text