github.com/whiteCcinn/protobuf-go@v1.0.9/internal/descfmt/stringer.go (about)

     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  	"github.com/whiteCcinn/protobuf-go/internal/detrand"
    16  	"github.com/whiteCcinn/protobuf-go/internal/pragma"
    17  	"github.com/whiteCcinn/protobuf-go/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 protoreflect.Names:
    34  			name = "Names"
    35  		case protoreflect.FieldNumbers:
    36  			name = "FieldNumbers"
    37  		case protoreflect.FieldRanges:
    38  			name = "FieldRanges"
    39  		case protoreflect.EnumRanges:
    40  			name = "EnumRanges"
    41  		case protoreflect.FileImports:
    42  			name = "FileImports"
    43  		case protoreflect.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 protoreflect.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 protoreflect.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 protoreflect.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 protoreflect.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 protoreflect.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.(protoreflect.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.(protoreflect.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((*protoreflect.FileDescriptor)(nil)).Elem():      {"Path", "Package", "Imports", "Messages", "Enums", "Extensions", "Services"},
   110  	reflect.TypeOf((*protoreflect.MessageDescriptor)(nil)).Elem():   {"IsMapEntry", "Fields", "Oneofs", "ReservedNames", "ReservedRanges", "RequiredNumbers", "ExtensionRanges", "Messages", "Enums", "Extensions"},
   111  	reflect.TypeOf((*protoreflect.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((*protoreflect.OneofDescriptor)(nil)).Elem():     {"Fields"}, // not directly used; must keep in sync with formatDescOpt
   113  	reflect.TypeOf((*protoreflect.EnumDescriptor)(nil)).Elem():      {"Values", "ReservedNames", "ReservedRanges"},
   114  	reflect.TypeOf((*protoreflect.EnumValueDescriptor)(nil)).Elem(): {"Number"},
   115  	reflect.TypeOf((*protoreflect.ServiceDescriptor)(nil)).Elem():   {"Methods"},
   116  	reflect.TypeOf((*protoreflect.MethodDescriptor)(nil)).Elem():    {"Input", "Output", "IsStreamingClient", "IsStreamingServer"},
   117  }
   118  
   119  func FormatDesc(s fmt.State, r rune, t protoreflect.Descriptor) {
   120  	io.WriteString(s, formatDescOpt(t, true, r == 'v' && (s.Flag('+') || s.Flag('#'))))
   121  }
   122  func formatDescOpt(t protoreflect.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.(protoreflect.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 protoreflect.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 protoreflect.EnumKind:
   160  							rs.recs = append(rs.recs, [2]string{"MapValue", string(v.Enum().FullName())})
   161  						case protoreflect.MessageKind, protoreflect.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 protoreflect.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().(protoreflect.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 protoreflect.FieldDescriptor, protoreflect.OneofDescriptor, protoreflect.EnumValueDescriptor, protoreflect.MethodDescriptor:
   254  			s = string(v.(protoreflect.Descriptor).Name())
   255  		case protoreflect.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  }