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  }