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