1// 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} 801