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