// Copyright 2018 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 descfmt provides functionality to format descriptors.
package descfmt import ( ) type list interface { Len() int pragma.DoNotImplement } func ( fmt.State, rune, list) { io.WriteString(, formatListOpt(, true, == 'v' && (.Flag('+') || .Flag('#')))) } func ( list, , bool) string { , := "[", "]" if { var string switch .(type) { case protoreflect.Names: = "Names" case protoreflect.FieldNumbers: = "FieldNumbers" case protoreflect.FieldRanges: = "FieldRanges" case protoreflect.EnumRanges: = "EnumRanges" case protoreflect.FileImports: = "FileImports" case protoreflect.Descriptor: = reflect.ValueOf().MethodByName("Get").Type().Out(0).Name() + "s" default: = reflect.ValueOf().Elem().Type().Name() } , = +"{", "}" } var []string switch vs := .(type) { case protoreflect.Names: for := 0; < .Len(); ++ { = append(, fmt.Sprint(.Get())) } return + joinStrings(, false) + case protoreflect.FieldNumbers: for := 0; < .Len(); ++ { = append(, fmt.Sprint(.Get())) } return + joinStrings(, false) + case protoreflect.FieldRanges: for := 0; < .Len(); ++ { := .Get() if [0]+1 == [1] { = append(, fmt.Sprintf("%d", [0])) } else { = append(, fmt.Sprintf("%d:%d", [0], [1])) // enum ranges are end exclusive } } return + joinStrings(, false) + case protoreflect.EnumRanges: for := 0; < .Len(); ++ { := .Get() if [0] == [1] { = append(, fmt.Sprintf("%d", [0])) } else { = append(, fmt.Sprintf("%d:%d", [0], int64([1])+1)) // enum ranges are end inclusive } } return + joinStrings(, false) + case protoreflect.FileImports: for := 0; < .Len(); ++ { var records .Append(reflect.ValueOf(.Get()), "Path", "Package", "IsPublic", "IsWeak") = append(, "{"+.Join()+"}") } return + joinStrings(, ) + default: , := .(protoreflect.EnumValueDescriptors) for := 0; < .Len(); ++ { := reflect.ValueOf().MethodByName("Get") := .Call([]reflect.Value{reflect.ValueOf()})[0].Interface() = append(, formatDescOpt(.(protoreflect.Descriptor), false, && !)) } return + joinStrings(, && ) + } } // descriptorAccessors is a list of accessors to print for each descriptor. // // Do not print all accessors since some contain redundant information, // while others are pointers that we do not want to follow since the descriptor // is actually a cyclic graph. // // Using a list allows us to print the accessors in a sensible order. var descriptorAccessors = map[reflect.Type][]string{ reflect.TypeOf((*protoreflect.FileDescriptor)(nil)).Elem(): {"Path", "Package", "Imports", "Messages", "Enums", "Extensions", "Services"}, reflect.TypeOf((*protoreflect.MessageDescriptor)(nil)).Elem(): {"IsMapEntry", "Fields", "Oneofs", "ReservedNames", "ReservedRanges", "RequiredNumbers", "ExtensionRanges", "Messages", "Enums", "Extensions"}, 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"}, reflect.TypeOf((*protoreflect.OneofDescriptor)(nil)).Elem(): {"Fields"}, // not directly used; must keep in sync with formatDescOpt reflect.TypeOf((*protoreflect.EnumDescriptor)(nil)).Elem(): {"Values", "ReservedNames", "ReservedRanges"}, reflect.TypeOf((*protoreflect.EnumValueDescriptor)(nil)).Elem(): {"Number"}, reflect.TypeOf((*protoreflect.ServiceDescriptor)(nil)).Elem(): {"Methods"}, reflect.TypeOf((*protoreflect.MethodDescriptor)(nil)).Elem(): {"Input", "Output", "IsStreamingClient", "IsStreamingServer"}, } func ( fmt.State, rune, protoreflect.Descriptor) { io.WriteString(, formatDescOpt(, true, == 'v' && (.Flag('+') || .Flag('#')))) } func ( protoreflect.Descriptor, , bool) string { := reflect.ValueOf() := .MethodByName("ProtoType").Type().In(0) , := "{", "}" if { = .Name() + "{" } , := .(protoreflect.FileDescriptor) := records{allowMulti: } if .IsPlaceholder() { if { .Append(, "Path", "Package", "IsPlaceholder") } else { .Append(, "FullName", "IsPlaceholder") } } else { switch { case : .Append(, "Syntax") case : .Append(, "Syntax", "FullName") default: .Append(, "Name") } switch t := .(type) { case protoreflect.FieldDescriptor: for , := range descriptorAccessors[] { switch { case "MapKey": if := .MapKey(); != nil { .recs = append(.recs, [2]string{"MapKey", .Kind().String()}) } case "MapValue": if := .MapValue(); != nil { switch .Kind() { case protoreflect.EnumKind: .recs = append(.recs, [2]string{"MapValue", string(.Enum().FullName())}) case protoreflect.MessageKind, protoreflect.GroupKind: .recs = append(.recs, [2]string{"MapValue", string(.Message().FullName())}) default: .recs = append(.recs, [2]string{"MapValue", .Kind().String()}) } } case "ContainingOneof": if := .ContainingOneof(); != nil { .recs = append(.recs, [2]string{"Oneof", string(.Name())}) } case "ContainingMessage": if .IsExtension() { .recs = append(.recs, [2]string{"Extendee", string(.ContainingMessage().FullName())}) } case "Message": if !.IsMap() { .Append(, ) } default: .Append(, ) } } case protoreflect.OneofDescriptor: var []string := .Fields() for := 0; < .Len(); ++ { = append(, string(.Get().Name())) } if len() > 0 { .recs = append(.recs, [2]string{"Fields", "[" + joinStrings(, false) + "]"}) } default: .Append(, descriptorAccessors[]...) } if .MethodByName("GoType").IsValid() { .Append(, "GoType") } } return + .Join() + } type records struct { recs [][2]string allowMulti bool } func ( *records) ( reflect.Value, ...string) { for , := range { var reflect.Value if := .MethodByName(); .IsValid() { = .Call(nil)[0] } if .Kind() == reflect.Struct && !.IsValid() { = .FieldByName() } if !.IsValid() { panic(fmt.Sprintf("unknown accessor: %v.%s", .Type(), )) } if , := .Interface().(protoreflect.Value); { = .MethodByName("Interface").Call(nil)[0] if !.IsNil() { = .Elem() } } // Ignore zero values. var bool switch .Kind() { case reflect.Interface, reflect.Slice: = .IsNil() case reflect.Bool: = .Bool() == false case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: = .Int() == 0 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: = .Uint() == 0 case reflect.String: = .String() == "" } if , := .Interface().(list); { = .Len() == 0 } if { continue } // Format the value. var string := .Interface() switch v := .(type) { case list: = formatListOpt(, false, .allowMulti) case protoreflect.FieldDescriptor, protoreflect.OneofDescriptor, protoreflect.EnumValueDescriptor, protoreflect.MethodDescriptor: = string(.(protoreflect.Descriptor).Name()) case protoreflect.Descriptor: = string(.FullName()) case string: = strconv.Quote() case []byte: = fmt.Sprintf("%q", ) default: = fmt.Sprint() } .recs = append(.recs, [2]string{, }) } } func ( *records) () string { var []string // In single line mode, simply join all records with commas. if !.allowMulti { for , := range .recs { = append(, [0]+formatColon(0)+[1]) } return joinStrings(, false) } // In allowMulti line mode, align single line records for more readable output. var int := func( int) { for , := range .recs[len():] { = append(, [0]+formatColon(-len([0]))+[1]) } = 0 } for , := range .recs { if := strings.Contains([1], "\n"); { () = append(, [0]+formatColon(0)+strings.Join(strings.Split([1], "\n"), "\n\t")) } else if < len([0]) { = len([0]) } } (len(.recs)) return joinStrings(, true) } func ( int) string { // Deliberately introduce instability into the debug output to // discourage users from performing string comparisons. // This provides us flexibility to change the output in the future. if detrand.Bool() { return ":" + strings.Repeat(" ", 1+) // use non-breaking spaces (U+00a0) } else { return ":" + strings.Repeat(" ", 1+) // use regular spaces (U+0020) } } func ( []string, bool) string { if len() == 0 { return "" } if { return "\n\t" + strings.Join(, "\n\t") + "\n" } return strings.Join(, ", ") }