gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

stringer.go (9924B)


      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 	"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 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 }