// 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 := reflect.ValueOf(.Get()) .Append(, []methodAndName{ {.MethodByName("Path"), "Path"}, {.MethodByName("Package"), "Package"}, {.MethodByName("IsPublic"), "IsPublic"}, {.MethodByName("IsWeak"), "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, && !, nil)) } return + joinStrings(, && ) + } } type methodAndName struct { method reflect.Value name string } func ( fmt.State, rune, protoreflect.Descriptor) { io.WriteString(, formatDescOpt(, true, == 'v' && (.Flag('+') || .Flag('#')), nil)) } func ( protoreflect.Descriptor, , bool, func(string)) string { return formatDescOpt(, , , ) } func ( protoreflect.Descriptor, , bool, func(string)) string { := reflect.ValueOf() := .MethodByName("ProtoType").Type().In(0) , := "{", "}" if { = .Name() + "{" } , := .(protoreflect.FileDescriptor) := records{ allowMulti: , record: , } if .IsPlaceholder() { if { .Append(, []methodAndName{ {.MethodByName("Path"), "Path"}, {.MethodByName("Package"), "Package"}, {.MethodByName("IsPlaceholder"), "IsPlaceholder"}, }...) } else { .Append(, []methodAndName{ {.MethodByName("FullName"), "FullName"}, {.MethodByName("IsPlaceholder"), "IsPlaceholder"}, }...) } } else { switch { case : .Append(, methodAndName{.MethodByName("Syntax"), "Syntax"}) case : .Append(, []methodAndName{ {.MethodByName("Syntax"), "Syntax"}, {.MethodByName("FullName"), "FullName"}, }...) default: .Append(, methodAndName{.MethodByName("Name"), "Name"}) } switch t := .(type) { case protoreflect.FieldDescriptor: := []methodAndName{ {.MethodByName("Number"), "Number"}, {.MethodByName("Cardinality"), "Cardinality"}, {.MethodByName("Kind"), "Kind"}, {.MethodByName("HasJSONName"), "HasJSONName"}, {.MethodByName("JSONName"), "JSONName"}, {.MethodByName("HasPresence"), "HasPresence"}, {.MethodByName("IsExtension"), "IsExtension"}, {.MethodByName("IsPacked"), "IsPacked"}, {.MethodByName("IsWeak"), "IsWeak"}, {.MethodByName("IsList"), "IsList"}, {.MethodByName("IsMap"), "IsMap"}, {.MethodByName("MapKey"), "MapKey"}, {.MethodByName("MapValue"), "MapValue"}, {.MethodByName("HasDefault"), "HasDefault"}, {.MethodByName("Default"), "Default"}, {.MethodByName("ContainingOneof"), "ContainingOneof"}, {.MethodByName("ContainingMessage"), "ContainingMessage"}, {.MethodByName("Message"), "Message"}, {.MethodByName("Enum"), "Enum"}, } for , := range { switch .name { case "MapKey": if := .MapKey(); != nil { .recs = append(.recs, [2]string{"MapKey", .Kind().String()}) } case "MapValue": if := .MapValue(); != nil { switch .Kind() { case protoreflect.EnumKind: .AppendRecs("MapValue", [2]string{"MapValue", string(.Enum().FullName())}) case protoreflect.MessageKind, protoreflect.GroupKind: .AppendRecs("MapValue", [2]string{"MapValue", string(.Message().FullName())}) default: .AppendRecs("MapValue", [2]string{"MapValue", .Kind().String()}) } } case "ContainingOneof": if := .ContainingOneof(); != nil { .AppendRecs("ContainingOneof", [2]string{"Oneof", string(.Name())}) } case "ContainingMessage": if .IsExtension() { .AppendRecs("ContainingMessage", [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 { .AppendRecs("Fields", [2]string{"Fields", "[" + joinStrings(, false) + "]"}) } case protoreflect.FileDescriptor: .Append(, []methodAndName{ {.MethodByName("Path"), "Path"}, {.MethodByName("Package"), "Package"}, {.MethodByName("Imports"), "Imports"}, {.MethodByName("Messages"), "Messages"}, {.MethodByName("Enums"), "Enums"}, {.MethodByName("Extensions"), "Extensions"}, {.MethodByName("Services"), "Services"}, }...) case protoreflect.MessageDescriptor: .Append(, []methodAndName{ {.MethodByName("IsMapEntry"), "IsMapEntry"}, {.MethodByName("Fields"), "Fields"}, {.MethodByName("Oneofs"), "Oneofs"}, {.MethodByName("ReservedNames"), "ReservedNames"}, {.MethodByName("ReservedRanges"), "ReservedRanges"}, {.MethodByName("RequiredNumbers"), "RequiredNumbers"}, {.MethodByName("ExtensionRanges"), "ExtensionRanges"}, {.MethodByName("Messages"), "Messages"}, {.MethodByName("Enums"), "Enums"}, {.MethodByName("Extensions"), "Extensions"}, }...) case protoreflect.EnumDescriptor: .Append(, []methodAndName{ {.MethodByName("Values"), "Values"}, {.MethodByName("ReservedNames"), "ReservedNames"}, {.MethodByName("ReservedRanges"), "ReservedRanges"}, {.MethodByName("IsClosed"), "IsClosed"}, }...) case protoreflect.EnumValueDescriptor: .Append(, []methodAndName{ {.MethodByName("Number"), "Number"}, }...) case protoreflect.ServiceDescriptor: .Append(, []methodAndName{ {.MethodByName("Methods"), "Methods"}, }...) case protoreflect.MethodDescriptor: .Append(, []methodAndName{ {.MethodByName("Input"), "Input"}, {.MethodByName("Output"), "Output"}, {.MethodByName("IsStreamingClient"), "IsStreamingClient"}, {.MethodByName("IsStreamingServer"), "IsStreamingServer"}, }...) } if := .MethodByName("GoType"); .IsValid() { .Append(, methodAndName{, "GoType"}) } } return + .Join() + } type records struct { recs [][2]string allowMulti bool // record is a function that will be called for every Append() or // AppendRecs() call, to be used for testing with the // InternalFormatDescOptForTesting function. record func(string) } func ( *records) ( string, [2]string) { if .record != nil { .record() } .recs = append(.recs, ) } func ( *records) ( reflect.Value, ...methodAndName) { for , := range { if .record != nil { .record(.name) } var reflect.Value if .method.IsValid() { = .method.Call(nil)[0] } if .Kind() == reflect.Struct && !.IsValid() { = .FieldByName(.name) } if !.IsValid() { panic(fmt.Sprintf("unknown accessor: %v.%s", .Type(), .name)) } 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{.name, }) } } 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(, ", ") }