go.ligato.io/vpp-agent/v3@v3.5.0/plugins/restapi/jsonschema/converter/proto_package.go (about)

     1  package converter
     2  
     3  import (
     4  	"strings"
     5  
     6  	"google.golang.org/protobuf/types/descriptorpb"
     7  )
     8  
     9  // ProtoPackage describes a package of Protobuf, which is an container of message types.
    10  type ProtoPackage struct {
    11  	name     string
    12  	parent   *ProtoPackage
    13  	children map[string]*ProtoPackage
    14  	types    map[string]*descriptorpb.DescriptorProto
    15  	enums    map[string]*descriptorpb.EnumDescriptorProto
    16  }
    17  
    18  func newProtoPackage(parent *ProtoPackage, name string) *ProtoPackage {
    19  	pkgName := name
    20  	if parent != nil {
    21  		pkgName = parent.name + "." + name
    22  	}
    23  
    24  	return &ProtoPackage{
    25  		name:     pkgName,
    26  		parent:   parent,
    27  		children: make(map[string]*ProtoPackage),
    28  		types:    make(map[string]*descriptorpb.DescriptorProto),
    29  		enums:    make(map[string]*descriptorpb.EnumDescriptorProto),
    30  	}
    31  }
    32  
    33  func (c *Converter) lookupType(pkg *ProtoPackage, name string) (*descriptorpb.DescriptorProto, string, bool) {
    34  	if strings.HasPrefix(name, ".") {
    35  		return c.relativelyLookupType(globalPkg, name[1:])
    36  	}
    37  
    38  	for ; pkg != nil; pkg = pkg.parent {
    39  		if desc, pkgName, ok := c.relativelyLookupType(pkg, name); ok {
    40  			return desc, pkgName, ok
    41  		}
    42  	}
    43  	return nil, "", false
    44  }
    45  
    46  func (c *Converter) lookupEnum(pkg *ProtoPackage, name string) (*descriptorpb.EnumDescriptorProto, string, bool) {
    47  	if strings.HasPrefix(name, ".") {
    48  		return c.relativelyLookupEnum(globalPkg, name[1:])
    49  	}
    50  
    51  	for ; pkg != nil; pkg = pkg.parent {
    52  		if desc, pkgName, ok := c.relativelyLookupEnum(pkg, name); ok {
    53  			return desc, pkgName, ok
    54  		}
    55  	}
    56  	return nil, "", false
    57  }
    58  
    59  func (c *Converter) relativelyLookupType(pkg *ProtoPackage, name string) (*descriptorpb.DescriptorProto, string, bool) {
    60  	components := strings.SplitN(name, ".", 2)
    61  	switch len(components) {
    62  	case 0:
    63  		c.logger.Debug("empty message name")
    64  		return nil, "", false
    65  	case 1:
    66  		found, ok := pkg.types[components[0]]
    67  		return found, pkg.name, ok
    68  	case 2:
    69  		c.logger.Tracef("Looking for %s in %s at %s (%v)", components[1], components[0], pkg.name, pkg)
    70  		if child, ok := pkg.children[components[0]]; ok {
    71  			found, pkgName, ok := c.relativelyLookupType(child, components[1])
    72  			return found, pkgName, ok
    73  		}
    74  		if msg, ok := pkg.types[components[0]]; ok {
    75  			found, ok := c.relativelyLookupNestedType(msg, components[1])
    76  			return found, pkg.name, ok
    77  		}
    78  		c.logger.WithField("component", components[0]).WithField("package_name", pkg.name).Debug("No such package nor message in package")
    79  		return nil, "", false
    80  	default:
    81  		c.logger.Error("Failed to lookup type")
    82  		return nil, "", false
    83  	}
    84  }
    85  
    86  func (c *Converter) relativelyLookupNestedType(desc *descriptorpb.DescriptorProto, name string) (*descriptorpb.DescriptorProto, bool) {
    87  	components := strings.Split(name, ".")
    88  componentLoop:
    89  	for _, component := range components {
    90  		for _, nested := range desc.GetNestedType() {
    91  			if nested.GetName() == component {
    92  				desc = nested
    93  				continue componentLoop
    94  			}
    95  		}
    96  		c.logger.WithField("component", component).WithField("description", desc.GetName()).Info("no such nested message")
    97  		return nil, false
    98  	}
    99  	return desc, true
   100  }
   101  
   102  func (c *Converter) relativelyLookupEnum(pkg *ProtoPackage, name string) (*descriptorpb.EnumDescriptorProto, string, bool) {
   103  	components := strings.SplitN(name, ".", 2)
   104  	switch len(components) {
   105  	case 0:
   106  		c.logger.Debug("empty enum name")
   107  		return nil, "", false
   108  	case 1:
   109  		found, ok := pkg.enums[components[0]]
   110  		return found, pkg.name, ok
   111  	case 2:
   112  		c.logger.Tracef("Looking for %s in %s at %s (%v)", components[1], components[0], pkg.name, pkg)
   113  		if child, ok := pkg.children[components[0]]; ok {
   114  			found, pkgName, ok := c.relativelyLookupEnum(child, components[1])
   115  			return found, pkgName, ok
   116  		}
   117  		if msg, ok := pkg.types[components[0]]; ok {
   118  			found, ok := c.relativelyLookupNestedEnum(msg, components[1])
   119  			return found, pkg.name, ok
   120  		}
   121  		c.logger.WithField("component", components[0]).WithField("package_name", pkg.name).Debug("No such package nor message in package")
   122  		return nil, "", false
   123  	default:
   124  		c.logger.Error("Failed to lookup type")
   125  		return nil, "", false
   126  	}
   127  }
   128  
   129  func (c *Converter) relativelyLookupNestedEnum(desc *descriptorpb.DescriptorProto, name string) (*descriptorpb.EnumDescriptorProto, bool) {
   130  	components := strings.Split(name, ".")
   131  
   132  	parent := desc
   133  
   134  	if len(components) > 1 {
   135  		// The enum is nested inside a potentially nested message definition.
   136  		msgComponents := strings.Join(components[0:len(components)-1], ".")
   137  		var found bool
   138  		parent, found = c.relativelyLookupNestedType(parent, msgComponents)
   139  		if !found {
   140  			return nil, false
   141  		}
   142  	}
   143  
   144  	// The enum is nested inside of a nested message. We need to dive down the
   145  	// tree to find the message the enum is nested in. Then we need to obtain the
   146  	// enum.
   147  	enumName := components[len(components)-1]
   148  	for _, enum := range parent.GetEnumType() {
   149  		if enum.GetName() == enumName {
   150  			return enum, true
   151  		}
   152  	}
   153  
   154  	return nil, false
   155  }
   156  
   157  func (c *Converter) relativelyLookupPackage(pkg *ProtoPackage, name string) (*ProtoPackage, bool) {
   158  	components := strings.Split(name, ".")
   159  	for _, c := range components {
   160  		var ok bool
   161  		pkg, ok = pkg.children[c]
   162  		if !ok {
   163  			return nil, false
   164  		}
   165  	}
   166  	return pkg, true
   167  }