go.ligato.io/vpp-agent/v3@v3.5.0/plugins/restapi/jsonschema/converter/sourcecodeinfo.go (about) 1 package converter 2 3 import ( 4 "google.golang.org/protobuf/proto" 5 "google.golang.org/protobuf/types/descriptorpb" 6 ) 7 8 // Protobuf tag values for relevant message fields. Full list here: 9 // https://github.com/protocolbuffers/protobuf/blob/master/src/google/protobuf/descriptorpb.proto 10 const ( 11 tag_FileDescriptor_messageType int32 = 4 12 tag_FileDescriptor_enumType int32 = 5 13 tag_Descriptor_field int32 = 2 14 tag_Descriptor_nestedType int32 = 3 15 tag_Descriptor_enumType int32 = 4 16 tag_Descriptor_oneofDecl int32 = 8 17 tag_EnumDescriptor_value int32 = 2 18 ) 19 20 type sourceCodeInfo struct { 21 lookup map[proto.Message]*descriptorpb.SourceCodeInfo_Location 22 } 23 24 func (s sourceCodeInfo) GetMessage(m *descriptorpb.DescriptorProto) *descriptorpb.SourceCodeInfo_Location { 25 return s.lookup[m] 26 } 27 28 func (s sourceCodeInfo) GetField(f *descriptorpb.FieldDescriptorProto) *descriptorpb.SourceCodeInfo_Location { 29 return s.lookup[f] 30 } 31 32 func (s sourceCodeInfo) GetEnum(e *descriptorpb.EnumDescriptorProto) *descriptorpb.SourceCodeInfo_Location { 33 return s.lookup[e] 34 } 35 36 func (s sourceCodeInfo) GetEnumValue(e *descriptorpb.EnumValueDescriptorProto) *descriptorpb.SourceCodeInfo_Location { 37 return s.lookup[e] 38 } 39 40 func newSourceCodeInfo(fs []*descriptorpb.FileDescriptorProto) *sourceCodeInfo { 41 // For each source location in the provided files 42 // - resolve the (annoyingly) encoded path to its message/field/service/enum/etc definition 43 // - store the source info by its resolved definition 44 lookup := map[proto.Message]*descriptorpb.SourceCodeInfo_Location{} 45 for _, f := range fs { 46 for _, loc := range f.GetSourceCodeInfo().GetLocation() { 47 declaration := getDefinitionAtPath(f, loc.Path) 48 if declaration != nil { 49 lookup[declaration] = loc 50 } 51 } 52 } 53 return &sourceCodeInfo{lookup} 54 } 55 56 // Resolve a protobuf "file-source path" to its associated definition (eg message/field/enum/etc). 57 // Note that some paths don't point to definitions (some reference subcomponents like name, type, 58 // field #, etc) and will therefore return nil. 59 func getDefinitionAtPath(file *descriptorpb.FileDescriptorProto, path []int32) proto.Message { 60 // The way protobuf encodes "file-source path" is a little opaque/tricky; 61 // this doc describes how it works: 62 // https://github.com/protocolbuffers/protobuf/blob/master/src/google/protobuf/descriptorpb.proto#L730 63 64 // Starting at the root of the file descriptor, traverse its object graph by following the 65 // specified path (and updating our position/state at each step) until either: 66 // - we reach the definition referenced by the path (and return it) 67 // - we hit a dead end because the path references a grammar element more granular than a 68 // definition (so we return nil) 69 var pos proto.Message = file 70 for step := 0; step < len(path); step++ { 71 switch p := pos.(type) { 72 case *descriptorpb.FileDescriptorProto: 73 switch path[step] { 74 case tag_FileDescriptor_messageType: 75 step++ 76 pos = p.MessageType[path[step]] 77 case tag_FileDescriptor_enumType: 78 step++ 79 pos = p.EnumType[path[step]] 80 default: 81 return nil // ignore all other types 82 } 83 84 case *descriptorpb.DescriptorProto: 85 switch path[step] { 86 case tag_Descriptor_field: 87 step++ 88 pos = p.Field[path[step]] 89 case tag_Descriptor_nestedType: 90 step++ 91 pos = p.NestedType[path[step]] 92 case tag_Descriptor_enumType: 93 step++ 94 pos = p.EnumType[path[step]] 95 case tag_Descriptor_oneofDecl: 96 step++ 97 pos = p.OneofDecl[path[step]] 98 default: 99 return nil // ignore all other types 100 } 101 102 case *descriptorpb.EnumDescriptorProto: 103 switch path[step] { 104 case tag_EnumDescriptor_value: 105 step++ 106 pos = p.Value[path[step]] 107 default: 108 return nil // ignore all other types 109 } 110 111 default: 112 return nil // ignore all other types 113 } 114 } 115 return pos 116 }