1*1c12ee1eSDan Willemsen// Copyright 2018 The Go Authors. All rights reserved. 2*1c12ee1eSDan Willemsen// Use of this source code is governed by a BSD-style 3*1c12ee1eSDan Willemsen// license that can be found in the LICENSE file. 4*1c12ee1eSDan Willemsen 5*1c12ee1eSDan Willemsen// Package protoregistry provides data structures to register and lookup 6*1c12ee1eSDan Willemsen// protobuf descriptor types. 7*1c12ee1eSDan Willemsen// 8*1c12ee1eSDan Willemsen// The Files registry contains file descriptors and provides the ability 9*1c12ee1eSDan Willemsen// to iterate over the files or lookup a specific descriptor within the files. 10*1c12ee1eSDan Willemsen// Files only contains protobuf descriptors and has no understanding of Go 11*1c12ee1eSDan Willemsen// type information that may be associated with each descriptor. 12*1c12ee1eSDan Willemsen// 13*1c12ee1eSDan Willemsen// The Types registry contains descriptor types for which there is a known 14*1c12ee1eSDan Willemsen// Go type associated with that descriptor. It provides the ability to iterate 15*1c12ee1eSDan Willemsen// over the registered types or lookup a type by name. 16*1c12ee1eSDan Willemsenpackage protoregistry 17*1c12ee1eSDan Willemsen 18*1c12ee1eSDan Willemsenimport ( 19*1c12ee1eSDan Willemsen "fmt" 20*1c12ee1eSDan Willemsen "os" 21*1c12ee1eSDan Willemsen "strings" 22*1c12ee1eSDan Willemsen "sync" 23*1c12ee1eSDan Willemsen 24*1c12ee1eSDan Willemsen "google.golang.org/protobuf/internal/encoding/messageset" 25*1c12ee1eSDan Willemsen "google.golang.org/protobuf/internal/errors" 26*1c12ee1eSDan Willemsen "google.golang.org/protobuf/internal/flags" 27*1c12ee1eSDan Willemsen "google.golang.org/protobuf/reflect/protoreflect" 28*1c12ee1eSDan Willemsen) 29*1c12ee1eSDan Willemsen 30*1c12ee1eSDan Willemsen// conflictPolicy configures the policy for handling registration conflicts. 31*1c12ee1eSDan Willemsen// 32*1c12ee1eSDan Willemsen// It can be over-written at compile time with a linker-initialized variable: 33*1c12ee1eSDan Willemsen// 34*1c12ee1eSDan Willemsen// go build -ldflags "-X google.golang.org/protobuf/reflect/protoregistry.conflictPolicy=warn" 35*1c12ee1eSDan Willemsen// 36*1c12ee1eSDan Willemsen// It can be over-written at program execution with an environment variable: 37*1c12ee1eSDan Willemsen// 38*1c12ee1eSDan Willemsen// GOLANG_PROTOBUF_REGISTRATION_CONFLICT=warn ./main 39*1c12ee1eSDan Willemsen// 40*1c12ee1eSDan Willemsen// Neither of the above are covered by the compatibility promise and 41*1c12ee1eSDan Willemsen// may be removed in a future release of this module. 42*1c12ee1eSDan Willemsenvar conflictPolicy = "panic" // "panic" | "warn" | "ignore" 43*1c12ee1eSDan Willemsen 44*1c12ee1eSDan Willemsen// ignoreConflict reports whether to ignore a registration conflict 45*1c12ee1eSDan Willemsen// given the descriptor being registered and the error. 46*1c12ee1eSDan Willemsen// It is a variable so that the behavior is easily overridden in another file. 47*1c12ee1eSDan Willemsenvar ignoreConflict = func(d protoreflect.Descriptor, err error) bool { 48*1c12ee1eSDan Willemsen const env = "GOLANG_PROTOBUF_REGISTRATION_CONFLICT" 49*1c12ee1eSDan Willemsen const faq = "https://protobuf.dev/reference/go/faq#namespace-conflict" 50*1c12ee1eSDan Willemsen policy := conflictPolicy 51*1c12ee1eSDan Willemsen if v := os.Getenv(env); v != "" { 52*1c12ee1eSDan Willemsen policy = v 53*1c12ee1eSDan Willemsen } 54*1c12ee1eSDan Willemsen switch policy { 55*1c12ee1eSDan Willemsen case "panic": 56*1c12ee1eSDan Willemsen panic(fmt.Sprintf("%v\nSee %v\n", err, faq)) 57*1c12ee1eSDan Willemsen case "warn": 58*1c12ee1eSDan Willemsen fmt.Fprintf(os.Stderr, "WARNING: %v\nSee %v\n\n", err, faq) 59*1c12ee1eSDan Willemsen return true 60*1c12ee1eSDan Willemsen case "ignore": 61*1c12ee1eSDan Willemsen return true 62*1c12ee1eSDan Willemsen default: 63*1c12ee1eSDan Willemsen panic("invalid " + env + " value: " + os.Getenv(env)) 64*1c12ee1eSDan Willemsen } 65*1c12ee1eSDan Willemsen} 66*1c12ee1eSDan Willemsen 67*1c12ee1eSDan Willemsenvar globalMutex sync.RWMutex 68*1c12ee1eSDan Willemsen 69*1c12ee1eSDan Willemsen// GlobalFiles is a global registry of file descriptors. 70*1c12ee1eSDan Willemsenvar GlobalFiles *Files = new(Files) 71*1c12ee1eSDan Willemsen 72*1c12ee1eSDan Willemsen// GlobalTypes is the registry used by default for type lookups 73*1c12ee1eSDan Willemsen// unless a local registry is provided by the user. 74*1c12ee1eSDan Willemsenvar GlobalTypes *Types = new(Types) 75*1c12ee1eSDan Willemsen 76*1c12ee1eSDan Willemsen// NotFound is a sentinel error value to indicate that the type was not found. 77*1c12ee1eSDan Willemsen// 78*1c12ee1eSDan Willemsen// Since registry lookup can happen in the critical performance path, resolvers 79*1c12ee1eSDan Willemsen// must return this exact error value, not an error wrapping it. 80*1c12ee1eSDan Willemsenvar NotFound = errors.New("not found") 81*1c12ee1eSDan Willemsen 82*1c12ee1eSDan Willemsen// Files is a registry for looking up or iterating over files and the 83*1c12ee1eSDan Willemsen// descriptors contained within them. 84*1c12ee1eSDan Willemsen// The Find and Range methods are safe for concurrent use. 85*1c12ee1eSDan Willemsentype Files struct { 86*1c12ee1eSDan Willemsen // The map of descsByName contains: 87*1c12ee1eSDan Willemsen // EnumDescriptor 88*1c12ee1eSDan Willemsen // EnumValueDescriptor 89*1c12ee1eSDan Willemsen // MessageDescriptor 90*1c12ee1eSDan Willemsen // ExtensionDescriptor 91*1c12ee1eSDan Willemsen // ServiceDescriptor 92*1c12ee1eSDan Willemsen // *packageDescriptor 93*1c12ee1eSDan Willemsen // 94*1c12ee1eSDan Willemsen // Note that files are stored as a slice, since a package may contain 95*1c12ee1eSDan Willemsen // multiple files. Only top-level declarations are registered. 96*1c12ee1eSDan Willemsen // Note that enum values are in the top-level since that are in the same 97*1c12ee1eSDan Willemsen // scope as the parent enum. 98*1c12ee1eSDan Willemsen descsByName map[protoreflect.FullName]interface{} 99*1c12ee1eSDan Willemsen filesByPath map[string][]protoreflect.FileDescriptor 100*1c12ee1eSDan Willemsen numFiles int 101*1c12ee1eSDan Willemsen} 102*1c12ee1eSDan Willemsen 103*1c12ee1eSDan Willemsentype packageDescriptor struct { 104*1c12ee1eSDan Willemsen files []protoreflect.FileDescriptor 105*1c12ee1eSDan Willemsen} 106*1c12ee1eSDan Willemsen 107*1c12ee1eSDan Willemsen// RegisterFile registers the provided file descriptor. 108*1c12ee1eSDan Willemsen// 109*1c12ee1eSDan Willemsen// If any descriptor within the file conflicts with the descriptor of any 110*1c12ee1eSDan Willemsen// previously registered file (e.g., two enums with the same full name), 111*1c12ee1eSDan Willemsen// then the file is not registered and an error is returned. 112*1c12ee1eSDan Willemsen// 113*1c12ee1eSDan Willemsen// It is permitted for multiple files to have the same file path. 114*1c12ee1eSDan Willemsenfunc (r *Files) RegisterFile(file protoreflect.FileDescriptor) error { 115*1c12ee1eSDan Willemsen if r == GlobalFiles { 116*1c12ee1eSDan Willemsen globalMutex.Lock() 117*1c12ee1eSDan Willemsen defer globalMutex.Unlock() 118*1c12ee1eSDan Willemsen } 119*1c12ee1eSDan Willemsen if r.descsByName == nil { 120*1c12ee1eSDan Willemsen r.descsByName = map[protoreflect.FullName]interface{}{ 121*1c12ee1eSDan Willemsen "": &packageDescriptor{}, 122*1c12ee1eSDan Willemsen } 123*1c12ee1eSDan Willemsen r.filesByPath = make(map[string][]protoreflect.FileDescriptor) 124*1c12ee1eSDan Willemsen } 125*1c12ee1eSDan Willemsen path := file.Path() 126*1c12ee1eSDan Willemsen if prev := r.filesByPath[path]; len(prev) > 0 { 127*1c12ee1eSDan Willemsen r.checkGenProtoConflict(path) 128*1c12ee1eSDan Willemsen err := errors.New("file %q is already registered", file.Path()) 129*1c12ee1eSDan Willemsen err = amendErrorWithCaller(err, prev[0], file) 130*1c12ee1eSDan Willemsen if !(r == GlobalFiles && ignoreConflict(file, err)) { 131*1c12ee1eSDan Willemsen return err 132*1c12ee1eSDan Willemsen } 133*1c12ee1eSDan Willemsen } 134*1c12ee1eSDan Willemsen 135*1c12ee1eSDan Willemsen for name := file.Package(); name != ""; name = name.Parent() { 136*1c12ee1eSDan Willemsen switch prev := r.descsByName[name]; prev.(type) { 137*1c12ee1eSDan Willemsen case nil, *packageDescriptor: 138*1c12ee1eSDan Willemsen default: 139*1c12ee1eSDan Willemsen err := errors.New("file %q has a package name conflict over %v", file.Path(), name) 140*1c12ee1eSDan Willemsen err = amendErrorWithCaller(err, prev, file) 141*1c12ee1eSDan Willemsen if r == GlobalFiles && ignoreConflict(file, err) { 142*1c12ee1eSDan Willemsen err = nil 143*1c12ee1eSDan Willemsen } 144*1c12ee1eSDan Willemsen return err 145*1c12ee1eSDan Willemsen } 146*1c12ee1eSDan Willemsen } 147*1c12ee1eSDan Willemsen var err error 148*1c12ee1eSDan Willemsen var hasConflict bool 149*1c12ee1eSDan Willemsen rangeTopLevelDescriptors(file, func(d protoreflect.Descriptor) { 150*1c12ee1eSDan Willemsen if prev := r.descsByName[d.FullName()]; prev != nil { 151*1c12ee1eSDan Willemsen hasConflict = true 152*1c12ee1eSDan Willemsen err = errors.New("file %q has a name conflict over %v", file.Path(), d.FullName()) 153*1c12ee1eSDan Willemsen err = amendErrorWithCaller(err, prev, file) 154*1c12ee1eSDan Willemsen if r == GlobalFiles && ignoreConflict(d, err) { 155*1c12ee1eSDan Willemsen err = nil 156*1c12ee1eSDan Willemsen } 157*1c12ee1eSDan Willemsen } 158*1c12ee1eSDan Willemsen }) 159*1c12ee1eSDan Willemsen if hasConflict { 160*1c12ee1eSDan Willemsen return err 161*1c12ee1eSDan Willemsen } 162*1c12ee1eSDan Willemsen 163*1c12ee1eSDan Willemsen for name := file.Package(); name != ""; name = name.Parent() { 164*1c12ee1eSDan Willemsen if r.descsByName[name] == nil { 165*1c12ee1eSDan Willemsen r.descsByName[name] = &packageDescriptor{} 166*1c12ee1eSDan Willemsen } 167*1c12ee1eSDan Willemsen } 168*1c12ee1eSDan Willemsen p := r.descsByName[file.Package()].(*packageDescriptor) 169*1c12ee1eSDan Willemsen p.files = append(p.files, file) 170*1c12ee1eSDan Willemsen rangeTopLevelDescriptors(file, func(d protoreflect.Descriptor) { 171*1c12ee1eSDan Willemsen r.descsByName[d.FullName()] = d 172*1c12ee1eSDan Willemsen }) 173*1c12ee1eSDan Willemsen r.filesByPath[path] = append(r.filesByPath[path], file) 174*1c12ee1eSDan Willemsen r.numFiles++ 175*1c12ee1eSDan Willemsen return nil 176*1c12ee1eSDan Willemsen} 177*1c12ee1eSDan Willemsen 178*1c12ee1eSDan Willemsen// Several well-known types were hosted in the google.golang.org/genproto module 179*1c12ee1eSDan Willemsen// but were later moved to this module. To avoid a weak dependency on the 180*1c12ee1eSDan Willemsen// genproto module (and its relatively large set of transitive dependencies), 181*1c12ee1eSDan Willemsen// we rely on a registration conflict to determine whether the genproto version 182*1c12ee1eSDan Willemsen// is too old (i.e., does not contain aliases to the new type declarations). 183*1c12ee1eSDan Willemsenfunc (r *Files) checkGenProtoConflict(path string) { 184*1c12ee1eSDan Willemsen if r != GlobalFiles { 185*1c12ee1eSDan Willemsen return 186*1c12ee1eSDan Willemsen } 187*1c12ee1eSDan Willemsen var prevPath string 188*1c12ee1eSDan Willemsen const prevModule = "google.golang.org/genproto" 189*1c12ee1eSDan Willemsen const prevVersion = "cb27e3aa (May 26th, 2020)" 190*1c12ee1eSDan Willemsen switch path { 191*1c12ee1eSDan Willemsen case "google/protobuf/field_mask.proto": 192*1c12ee1eSDan Willemsen prevPath = prevModule + "/protobuf/field_mask" 193*1c12ee1eSDan Willemsen case "google/protobuf/api.proto": 194*1c12ee1eSDan Willemsen prevPath = prevModule + "/protobuf/api" 195*1c12ee1eSDan Willemsen case "google/protobuf/type.proto": 196*1c12ee1eSDan Willemsen prevPath = prevModule + "/protobuf/ptype" 197*1c12ee1eSDan Willemsen case "google/protobuf/source_context.proto": 198*1c12ee1eSDan Willemsen prevPath = prevModule + "/protobuf/source_context" 199*1c12ee1eSDan Willemsen default: 200*1c12ee1eSDan Willemsen return 201*1c12ee1eSDan Willemsen } 202*1c12ee1eSDan Willemsen pkgName := strings.TrimSuffix(strings.TrimPrefix(path, "google/protobuf/"), ".proto") 203*1c12ee1eSDan Willemsen pkgName = strings.Replace(pkgName, "_", "", -1) + "pb" // e.g., "field_mask" => "fieldmaskpb" 204*1c12ee1eSDan Willemsen currPath := "google.golang.org/protobuf/types/known/" + pkgName 205*1c12ee1eSDan Willemsen panic(fmt.Sprintf(""+ 206*1c12ee1eSDan Willemsen "duplicate registration of %q\n"+ 207*1c12ee1eSDan Willemsen "\n"+ 208*1c12ee1eSDan Willemsen "The generated definition for this file has moved:\n"+ 209*1c12ee1eSDan Willemsen "\tfrom: %q\n"+ 210*1c12ee1eSDan Willemsen "\tto: %q\n"+ 211*1c12ee1eSDan Willemsen "A dependency on the %q module must\n"+ 212*1c12ee1eSDan Willemsen "be at version %v or higher.\n"+ 213*1c12ee1eSDan Willemsen "\n"+ 214*1c12ee1eSDan Willemsen "Upgrade the dependency by running:\n"+ 215*1c12ee1eSDan Willemsen "\tgo get -u %v\n", 216*1c12ee1eSDan Willemsen path, prevPath, currPath, prevModule, prevVersion, prevPath)) 217*1c12ee1eSDan Willemsen} 218*1c12ee1eSDan Willemsen 219*1c12ee1eSDan Willemsen// FindDescriptorByName looks up a descriptor by the full name. 220*1c12ee1eSDan Willemsen// 221*1c12ee1eSDan Willemsen// This returns (nil, NotFound) if not found. 222*1c12ee1eSDan Willemsenfunc (r *Files) FindDescriptorByName(name protoreflect.FullName) (protoreflect.Descriptor, error) { 223*1c12ee1eSDan Willemsen if r == nil { 224*1c12ee1eSDan Willemsen return nil, NotFound 225*1c12ee1eSDan Willemsen } 226*1c12ee1eSDan Willemsen if r == GlobalFiles { 227*1c12ee1eSDan Willemsen globalMutex.RLock() 228*1c12ee1eSDan Willemsen defer globalMutex.RUnlock() 229*1c12ee1eSDan Willemsen } 230*1c12ee1eSDan Willemsen prefix := name 231*1c12ee1eSDan Willemsen suffix := nameSuffix("") 232*1c12ee1eSDan Willemsen for prefix != "" { 233*1c12ee1eSDan Willemsen if d, ok := r.descsByName[prefix]; ok { 234*1c12ee1eSDan Willemsen switch d := d.(type) { 235*1c12ee1eSDan Willemsen case protoreflect.EnumDescriptor: 236*1c12ee1eSDan Willemsen if d.FullName() == name { 237*1c12ee1eSDan Willemsen return d, nil 238*1c12ee1eSDan Willemsen } 239*1c12ee1eSDan Willemsen case protoreflect.EnumValueDescriptor: 240*1c12ee1eSDan Willemsen if d.FullName() == name { 241*1c12ee1eSDan Willemsen return d, nil 242*1c12ee1eSDan Willemsen } 243*1c12ee1eSDan Willemsen case protoreflect.MessageDescriptor: 244*1c12ee1eSDan Willemsen if d.FullName() == name { 245*1c12ee1eSDan Willemsen return d, nil 246*1c12ee1eSDan Willemsen } 247*1c12ee1eSDan Willemsen if d := findDescriptorInMessage(d, suffix); d != nil && d.FullName() == name { 248*1c12ee1eSDan Willemsen return d, nil 249*1c12ee1eSDan Willemsen } 250*1c12ee1eSDan Willemsen case protoreflect.ExtensionDescriptor: 251*1c12ee1eSDan Willemsen if d.FullName() == name { 252*1c12ee1eSDan Willemsen return d, nil 253*1c12ee1eSDan Willemsen } 254*1c12ee1eSDan Willemsen case protoreflect.ServiceDescriptor: 255*1c12ee1eSDan Willemsen if d.FullName() == name { 256*1c12ee1eSDan Willemsen return d, nil 257*1c12ee1eSDan Willemsen } 258*1c12ee1eSDan Willemsen if d := d.Methods().ByName(suffix.Pop()); d != nil && d.FullName() == name { 259*1c12ee1eSDan Willemsen return d, nil 260*1c12ee1eSDan Willemsen } 261*1c12ee1eSDan Willemsen } 262*1c12ee1eSDan Willemsen return nil, NotFound 263*1c12ee1eSDan Willemsen } 264*1c12ee1eSDan Willemsen prefix = prefix.Parent() 265*1c12ee1eSDan Willemsen suffix = nameSuffix(name[len(prefix)+len("."):]) 266*1c12ee1eSDan Willemsen } 267*1c12ee1eSDan Willemsen return nil, NotFound 268*1c12ee1eSDan Willemsen} 269*1c12ee1eSDan Willemsen 270*1c12ee1eSDan Willemsenfunc findDescriptorInMessage(md protoreflect.MessageDescriptor, suffix nameSuffix) protoreflect.Descriptor { 271*1c12ee1eSDan Willemsen name := suffix.Pop() 272*1c12ee1eSDan Willemsen if suffix == "" { 273*1c12ee1eSDan Willemsen if ed := md.Enums().ByName(name); ed != nil { 274*1c12ee1eSDan Willemsen return ed 275*1c12ee1eSDan Willemsen } 276*1c12ee1eSDan Willemsen for i := md.Enums().Len() - 1; i >= 0; i-- { 277*1c12ee1eSDan Willemsen if vd := md.Enums().Get(i).Values().ByName(name); vd != nil { 278*1c12ee1eSDan Willemsen return vd 279*1c12ee1eSDan Willemsen } 280*1c12ee1eSDan Willemsen } 281*1c12ee1eSDan Willemsen if xd := md.Extensions().ByName(name); xd != nil { 282*1c12ee1eSDan Willemsen return xd 283*1c12ee1eSDan Willemsen } 284*1c12ee1eSDan Willemsen if fd := md.Fields().ByName(name); fd != nil { 285*1c12ee1eSDan Willemsen return fd 286*1c12ee1eSDan Willemsen } 287*1c12ee1eSDan Willemsen if od := md.Oneofs().ByName(name); od != nil { 288*1c12ee1eSDan Willemsen return od 289*1c12ee1eSDan Willemsen } 290*1c12ee1eSDan Willemsen } 291*1c12ee1eSDan Willemsen if md := md.Messages().ByName(name); md != nil { 292*1c12ee1eSDan Willemsen if suffix == "" { 293*1c12ee1eSDan Willemsen return md 294*1c12ee1eSDan Willemsen } 295*1c12ee1eSDan Willemsen return findDescriptorInMessage(md, suffix) 296*1c12ee1eSDan Willemsen } 297*1c12ee1eSDan Willemsen return nil 298*1c12ee1eSDan Willemsen} 299*1c12ee1eSDan Willemsen 300*1c12ee1eSDan Willemsentype nameSuffix string 301*1c12ee1eSDan Willemsen 302*1c12ee1eSDan Willemsenfunc (s *nameSuffix) Pop() (name protoreflect.Name) { 303*1c12ee1eSDan Willemsen if i := strings.IndexByte(string(*s), '.'); i >= 0 { 304*1c12ee1eSDan Willemsen name, *s = protoreflect.Name((*s)[:i]), (*s)[i+1:] 305*1c12ee1eSDan Willemsen } else { 306*1c12ee1eSDan Willemsen name, *s = protoreflect.Name((*s)), "" 307*1c12ee1eSDan Willemsen } 308*1c12ee1eSDan Willemsen return name 309*1c12ee1eSDan Willemsen} 310*1c12ee1eSDan Willemsen 311*1c12ee1eSDan Willemsen// FindFileByPath looks up a file by the path. 312*1c12ee1eSDan Willemsen// 313*1c12ee1eSDan Willemsen// This returns (nil, NotFound) if not found. 314*1c12ee1eSDan Willemsen// This returns an error if multiple files have the same path. 315*1c12ee1eSDan Willemsenfunc (r *Files) FindFileByPath(path string) (protoreflect.FileDescriptor, error) { 316*1c12ee1eSDan Willemsen if r == nil { 317*1c12ee1eSDan Willemsen return nil, NotFound 318*1c12ee1eSDan Willemsen } 319*1c12ee1eSDan Willemsen if r == GlobalFiles { 320*1c12ee1eSDan Willemsen globalMutex.RLock() 321*1c12ee1eSDan Willemsen defer globalMutex.RUnlock() 322*1c12ee1eSDan Willemsen } 323*1c12ee1eSDan Willemsen fds := r.filesByPath[path] 324*1c12ee1eSDan Willemsen switch len(fds) { 325*1c12ee1eSDan Willemsen case 0: 326*1c12ee1eSDan Willemsen return nil, NotFound 327*1c12ee1eSDan Willemsen case 1: 328*1c12ee1eSDan Willemsen return fds[0], nil 329*1c12ee1eSDan Willemsen default: 330*1c12ee1eSDan Willemsen return nil, errors.New("multiple files named %q", path) 331*1c12ee1eSDan Willemsen } 332*1c12ee1eSDan Willemsen} 333*1c12ee1eSDan Willemsen 334*1c12ee1eSDan Willemsen// NumFiles reports the number of registered files, 335*1c12ee1eSDan Willemsen// including duplicate files with the same name. 336*1c12ee1eSDan Willemsenfunc (r *Files) NumFiles() int { 337*1c12ee1eSDan Willemsen if r == nil { 338*1c12ee1eSDan Willemsen return 0 339*1c12ee1eSDan Willemsen } 340*1c12ee1eSDan Willemsen if r == GlobalFiles { 341*1c12ee1eSDan Willemsen globalMutex.RLock() 342*1c12ee1eSDan Willemsen defer globalMutex.RUnlock() 343*1c12ee1eSDan Willemsen } 344*1c12ee1eSDan Willemsen return r.numFiles 345*1c12ee1eSDan Willemsen} 346*1c12ee1eSDan Willemsen 347*1c12ee1eSDan Willemsen// RangeFiles iterates over all registered files while f returns true. 348*1c12ee1eSDan Willemsen// If multiple files have the same name, RangeFiles iterates over all of them. 349*1c12ee1eSDan Willemsen// The iteration order is undefined. 350*1c12ee1eSDan Willemsenfunc (r *Files) RangeFiles(f func(protoreflect.FileDescriptor) bool) { 351*1c12ee1eSDan Willemsen if r == nil { 352*1c12ee1eSDan Willemsen return 353*1c12ee1eSDan Willemsen } 354*1c12ee1eSDan Willemsen if r == GlobalFiles { 355*1c12ee1eSDan Willemsen globalMutex.RLock() 356*1c12ee1eSDan Willemsen defer globalMutex.RUnlock() 357*1c12ee1eSDan Willemsen } 358*1c12ee1eSDan Willemsen for _, files := range r.filesByPath { 359*1c12ee1eSDan Willemsen for _, file := range files { 360*1c12ee1eSDan Willemsen if !f(file) { 361*1c12ee1eSDan Willemsen return 362*1c12ee1eSDan Willemsen } 363*1c12ee1eSDan Willemsen } 364*1c12ee1eSDan Willemsen } 365*1c12ee1eSDan Willemsen} 366*1c12ee1eSDan Willemsen 367*1c12ee1eSDan Willemsen// NumFilesByPackage reports the number of registered files in a proto package. 368*1c12ee1eSDan Willemsenfunc (r *Files) NumFilesByPackage(name protoreflect.FullName) int { 369*1c12ee1eSDan Willemsen if r == nil { 370*1c12ee1eSDan Willemsen return 0 371*1c12ee1eSDan Willemsen } 372*1c12ee1eSDan Willemsen if r == GlobalFiles { 373*1c12ee1eSDan Willemsen globalMutex.RLock() 374*1c12ee1eSDan Willemsen defer globalMutex.RUnlock() 375*1c12ee1eSDan Willemsen } 376*1c12ee1eSDan Willemsen p, ok := r.descsByName[name].(*packageDescriptor) 377*1c12ee1eSDan Willemsen if !ok { 378*1c12ee1eSDan Willemsen return 0 379*1c12ee1eSDan Willemsen } 380*1c12ee1eSDan Willemsen return len(p.files) 381*1c12ee1eSDan Willemsen} 382*1c12ee1eSDan Willemsen 383*1c12ee1eSDan Willemsen// RangeFilesByPackage iterates over all registered files in a given proto package 384*1c12ee1eSDan Willemsen// while f returns true. The iteration order is undefined. 385*1c12ee1eSDan Willemsenfunc (r *Files) RangeFilesByPackage(name protoreflect.FullName, f func(protoreflect.FileDescriptor) bool) { 386*1c12ee1eSDan Willemsen if r == nil { 387*1c12ee1eSDan Willemsen return 388*1c12ee1eSDan Willemsen } 389*1c12ee1eSDan Willemsen if r == GlobalFiles { 390*1c12ee1eSDan Willemsen globalMutex.RLock() 391*1c12ee1eSDan Willemsen defer globalMutex.RUnlock() 392*1c12ee1eSDan Willemsen } 393*1c12ee1eSDan Willemsen p, ok := r.descsByName[name].(*packageDescriptor) 394*1c12ee1eSDan Willemsen if !ok { 395*1c12ee1eSDan Willemsen return 396*1c12ee1eSDan Willemsen } 397*1c12ee1eSDan Willemsen for _, file := range p.files { 398*1c12ee1eSDan Willemsen if !f(file) { 399*1c12ee1eSDan Willemsen return 400*1c12ee1eSDan Willemsen } 401*1c12ee1eSDan Willemsen } 402*1c12ee1eSDan Willemsen} 403*1c12ee1eSDan Willemsen 404*1c12ee1eSDan Willemsen// rangeTopLevelDescriptors iterates over all top-level descriptors in a file 405*1c12ee1eSDan Willemsen// which will be directly entered into the registry. 406*1c12ee1eSDan Willemsenfunc rangeTopLevelDescriptors(fd protoreflect.FileDescriptor, f func(protoreflect.Descriptor)) { 407*1c12ee1eSDan Willemsen eds := fd.Enums() 408*1c12ee1eSDan Willemsen for i := eds.Len() - 1; i >= 0; i-- { 409*1c12ee1eSDan Willemsen f(eds.Get(i)) 410*1c12ee1eSDan Willemsen vds := eds.Get(i).Values() 411*1c12ee1eSDan Willemsen for i := vds.Len() - 1; i >= 0; i-- { 412*1c12ee1eSDan Willemsen f(vds.Get(i)) 413*1c12ee1eSDan Willemsen } 414*1c12ee1eSDan Willemsen } 415*1c12ee1eSDan Willemsen mds := fd.Messages() 416*1c12ee1eSDan Willemsen for i := mds.Len() - 1; i >= 0; i-- { 417*1c12ee1eSDan Willemsen f(mds.Get(i)) 418*1c12ee1eSDan Willemsen } 419*1c12ee1eSDan Willemsen xds := fd.Extensions() 420*1c12ee1eSDan Willemsen for i := xds.Len() - 1; i >= 0; i-- { 421*1c12ee1eSDan Willemsen f(xds.Get(i)) 422*1c12ee1eSDan Willemsen } 423*1c12ee1eSDan Willemsen sds := fd.Services() 424*1c12ee1eSDan Willemsen for i := sds.Len() - 1; i >= 0; i-- { 425*1c12ee1eSDan Willemsen f(sds.Get(i)) 426*1c12ee1eSDan Willemsen } 427*1c12ee1eSDan Willemsen} 428*1c12ee1eSDan Willemsen 429*1c12ee1eSDan Willemsen// MessageTypeResolver is an interface for looking up messages. 430*1c12ee1eSDan Willemsen// 431*1c12ee1eSDan Willemsen// A compliant implementation must deterministically return the same type 432*1c12ee1eSDan Willemsen// if no error is encountered. 433*1c12ee1eSDan Willemsen// 434*1c12ee1eSDan Willemsen// The Types type implements this interface. 435*1c12ee1eSDan Willemsentype MessageTypeResolver interface { 436*1c12ee1eSDan Willemsen // FindMessageByName looks up a message by its full name. 437*1c12ee1eSDan Willemsen // E.g., "google.protobuf.Any" 438*1c12ee1eSDan Willemsen // 439*1c12ee1eSDan Willemsen // This return (nil, NotFound) if not found. 440*1c12ee1eSDan Willemsen FindMessageByName(message protoreflect.FullName) (protoreflect.MessageType, error) 441*1c12ee1eSDan Willemsen 442*1c12ee1eSDan Willemsen // FindMessageByURL looks up a message by a URL identifier. 443*1c12ee1eSDan Willemsen // See documentation on google.protobuf.Any.type_url for the URL format. 444*1c12ee1eSDan Willemsen // 445*1c12ee1eSDan Willemsen // This returns (nil, NotFound) if not found. 446*1c12ee1eSDan Willemsen FindMessageByURL(url string) (protoreflect.MessageType, error) 447*1c12ee1eSDan Willemsen} 448*1c12ee1eSDan Willemsen 449*1c12ee1eSDan Willemsen// ExtensionTypeResolver is an interface for looking up extensions. 450*1c12ee1eSDan Willemsen// 451*1c12ee1eSDan Willemsen// A compliant implementation must deterministically return the same type 452*1c12ee1eSDan Willemsen// if no error is encountered. 453*1c12ee1eSDan Willemsen// 454*1c12ee1eSDan Willemsen// The Types type implements this interface. 455*1c12ee1eSDan Willemsentype ExtensionTypeResolver interface { 456*1c12ee1eSDan Willemsen // FindExtensionByName looks up a extension field by the field's full name. 457*1c12ee1eSDan Willemsen // Note that this is the full name of the field as determined by 458*1c12ee1eSDan Willemsen // where the extension is declared and is unrelated to the full name of the 459*1c12ee1eSDan Willemsen // message being extended. 460*1c12ee1eSDan Willemsen // 461*1c12ee1eSDan Willemsen // This returns (nil, NotFound) if not found. 462*1c12ee1eSDan Willemsen FindExtensionByName(field protoreflect.FullName) (protoreflect.ExtensionType, error) 463*1c12ee1eSDan Willemsen 464*1c12ee1eSDan Willemsen // FindExtensionByNumber looks up a extension field by the field number 465*1c12ee1eSDan Willemsen // within some parent message, identified by full name. 466*1c12ee1eSDan Willemsen // 467*1c12ee1eSDan Willemsen // This returns (nil, NotFound) if not found. 468*1c12ee1eSDan Willemsen FindExtensionByNumber(message protoreflect.FullName, field protoreflect.FieldNumber) (protoreflect.ExtensionType, error) 469*1c12ee1eSDan Willemsen} 470*1c12ee1eSDan Willemsen 471*1c12ee1eSDan Willemsenvar ( 472*1c12ee1eSDan Willemsen _ MessageTypeResolver = (*Types)(nil) 473*1c12ee1eSDan Willemsen _ ExtensionTypeResolver = (*Types)(nil) 474*1c12ee1eSDan Willemsen) 475*1c12ee1eSDan Willemsen 476*1c12ee1eSDan Willemsen// Types is a registry for looking up or iterating over descriptor types. 477*1c12ee1eSDan Willemsen// The Find and Range methods are safe for concurrent use. 478*1c12ee1eSDan Willemsentype Types struct { 479*1c12ee1eSDan Willemsen typesByName typesByName 480*1c12ee1eSDan Willemsen extensionsByMessage extensionsByMessage 481*1c12ee1eSDan Willemsen 482*1c12ee1eSDan Willemsen numEnums int 483*1c12ee1eSDan Willemsen numMessages int 484*1c12ee1eSDan Willemsen numExtensions int 485*1c12ee1eSDan Willemsen} 486*1c12ee1eSDan Willemsen 487*1c12ee1eSDan Willemsentype ( 488*1c12ee1eSDan Willemsen typesByName map[protoreflect.FullName]interface{} 489*1c12ee1eSDan Willemsen extensionsByMessage map[protoreflect.FullName]extensionsByNumber 490*1c12ee1eSDan Willemsen extensionsByNumber map[protoreflect.FieldNumber]protoreflect.ExtensionType 491*1c12ee1eSDan Willemsen) 492*1c12ee1eSDan Willemsen 493*1c12ee1eSDan Willemsen// RegisterMessage registers the provided message type. 494*1c12ee1eSDan Willemsen// 495*1c12ee1eSDan Willemsen// If a naming conflict occurs, the type is not registered and an error is returned. 496*1c12ee1eSDan Willemsenfunc (r *Types) RegisterMessage(mt protoreflect.MessageType) error { 497*1c12ee1eSDan Willemsen // Under rare circumstances getting the descriptor might recursively 498*1c12ee1eSDan Willemsen // examine the registry, so fetch it before locking. 499*1c12ee1eSDan Willemsen md := mt.Descriptor() 500*1c12ee1eSDan Willemsen 501*1c12ee1eSDan Willemsen if r == GlobalTypes { 502*1c12ee1eSDan Willemsen globalMutex.Lock() 503*1c12ee1eSDan Willemsen defer globalMutex.Unlock() 504*1c12ee1eSDan Willemsen } 505*1c12ee1eSDan Willemsen 506*1c12ee1eSDan Willemsen if err := r.register("message", md, mt); err != nil { 507*1c12ee1eSDan Willemsen return err 508*1c12ee1eSDan Willemsen } 509*1c12ee1eSDan Willemsen r.numMessages++ 510*1c12ee1eSDan Willemsen return nil 511*1c12ee1eSDan Willemsen} 512*1c12ee1eSDan Willemsen 513*1c12ee1eSDan Willemsen// RegisterEnum registers the provided enum type. 514*1c12ee1eSDan Willemsen// 515*1c12ee1eSDan Willemsen// If a naming conflict occurs, the type is not registered and an error is returned. 516*1c12ee1eSDan Willemsenfunc (r *Types) RegisterEnum(et protoreflect.EnumType) error { 517*1c12ee1eSDan Willemsen // Under rare circumstances getting the descriptor might recursively 518*1c12ee1eSDan Willemsen // examine the registry, so fetch it before locking. 519*1c12ee1eSDan Willemsen ed := et.Descriptor() 520*1c12ee1eSDan Willemsen 521*1c12ee1eSDan Willemsen if r == GlobalTypes { 522*1c12ee1eSDan Willemsen globalMutex.Lock() 523*1c12ee1eSDan Willemsen defer globalMutex.Unlock() 524*1c12ee1eSDan Willemsen } 525*1c12ee1eSDan Willemsen 526*1c12ee1eSDan Willemsen if err := r.register("enum", ed, et); err != nil { 527*1c12ee1eSDan Willemsen return err 528*1c12ee1eSDan Willemsen } 529*1c12ee1eSDan Willemsen r.numEnums++ 530*1c12ee1eSDan Willemsen return nil 531*1c12ee1eSDan Willemsen} 532*1c12ee1eSDan Willemsen 533*1c12ee1eSDan Willemsen// RegisterExtension registers the provided extension type. 534*1c12ee1eSDan Willemsen// 535*1c12ee1eSDan Willemsen// If a naming conflict occurs, the type is not registered and an error is returned. 536*1c12ee1eSDan Willemsenfunc (r *Types) RegisterExtension(xt protoreflect.ExtensionType) error { 537*1c12ee1eSDan Willemsen // Under rare circumstances getting the descriptor might recursively 538*1c12ee1eSDan Willemsen // examine the registry, so fetch it before locking. 539*1c12ee1eSDan Willemsen // 540*1c12ee1eSDan Willemsen // A known case where this can happen: Fetching the TypeDescriptor for a 541*1c12ee1eSDan Willemsen // legacy ExtensionDesc can consult the global registry. 542*1c12ee1eSDan Willemsen xd := xt.TypeDescriptor() 543*1c12ee1eSDan Willemsen 544*1c12ee1eSDan Willemsen if r == GlobalTypes { 545*1c12ee1eSDan Willemsen globalMutex.Lock() 546*1c12ee1eSDan Willemsen defer globalMutex.Unlock() 547*1c12ee1eSDan Willemsen } 548*1c12ee1eSDan Willemsen 549*1c12ee1eSDan Willemsen field := xd.Number() 550*1c12ee1eSDan Willemsen message := xd.ContainingMessage().FullName() 551*1c12ee1eSDan Willemsen if prev := r.extensionsByMessage[message][field]; prev != nil { 552*1c12ee1eSDan Willemsen err := errors.New("extension number %d is already registered on message %v", field, message) 553*1c12ee1eSDan Willemsen err = amendErrorWithCaller(err, prev, xt) 554*1c12ee1eSDan Willemsen if !(r == GlobalTypes && ignoreConflict(xd, err)) { 555*1c12ee1eSDan Willemsen return err 556*1c12ee1eSDan Willemsen } 557*1c12ee1eSDan Willemsen } 558*1c12ee1eSDan Willemsen 559*1c12ee1eSDan Willemsen if err := r.register("extension", xd, xt); err != nil { 560*1c12ee1eSDan Willemsen return err 561*1c12ee1eSDan Willemsen } 562*1c12ee1eSDan Willemsen if r.extensionsByMessage == nil { 563*1c12ee1eSDan Willemsen r.extensionsByMessage = make(extensionsByMessage) 564*1c12ee1eSDan Willemsen } 565*1c12ee1eSDan Willemsen if r.extensionsByMessage[message] == nil { 566*1c12ee1eSDan Willemsen r.extensionsByMessage[message] = make(extensionsByNumber) 567*1c12ee1eSDan Willemsen } 568*1c12ee1eSDan Willemsen r.extensionsByMessage[message][field] = xt 569*1c12ee1eSDan Willemsen r.numExtensions++ 570*1c12ee1eSDan Willemsen return nil 571*1c12ee1eSDan Willemsen} 572*1c12ee1eSDan Willemsen 573*1c12ee1eSDan Willemsenfunc (r *Types) register(kind string, desc protoreflect.Descriptor, typ interface{}) error { 574*1c12ee1eSDan Willemsen name := desc.FullName() 575*1c12ee1eSDan Willemsen prev := r.typesByName[name] 576*1c12ee1eSDan Willemsen if prev != nil { 577*1c12ee1eSDan Willemsen err := errors.New("%v %v is already registered", kind, name) 578*1c12ee1eSDan Willemsen err = amendErrorWithCaller(err, prev, typ) 579*1c12ee1eSDan Willemsen if !(r == GlobalTypes && ignoreConflict(desc, err)) { 580*1c12ee1eSDan Willemsen return err 581*1c12ee1eSDan Willemsen } 582*1c12ee1eSDan Willemsen } 583*1c12ee1eSDan Willemsen if r.typesByName == nil { 584*1c12ee1eSDan Willemsen r.typesByName = make(typesByName) 585*1c12ee1eSDan Willemsen } 586*1c12ee1eSDan Willemsen r.typesByName[name] = typ 587*1c12ee1eSDan Willemsen return nil 588*1c12ee1eSDan Willemsen} 589*1c12ee1eSDan Willemsen 590*1c12ee1eSDan Willemsen// FindEnumByName looks up an enum by its full name. 591*1c12ee1eSDan Willemsen// E.g., "google.protobuf.Field.Kind". 592*1c12ee1eSDan Willemsen// 593*1c12ee1eSDan Willemsen// This returns (nil, NotFound) if not found. 594*1c12ee1eSDan Willemsenfunc (r *Types) FindEnumByName(enum protoreflect.FullName) (protoreflect.EnumType, error) { 595*1c12ee1eSDan Willemsen if r == nil { 596*1c12ee1eSDan Willemsen return nil, NotFound 597*1c12ee1eSDan Willemsen } 598*1c12ee1eSDan Willemsen if r == GlobalTypes { 599*1c12ee1eSDan Willemsen globalMutex.RLock() 600*1c12ee1eSDan Willemsen defer globalMutex.RUnlock() 601*1c12ee1eSDan Willemsen } 602*1c12ee1eSDan Willemsen if v := r.typesByName[enum]; v != nil { 603*1c12ee1eSDan Willemsen if et, _ := v.(protoreflect.EnumType); et != nil { 604*1c12ee1eSDan Willemsen return et, nil 605*1c12ee1eSDan Willemsen } 606*1c12ee1eSDan Willemsen return nil, errors.New("found wrong type: got %v, want enum", typeName(v)) 607*1c12ee1eSDan Willemsen } 608*1c12ee1eSDan Willemsen return nil, NotFound 609*1c12ee1eSDan Willemsen} 610*1c12ee1eSDan Willemsen 611*1c12ee1eSDan Willemsen// FindMessageByName looks up a message by its full name, 612*1c12ee1eSDan Willemsen// e.g. "google.protobuf.Any". 613*1c12ee1eSDan Willemsen// 614*1c12ee1eSDan Willemsen// This returns (nil, NotFound) if not found. 615*1c12ee1eSDan Willemsenfunc (r *Types) FindMessageByName(message protoreflect.FullName) (protoreflect.MessageType, error) { 616*1c12ee1eSDan Willemsen if r == nil { 617*1c12ee1eSDan Willemsen return nil, NotFound 618*1c12ee1eSDan Willemsen } 619*1c12ee1eSDan Willemsen if r == GlobalTypes { 620*1c12ee1eSDan Willemsen globalMutex.RLock() 621*1c12ee1eSDan Willemsen defer globalMutex.RUnlock() 622*1c12ee1eSDan Willemsen } 623*1c12ee1eSDan Willemsen if v := r.typesByName[message]; v != nil { 624*1c12ee1eSDan Willemsen if mt, _ := v.(protoreflect.MessageType); mt != nil { 625*1c12ee1eSDan Willemsen return mt, nil 626*1c12ee1eSDan Willemsen } 627*1c12ee1eSDan Willemsen return nil, errors.New("found wrong type: got %v, want message", typeName(v)) 628*1c12ee1eSDan Willemsen } 629*1c12ee1eSDan Willemsen return nil, NotFound 630*1c12ee1eSDan Willemsen} 631*1c12ee1eSDan Willemsen 632*1c12ee1eSDan Willemsen// FindMessageByURL looks up a message by a URL identifier. 633*1c12ee1eSDan Willemsen// See documentation on google.protobuf.Any.type_url for the URL format. 634*1c12ee1eSDan Willemsen// 635*1c12ee1eSDan Willemsen// This returns (nil, NotFound) if not found. 636*1c12ee1eSDan Willemsenfunc (r *Types) FindMessageByURL(url string) (protoreflect.MessageType, error) { 637*1c12ee1eSDan Willemsen // This function is similar to FindMessageByName but 638*1c12ee1eSDan Willemsen // truncates anything before and including '/' in the URL. 639*1c12ee1eSDan Willemsen if r == nil { 640*1c12ee1eSDan Willemsen return nil, NotFound 641*1c12ee1eSDan Willemsen } 642*1c12ee1eSDan Willemsen if r == GlobalTypes { 643*1c12ee1eSDan Willemsen globalMutex.RLock() 644*1c12ee1eSDan Willemsen defer globalMutex.RUnlock() 645*1c12ee1eSDan Willemsen } 646*1c12ee1eSDan Willemsen message := protoreflect.FullName(url) 647*1c12ee1eSDan Willemsen if i := strings.LastIndexByte(url, '/'); i >= 0 { 648*1c12ee1eSDan Willemsen message = message[i+len("/"):] 649*1c12ee1eSDan Willemsen } 650*1c12ee1eSDan Willemsen 651*1c12ee1eSDan Willemsen if v := r.typesByName[message]; v != nil { 652*1c12ee1eSDan Willemsen if mt, _ := v.(protoreflect.MessageType); mt != nil { 653*1c12ee1eSDan Willemsen return mt, nil 654*1c12ee1eSDan Willemsen } 655*1c12ee1eSDan Willemsen return nil, errors.New("found wrong type: got %v, want message", typeName(v)) 656*1c12ee1eSDan Willemsen } 657*1c12ee1eSDan Willemsen return nil, NotFound 658*1c12ee1eSDan Willemsen} 659*1c12ee1eSDan Willemsen 660*1c12ee1eSDan Willemsen// FindExtensionByName looks up a extension field by the field's full name. 661*1c12ee1eSDan Willemsen// Note that this is the full name of the field as determined by 662*1c12ee1eSDan Willemsen// where the extension is declared and is unrelated to the full name of the 663*1c12ee1eSDan Willemsen// message being extended. 664*1c12ee1eSDan Willemsen// 665*1c12ee1eSDan Willemsen// This returns (nil, NotFound) if not found. 666*1c12ee1eSDan Willemsenfunc (r *Types) FindExtensionByName(field protoreflect.FullName) (protoreflect.ExtensionType, error) { 667*1c12ee1eSDan Willemsen if r == nil { 668*1c12ee1eSDan Willemsen return nil, NotFound 669*1c12ee1eSDan Willemsen } 670*1c12ee1eSDan Willemsen if r == GlobalTypes { 671*1c12ee1eSDan Willemsen globalMutex.RLock() 672*1c12ee1eSDan Willemsen defer globalMutex.RUnlock() 673*1c12ee1eSDan Willemsen } 674*1c12ee1eSDan Willemsen if v := r.typesByName[field]; v != nil { 675*1c12ee1eSDan Willemsen if xt, _ := v.(protoreflect.ExtensionType); xt != nil { 676*1c12ee1eSDan Willemsen return xt, nil 677*1c12ee1eSDan Willemsen } 678*1c12ee1eSDan Willemsen 679*1c12ee1eSDan Willemsen // MessageSet extensions are special in that the name of the extension 680*1c12ee1eSDan Willemsen // is the name of the message type used to extend the MessageSet. 681*1c12ee1eSDan Willemsen // This naming scheme is used by text and JSON serialization. 682*1c12ee1eSDan Willemsen // 683*1c12ee1eSDan Willemsen // This feature is protected by the ProtoLegacy flag since MessageSets 684*1c12ee1eSDan Willemsen // are a proto1 feature that is long deprecated. 685*1c12ee1eSDan Willemsen if flags.ProtoLegacy { 686*1c12ee1eSDan Willemsen if _, ok := v.(protoreflect.MessageType); ok { 687*1c12ee1eSDan Willemsen field := field.Append(messageset.ExtensionName) 688*1c12ee1eSDan Willemsen if v := r.typesByName[field]; v != nil { 689*1c12ee1eSDan Willemsen if xt, _ := v.(protoreflect.ExtensionType); xt != nil { 690*1c12ee1eSDan Willemsen if messageset.IsMessageSetExtension(xt.TypeDescriptor()) { 691*1c12ee1eSDan Willemsen return xt, nil 692*1c12ee1eSDan Willemsen } 693*1c12ee1eSDan Willemsen } 694*1c12ee1eSDan Willemsen } 695*1c12ee1eSDan Willemsen } 696*1c12ee1eSDan Willemsen } 697*1c12ee1eSDan Willemsen 698*1c12ee1eSDan Willemsen return nil, errors.New("found wrong type: got %v, want extension", typeName(v)) 699*1c12ee1eSDan Willemsen } 700*1c12ee1eSDan Willemsen return nil, NotFound 701*1c12ee1eSDan Willemsen} 702*1c12ee1eSDan Willemsen 703*1c12ee1eSDan Willemsen// FindExtensionByNumber looks up a extension field by the field number 704*1c12ee1eSDan Willemsen// within some parent message, identified by full name. 705*1c12ee1eSDan Willemsen// 706*1c12ee1eSDan Willemsen// This returns (nil, NotFound) if not found. 707*1c12ee1eSDan Willemsenfunc (r *Types) FindExtensionByNumber(message protoreflect.FullName, field protoreflect.FieldNumber) (protoreflect.ExtensionType, error) { 708*1c12ee1eSDan Willemsen if r == nil { 709*1c12ee1eSDan Willemsen return nil, NotFound 710*1c12ee1eSDan Willemsen } 711*1c12ee1eSDan Willemsen if r == GlobalTypes { 712*1c12ee1eSDan Willemsen globalMutex.RLock() 713*1c12ee1eSDan Willemsen defer globalMutex.RUnlock() 714*1c12ee1eSDan Willemsen } 715*1c12ee1eSDan Willemsen if xt, ok := r.extensionsByMessage[message][field]; ok { 716*1c12ee1eSDan Willemsen return xt, nil 717*1c12ee1eSDan Willemsen } 718*1c12ee1eSDan Willemsen return nil, NotFound 719*1c12ee1eSDan Willemsen} 720*1c12ee1eSDan Willemsen 721*1c12ee1eSDan Willemsen// NumEnums reports the number of registered enums. 722*1c12ee1eSDan Willemsenfunc (r *Types) NumEnums() int { 723*1c12ee1eSDan Willemsen if r == nil { 724*1c12ee1eSDan Willemsen return 0 725*1c12ee1eSDan Willemsen } 726*1c12ee1eSDan Willemsen if r == GlobalTypes { 727*1c12ee1eSDan Willemsen globalMutex.RLock() 728*1c12ee1eSDan Willemsen defer globalMutex.RUnlock() 729*1c12ee1eSDan Willemsen } 730*1c12ee1eSDan Willemsen return r.numEnums 731*1c12ee1eSDan Willemsen} 732*1c12ee1eSDan Willemsen 733*1c12ee1eSDan Willemsen// RangeEnums iterates over all registered enums while f returns true. 734*1c12ee1eSDan Willemsen// Iteration order is undefined. 735*1c12ee1eSDan Willemsenfunc (r *Types) RangeEnums(f func(protoreflect.EnumType) bool) { 736*1c12ee1eSDan Willemsen if r == nil { 737*1c12ee1eSDan Willemsen return 738*1c12ee1eSDan Willemsen } 739*1c12ee1eSDan Willemsen if r == GlobalTypes { 740*1c12ee1eSDan Willemsen globalMutex.RLock() 741*1c12ee1eSDan Willemsen defer globalMutex.RUnlock() 742*1c12ee1eSDan Willemsen } 743*1c12ee1eSDan Willemsen for _, typ := range r.typesByName { 744*1c12ee1eSDan Willemsen if et, ok := typ.(protoreflect.EnumType); ok { 745*1c12ee1eSDan Willemsen if !f(et) { 746*1c12ee1eSDan Willemsen return 747*1c12ee1eSDan Willemsen } 748*1c12ee1eSDan Willemsen } 749*1c12ee1eSDan Willemsen } 750*1c12ee1eSDan Willemsen} 751*1c12ee1eSDan Willemsen 752*1c12ee1eSDan Willemsen// NumMessages reports the number of registered messages. 753*1c12ee1eSDan Willemsenfunc (r *Types) NumMessages() int { 754*1c12ee1eSDan Willemsen if r == nil { 755*1c12ee1eSDan Willemsen return 0 756*1c12ee1eSDan Willemsen } 757*1c12ee1eSDan Willemsen if r == GlobalTypes { 758*1c12ee1eSDan Willemsen globalMutex.RLock() 759*1c12ee1eSDan Willemsen defer globalMutex.RUnlock() 760*1c12ee1eSDan Willemsen } 761*1c12ee1eSDan Willemsen return r.numMessages 762*1c12ee1eSDan Willemsen} 763*1c12ee1eSDan Willemsen 764*1c12ee1eSDan Willemsen// RangeMessages iterates over all registered messages while f returns true. 765*1c12ee1eSDan Willemsen// Iteration order is undefined. 766*1c12ee1eSDan Willemsenfunc (r *Types) RangeMessages(f func(protoreflect.MessageType) bool) { 767*1c12ee1eSDan Willemsen if r == nil { 768*1c12ee1eSDan Willemsen return 769*1c12ee1eSDan Willemsen } 770*1c12ee1eSDan Willemsen if r == GlobalTypes { 771*1c12ee1eSDan Willemsen globalMutex.RLock() 772*1c12ee1eSDan Willemsen defer globalMutex.RUnlock() 773*1c12ee1eSDan Willemsen } 774*1c12ee1eSDan Willemsen for _, typ := range r.typesByName { 775*1c12ee1eSDan Willemsen if mt, ok := typ.(protoreflect.MessageType); ok { 776*1c12ee1eSDan Willemsen if !f(mt) { 777*1c12ee1eSDan Willemsen return 778*1c12ee1eSDan Willemsen } 779*1c12ee1eSDan Willemsen } 780*1c12ee1eSDan Willemsen } 781*1c12ee1eSDan Willemsen} 782*1c12ee1eSDan Willemsen 783*1c12ee1eSDan Willemsen// NumExtensions reports the number of registered extensions. 784*1c12ee1eSDan Willemsenfunc (r *Types) NumExtensions() int { 785*1c12ee1eSDan Willemsen if r == nil { 786*1c12ee1eSDan Willemsen return 0 787*1c12ee1eSDan Willemsen } 788*1c12ee1eSDan Willemsen if r == GlobalTypes { 789*1c12ee1eSDan Willemsen globalMutex.RLock() 790*1c12ee1eSDan Willemsen defer globalMutex.RUnlock() 791*1c12ee1eSDan Willemsen } 792*1c12ee1eSDan Willemsen return r.numExtensions 793*1c12ee1eSDan Willemsen} 794*1c12ee1eSDan Willemsen 795*1c12ee1eSDan Willemsen// RangeExtensions iterates over all registered extensions while f returns true. 796*1c12ee1eSDan Willemsen// Iteration order is undefined. 797*1c12ee1eSDan Willemsenfunc (r *Types) RangeExtensions(f func(protoreflect.ExtensionType) bool) { 798*1c12ee1eSDan Willemsen if r == nil { 799*1c12ee1eSDan Willemsen return 800*1c12ee1eSDan Willemsen } 801*1c12ee1eSDan Willemsen if r == GlobalTypes { 802*1c12ee1eSDan Willemsen globalMutex.RLock() 803*1c12ee1eSDan Willemsen defer globalMutex.RUnlock() 804*1c12ee1eSDan Willemsen } 805*1c12ee1eSDan Willemsen for _, typ := range r.typesByName { 806*1c12ee1eSDan Willemsen if xt, ok := typ.(protoreflect.ExtensionType); ok { 807*1c12ee1eSDan Willemsen if !f(xt) { 808*1c12ee1eSDan Willemsen return 809*1c12ee1eSDan Willemsen } 810*1c12ee1eSDan Willemsen } 811*1c12ee1eSDan Willemsen } 812*1c12ee1eSDan Willemsen} 813*1c12ee1eSDan Willemsen 814*1c12ee1eSDan Willemsen// NumExtensionsByMessage reports the number of registered extensions for 815*1c12ee1eSDan Willemsen// a given message type. 816*1c12ee1eSDan Willemsenfunc (r *Types) NumExtensionsByMessage(message protoreflect.FullName) int { 817*1c12ee1eSDan Willemsen if r == nil { 818*1c12ee1eSDan Willemsen return 0 819*1c12ee1eSDan Willemsen } 820*1c12ee1eSDan Willemsen if r == GlobalTypes { 821*1c12ee1eSDan Willemsen globalMutex.RLock() 822*1c12ee1eSDan Willemsen defer globalMutex.RUnlock() 823*1c12ee1eSDan Willemsen } 824*1c12ee1eSDan Willemsen return len(r.extensionsByMessage[message]) 825*1c12ee1eSDan Willemsen} 826*1c12ee1eSDan Willemsen 827*1c12ee1eSDan Willemsen// RangeExtensionsByMessage iterates over all registered extensions filtered 828*1c12ee1eSDan Willemsen// by a given message type while f returns true. Iteration order is undefined. 829*1c12ee1eSDan Willemsenfunc (r *Types) RangeExtensionsByMessage(message protoreflect.FullName, f func(protoreflect.ExtensionType) bool) { 830*1c12ee1eSDan Willemsen if r == nil { 831*1c12ee1eSDan Willemsen return 832*1c12ee1eSDan Willemsen } 833*1c12ee1eSDan Willemsen if r == GlobalTypes { 834*1c12ee1eSDan Willemsen globalMutex.RLock() 835*1c12ee1eSDan Willemsen defer globalMutex.RUnlock() 836*1c12ee1eSDan Willemsen } 837*1c12ee1eSDan Willemsen for _, xt := range r.extensionsByMessage[message] { 838*1c12ee1eSDan Willemsen if !f(xt) { 839*1c12ee1eSDan Willemsen return 840*1c12ee1eSDan Willemsen } 841*1c12ee1eSDan Willemsen } 842*1c12ee1eSDan Willemsen} 843*1c12ee1eSDan Willemsen 844*1c12ee1eSDan Willemsenfunc typeName(t interface{}) string { 845*1c12ee1eSDan Willemsen switch t.(type) { 846*1c12ee1eSDan Willemsen case protoreflect.EnumType: 847*1c12ee1eSDan Willemsen return "enum" 848*1c12ee1eSDan Willemsen case protoreflect.MessageType: 849*1c12ee1eSDan Willemsen return "message" 850*1c12ee1eSDan Willemsen case protoreflect.ExtensionType: 851*1c12ee1eSDan Willemsen return "extension" 852*1c12ee1eSDan Willemsen default: 853*1c12ee1eSDan Willemsen return fmt.Sprintf("%T", t) 854*1c12ee1eSDan Willemsen } 855*1c12ee1eSDan Willemsen} 856*1c12ee1eSDan Willemsen 857*1c12ee1eSDan Willemsenfunc amendErrorWithCaller(err error, prev, curr interface{}) error { 858*1c12ee1eSDan Willemsen prevPkg := goPackage(prev) 859*1c12ee1eSDan Willemsen currPkg := goPackage(curr) 860*1c12ee1eSDan Willemsen if prevPkg == "" || currPkg == "" || prevPkg == currPkg { 861*1c12ee1eSDan Willemsen return err 862*1c12ee1eSDan Willemsen } 863*1c12ee1eSDan Willemsen return errors.New("%s\n\tpreviously from: %q\n\tcurrently from: %q", err, prevPkg, currPkg) 864*1c12ee1eSDan Willemsen} 865*1c12ee1eSDan Willemsen 866*1c12ee1eSDan Willemsenfunc goPackage(v interface{}) string { 867*1c12ee1eSDan Willemsen switch d := v.(type) { 868*1c12ee1eSDan Willemsen case protoreflect.EnumType: 869*1c12ee1eSDan Willemsen v = d.Descriptor() 870*1c12ee1eSDan Willemsen case protoreflect.MessageType: 871*1c12ee1eSDan Willemsen v = d.Descriptor() 872*1c12ee1eSDan Willemsen case protoreflect.ExtensionType: 873*1c12ee1eSDan Willemsen v = d.TypeDescriptor() 874*1c12ee1eSDan Willemsen } 875*1c12ee1eSDan Willemsen if d, ok := v.(protoreflect.Descriptor); ok { 876*1c12ee1eSDan Willemsen v = d.ParentFile() 877*1c12ee1eSDan Willemsen } 878*1c12ee1eSDan Willemsen if d, ok := v.(interface{ GoPackagePath() string }); ok { 879*1c12ee1eSDan Willemsen return d.GoPackagePath() 880*1c12ee1eSDan Willemsen } 881*1c12ee1eSDan Willemsen return "" 882*1c12ee1eSDan Willemsen} 883