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 }