vendor/google.golang.org/protobuf/internal/descfmt/stringer.go
changeset 256 6d9efbef00a9
child 260 445e01aede7e
equal deleted inserted replaced
255:4f153a23adab 256:6d9efbef00a9
       
     1 // 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 
       
     5 // Package descfmt provides functionality to format descriptors.
       
     6 package descfmt
       
     7 
       
     8 import (
       
     9 	"fmt"
       
    10 	"io"
       
    11 	"reflect"
       
    12 	"strconv"
       
    13 	"strings"
       
    14 
       
    15 	"google.golang.org/protobuf/internal/detrand"
       
    16 	"google.golang.org/protobuf/internal/pragma"
       
    17 	pref "google.golang.org/protobuf/reflect/protoreflect"
       
    18 )
       
    19 
       
    20 type list interface {
       
    21 	Len() int
       
    22 	pragma.DoNotImplement
       
    23 }
       
    24 
       
    25 func FormatList(s fmt.State, r rune, vs list) {
       
    26 	io.WriteString(s, formatListOpt(vs, true, r == 'v' && (s.Flag('+') || s.Flag('#'))))
       
    27 }
       
    28 func formatListOpt(vs list, isRoot, allowMulti bool) string {
       
    29 	start, end := "[", "]"
       
    30 	if isRoot {
       
    31 		var name string
       
    32 		switch vs.(type) {
       
    33 		case pref.Names:
       
    34 			name = "Names"
       
    35 		case pref.FieldNumbers:
       
    36 			name = "FieldNumbers"
       
    37 		case pref.FieldRanges:
       
    38 			name = "FieldRanges"
       
    39 		case pref.EnumRanges:
       
    40 			name = "EnumRanges"
       
    41 		case pref.FileImports:
       
    42 			name = "FileImports"
       
    43 		case pref.Descriptor:
       
    44 			name = reflect.ValueOf(vs).MethodByName("Get").Type().Out(0).Name() + "s"
       
    45 		default:
       
    46 			name = reflect.ValueOf(vs).Elem().Type().Name()
       
    47 		}
       
    48 		start, end = name+"{", "}"
       
    49 	}
       
    50 
       
    51 	var ss []string
       
    52 	switch vs := vs.(type) {
       
    53 	case pref.Names:
       
    54 		for i := 0; i < vs.Len(); i++ {
       
    55 			ss = append(ss, fmt.Sprint(vs.Get(i)))
       
    56 		}
       
    57 		return start + joinStrings(ss, false) + end
       
    58 	case pref.FieldNumbers:
       
    59 		for i := 0; i < vs.Len(); i++ {
       
    60 			ss = append(ss, fmt.Sprint(vs.Get(i)))
       
    61 		}
       
    62 		return start + joinStrings(ss, false) + end
       
    63 	case pref.FieldRanges:
       
    64 		for i := 0; i < vs.Len(); i++ {
       
    65 			r := vs.Get(i)
       
    66 			if r[0]+1 == r[1] {
       
    67 				ss = append(ss, fmt.Sprintf("%d", r[0]))
       
    68 			} else {
       
    69 				ss = append(ss, fmt.Sprintf("%d:%d", r[0], r[1])) // enum ranges are end exclusive
       
    70 			}
       
    71 		}
       
    72 		return start + joinStrings(ss, false) + end
       
    73 	case pref.EnumRanges:
       
    74 		for i := 0; i < vs.Len(); i++ {
       
    75 			r := vs.Get(i)
       
    76 			if r[0] == r[1] {
       
    77 				ss = append(ss, fmt.Sprintf("%d", r[0]))
       
    78 			} else {
       
    79 				ss = append(ss, fmt.Sprintf("%d:%d", r[0], int64(r[1])+1)) // enum ranges are end inclusive
       
    80 			}
       
    81 		}
       
    82 		return start + joinStrings(ss, false) + end
       
    83 	case pref.FileImports:
       
    84 		for i := 0; i < vs.Len(); i++ {
       
    85 			var rs records
       
    86 			rs.Append(reflect.ValueOf(vs.Get(i)), "Path", "Package", "IsPublic", "IsWeak")
       
    87 			ss = append(ss, "{"+rs.Join()+"}")
       
    88 		}
       
    89 		return start + joinStrings(ss, allowMulti) + end
       
    90 	default:
       
    91 		_, isEnumValue := vs.(pref.EnumValueDescriptors)
       
    92 		for i := 0; i < vs.Len(); i++ {
       
    93 			m := reflect.ValueOf(vs).MethodByName("Get")
       
    94 			v := m.Call([]reflect.Value{reflect.ValueOf(i)})[0].Interface()
       
    95 			ss = append(ss, formatDescOpt(v.(pref.Descriptor), false, allowMulti && !isEnumValue))
       
    96 		}
       
    97 		return start + joinStrings(ss, allowMulti && isEnumValue) + end
       
    98 	}
       
    99 }
       
   100 
       
   101 // descriptorAccessors is a list of accessors to print for each descriptor.
       
   102 //
       
   103 // Do not print all accessors since some contain redundant information,
       
   104 // while others are pointers that we do not want to follow since the descriptor
       
   105 // is actually a cyclic graph.
       
   106 //
       
   107 // Using a list allows us to print the accessors in a sensible order.
       
   108 var descriptorAccessors = map[reflect.Type][]string{
       
   109 	reflect.TypeOf((*pref.FileDescriptor)(nil)).Elem():      {"Path", "Package", "Imports", "Messages", "Enums", "Extensions", "Services"},
       
   110 	reflect.TypeOf((*pref.MessageDescriptor)(nil)).Elem():   {"IsMapEntry", "Fields", "Oneofs", "ReservedNames", "ReservedRanges", "RequiredNumbers", "ExtensionRanges", "Messages", "Enums", "Extensions"},
       
   111 	reflect.TypeOf((*pref.FieldDescriptor)(nil)).Elem():     {"Number", "Cardinality", "Kind", "HasJSONName", "JSONName", "HasPresence", "IsExtension", "IsPacked", "IsWeak", "IsList", "IsMap", "MapKey", "MapValue", "HasDefault", "Default", "ContainingOneof", "ContainingMessage", "Message", "Enum"},
       
   112 	reflect.TypeOf((*pref.OneofDescriptor)(nil)).Elem():     {"Fields"}, // not directly used; must keep in sync with formatDescOpt
       
   113 	reflect.TypeOf((*pref.EnumDescriptor)(nil)).Elem():      {"Values", "ReservedNames", "ReservedRanges"},
       
   114 	reflect.TypeOf((*pref.EnumValueDescriptor)(nil)).Elem(): {"Number"},
       
   115 	reflect.TypeOf((*pref.ServiceDescriptor)(nil)).Elem():   {"Methods"},
       
   116 	reflect.TypeOf((*pref.MethodDescriptor)(nil)).Elem():    {"Input", "Output", "IsStreamingClient", "IsStreamingServer"},
       
   117 }
       
   118 
       
   119 func FormatDesc(s fmt.State, r rune, t pref.Descriptor) {
       
   120 	io.WriteString(s, formatDescOpt(t, true, r == 'v' && (s.Flag('+') || s.Flag('#'))))
       
   121 }
       
   122 func formatDescOpt(t pref.Descriptor, isRoot, allowMulti bool) string {
       
   123 	rv := reflect.ValueOf(t)
       
   124 	rt := rv.MethodByName("ProtoType").Type().In(0)
       
   125 
       
   126 	start, end := "{", "}"
       
   127 	if isRoot {
       
   128 		start = rt.Name() + "{"
       
   129 	}
       
   130 
       
   131 	_, isFile := t.(pref.FileDescriptor)
       
   132 	rs := records{allowMulti: allowMulti}
       
   133 	if t.IsPlaceholder() {
       
   134 		if isFile {
       
   135 			rs.Append(rv, "Path", "Package", "IsPlaceholder")
       
   136 		} else {
       
   137 			rs.Append(rv, "FullName", "IsPlaceholder")
       
   138 		}
       
   139 	} else {
       
   140 		switch {
       
   141 		case isFile:
       
   142 			rs.Append(rv, "Syntax")
       
   143 		case isRoot:
       
   144 			rs.Append(rv, "Syntax", "FullName")
       
   145 		default:
       
   146 			rs.Append(rv, "Name")
       
   147 		}
       
   148 		switch t := t.(type) {
       
   149 		case pref.FieldDescriptor:
       
   150 			for _, s := range descriptorAccessors[rt] {
       
   151 				switch s {
       
   152 				case "MapKey":
       
   153 					if k := t.MapKey(); k != nil {
       
   154 						rs.recs = append(rs.recs, [2]string{"MapKey", k.Kind().String()})
       
   155 					}
       
   156 				case "MapValue":
       
   157 					if v := t.MapValue(); v != nil {
       
   158 						switch v.Kind() {
       
   159 						case pref.EnumKind:
       
   160 							rs.recs = append(rs.recs, [2]string{"MapValue", string(v.Enum().FullName())})
       
   161 						case pref.MessageKind, pref.GroupKind:
       
   162 							rs.recs = append(rs.recs, [2]string{"MapValue", string(v.Message().FullName())})
       
   163 						default:
       
   164 							rs.recs = append(rs.recs, [2]string{"MapValue", v.Kind().String()})
       
   165 						}
       
   166 					}
       
   167 				case "ContainingOneof":
       
   168 					if od := t.ContainingOneof(); od != nil {
       
   169 						rs.recs = append(rs.recs, [2]string{"Oneof", string(od.Name())})
       
   170 					}
       
   171 				case "ContainingMessage":
       
   172 					if t.IsExtension() {
       
   173 						rs.recs = append(rs.recs, [2]string{"Extendee", string(t.ContainingMessage().FullName())})
       
   174 					}
       
   175 				case "Message":
       
   176 					if !t.IsMap() {
       
   177 						rs.Append(rv, s)
       
   178 					}
       
   179 				default:
       
   180 					rs.Append(rv, s)
       
   181 				}
       
   182 			}
       
   183 		case pref.OneofDescriptor:
       
   184 			var ss []string
       
   185 			fs := t.Fields()
       
   186 			for i := 0; i < fs.Len(); i++ {
       
   187 				ss = append(ss, string(fs.Get(i).Name()))
       
   188 			}
       
   189 			if len(ss) > 0 {
       
   190 				rs.recs = append(rs.recs, [2]string{"Fields", "[" + joinStrings(ss, false) + "]"})
       
   191 			}
       
   192 		default:
       
   193 			rs.Append(rv, descriptorAccessors[rt]...)
       
   194 		}
       
   195 		if rv.MethodByName("GoType").IsValid() {
       
   196 			rs.Append(rv, "GoType")
       
   197 		}
       
   198 	}
       
   199 	return start + rs.Join() + end
       
   200 }
       
   201 
       
   202 type records struct {
       
   203 	recs       [][2]string
       
   204 	allowMulti bool
       
   205 }
       
   206 
       
   207 func (rs *records) Append(v reflect.Value, accessors ...string) {
       
   208 	for _, a := range accessors {
       
   209 		var rv reflect.Value
       
   210 		if m := v.MethodByName(a); m.IsValid() {
       
   211 			rv = m.Call(nil)[0]
       
   212 		}
       
   213 		if v.Kind() == reflect.Struct && !rv.IsValid() {
       
   214 			rv = v.FieldByName(a)
       
   215 		}
       
   216 		if !rv.IsValid() {
       
   217 			panic(fmt.Sprintf("unknown accessor: %v.%s", v.Type(), a))
       
   218 		}
       
   219 		if _, ok := rv.Interface().(pref.Value); ok {
       
   220 			rv = rv.MethodByName("Interface").Call(nil)[0]
       
   221 			if !rv.IsNil() {
       
   222 				rv = rv.Elem()
       
   223 			}
       
   224 		}
       
   225 
       
   226 		// Ignore zero values.
       
   227 		var isZero bool
       
   228 		switch rv.Kind() {
       
   229 		case reflect.Interface, reflect.Slice:
       
   230 			isZero = rv.IsNil()
       
   231 		case reflect.Bool:
       
   232 			isZero = rv.Bool() == false
       
   233 		case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
       
   234 			isZero = rv.Int() == 0
       
   235 		case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
       
   236 			isZero = rv.Uint() == 0
       
   237 		case reflect.String:
       
   238 			isZero = rv.String() == ""
       
   239 		}
       
   240 		if n, ok := rv.Interface().(list); ok {
       
   241 			isZero = n.Len() == 0
       
   242 		}
       
   243 		if isZero {
       
   244 			continue
       
   245 		}
       
   246 
       
   247 		// Format the value.
       
   248 		var s string
       
   249 		v := rv.Interface()
       
   250 		switch v := v.(type) {
       
   251 		case list:
       
   252 			s = formatListOpt(v, false, rs.allowMulti)
       
   253 		case pref.FieldDescriptor, pref.OneofDescriptor, pref.EnumValueDescriptor, pref.MethodDescriptor:
       
   254 			s = string(v.(pref.Descriptor).Name())
       
   255 		case pref.Descriptor:
       
   256 			s = string(v.FullName())
       
   257 		case string:
       
   258 			s = strconv.Quote(v)
       
   259 		case []byte:
       
   260 			s = fmt.Sprintf("%q", v)
       
   261 		default:
       
   262 			s = fmt.Sprint(v)
       
   263 		}
       
   264 		rs.recs = append(rs.recs, [2]string{a, s})
       
   265 	}
       
   266 }
       
   267 
       
   268 func (rs *records) Join() string {
       
   269 	var ss []string
       
   270 
       
   271 	// In single line mode, simply join all records with commas.
       
   272 	if !rs.allowMulti {
       
   273 		for _, r := range rs.recs {
       
   274 			ss = append(ss, r[0]+formatColon(0)+r[1])
       
   275 		}
       
   276 		return joinStrings(ss, false)
       
   277 	}
       
   278 
       
   279 	// In allowMulti line mode, align single line records for more readable output.
       
   280 	var maxLen int
       
   281 	flush := func(i int) {
       
   282 		for _, r := range rs.recs[len(ss):i] {
       
   283 			ss = append(ss, r[0]+formatColon(maxLen-len(r[0]))+r[1])
       
   284 		}
       
   285 		maxLen = 0
       
   286 	}
       
   287 	for i, r := range rs.recs {
       
   288 		if isMulti := strings.Contains(r[1], "\n"); isMulti {
       
   289 			flush(i)
       
   290 			ss = append(ss, r[0]+formatColon(0)+strings.Join(strings.Split(r[1], "\n"), "\n\t"))
       
   291 		} else if maxLen < len(r[0]) {
       
   292 			maxLen = len(r[0])
       
   293 		}
       
   294 	}
       
   295 	flush(len(rs.recs))
       
   296 	return joinStrings(ss, true)
       
   297 }
       
   298 
       
   299 func formatColon(padding int) string {
       
   300 	// Deliberately introduce instability into the debug output to
       
   301 	// discourage users from performing string comparisons.
       
   302 	// This provides us flexibility to change the output in the future.
       
   303 	if detrand.Bool() {
       
   304 		return ":" + strings.Repeat(" ", 1+padding) // use non-breaking spaces (U+00a0)
       
   305 	} else {
       
   306 		return ":" + strings.Repeat(" ", 1+padding) // use regular spaces (U+0020)
       
   307 	}
       
   308 }
       
   309 
       
   310 func joinStrings(ss []string, isMulti bool) string {
       
   311 	if len(ss) == 0 {
       
   312 		return ""
       
   313 	}
       
   314 	if isMulti {
       
   315 		return "\n\t" + strings.Join(ss, "\n\t") + "\n"
       
   316 	}
       
   317 	return strings.Join(ss, ", ")
       
   318 }