github.com/googleapis/api-linter@v1.65.2/rules/internal/utils/find.go (about) 1 // Copyright 2020 Google LLC 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // https://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package utils 16 17 import ( 18 "sort" 19 "strings" 20 21 "github.com/jhump/protoreflect/desc" 22 "google.golang.org/protobuf/types/descriptorpb" 23 ) 24 25 // FindMessage looks for a message in a file and all imports within the 26 // same package. 27 func FindMessage(f *desc.FileDescriptor, name string) *desc.MessageDescriptor { 28 // FileDescriptor.FindMessage requires fully-qualified message names; 29 // attempt to infer that. 30 if !strings.Contains(name, ".") && f.GetPackage() != "" { 31 name = f.GetPackage() + "." + name 32 } 33 34 // Attempt to find the message in the file provided. 35 if m := f.FindMessage(name); m != nil { 36 return m 37 } 38 39 // Attempt to find the message in any dependency files if they are in the 40 // same package. 41 for _, dep := range f.GetDependencies() { 42 if f.GetPackage() == dep.GetPackage() { 43 if m := FindMessage(dep, name); m != nil { 44 return m 45 } 46 } 47 } 48 49 // Whelp, no luck. Too bad. 50 return nil 51 } 52 53 // FindMethod searches a file and all imports within the same package, and 54 // returns the method with a given name, or nil if none is found. 55 func FindMethod(f *desc.FileDescriptor, name string) *desc.MethodDescriptor { 56 for _, s := range getServices(f) { 57 for _, m := range s.GetMethods() { 58 if m.GetName() == name { 59 return m 60 } 61 } 62 } 63 return nil 64 } 65 66 // getServices finds all services in a file and all imports within the 67 // same package. 68 func getServices(f *desc.FileDescriptor) []*desc.ServiceDescriptor { 69 answer := f.GetServices() 70 for _, dep := range f.GetDependencies() { 71 if f.GetPackage() == dep.GetPackage() { 72 answer = append(answer, getServices(dep)...) 73 } 74 } 75 return answer 76 } 77 78 // GetAllDependencies returns all dependencies. 79 func GetAllDependencies(file *desc.FileDescriptor) map[string]*desc.FileDescriptor { 80 answer := map[string]*desc.FileDescriptor{file.GetName(): file} 81 for _, f := range file.GetDependencies() { 82 if _, found := answer[f.GetName()]; !found { 83 answer[f.GetName()] = f 84 for name, f2 := range GetAllDependencies(f) { 85 answer[name] = f2 86 } 87 } 88 } 89 return answer 90 } 91 92 type fieldSorter []*desc.FieldDescriptor 93 94 // Len is part of sort.Interface. 95 func (f fieldSorter) Len() int { 96 return len(f) 97 } 98 99 // Swap is part of sort.Interface. 100 func (f fieldSorter) Swap(i, j int) { 101 f[i], f[j] = f[j], f[i] 102 } 103 104 // Less is part of sort.Interface. Compare field number. 105 func (f fieldSorter) Less(i, j int) bool { 106 return f[i].GetNumber() < f[j].GetNumber() 107 } 108 109 // GetRepeatedMessageFields returns all fields labeled `repeated` of type 110 // Message in the given message, sorted in field number order. 111 func GetRepeatedMessageFields(m *desc.MessageDescriptor) []*desc.FieldDescriptor { 112 var fields fieldSorter 113 114 // If an unresolable message is fed into this helper, return empty slice. 115 if m == nil { 116 return fields 117 } 118 119 for _, f := range m.GetFields() { 120 if f.IsRepeated() && f.GetType() == descriptorpb.FieldDescriptorProto_TYPE_MESSAGE { 121 fields = append(fields, f) 122 } 123 } 124 125 sort.Sort(fields) 126 127 return fields 128 } 129 130 // FindFieldDotNotation returns a field descriptor from a given message that 131 // corresponds to the dot separated path e.g. "book.name". If the path is 132 // unresolable the method returns nil. This is especially useful for resolving 133 // path variables in google.api.http and nested fields in 134 // google.api.method_signature annotations. 135 func FindFieldDotNotation(msg *desc.MessageDescriptor, ref string) *desc.FieldDescriptor { 136 path := strings.Split(ref, ".") 137 end := len(path) - 1 138 for i, seg := range path { 139 field := msg.FindFieldByName(seg) 140 if field == nil { 141 return nil 142 } 143 144 if m := field.GetMessageType(); m != nil && i != end { 145 msg = m 146 continue 147 } 148 149 return field 150 } 151 152 return nil 153 }