github.com/bakjos/protoreflect@v1.9.2/desc/builder/file.go (about) 1 package builder 2 3 import ( 4 "fmt" 5 "sort" 6 "strings" 7 "sync/atomic" 8 9 "github.com/golang/protobuf/proto" 10 dpb "github.com/golang/protobuf/protoc-gen-go/descriptor" 11 12 "github.com/bakjos/protoreflect/desc" 13 "github.com/bakjos/protoreflect/desc/internal" 14 ) 15 16 var uniqueFileCounter uint64 17 18 func uniqueFileName() string { 19 i := atomic.AddUint64(&uniqueFileCounter, 1) 20 return fmt.Sprintf("{generated-file-%04x}.proto", i) 21 } 22 23 func makeUnique(name string, existingNames map[string]struct{}) string { 24 i := 1 25 n := name 26 for { 27 if _, ok := existingNames[n]; !ok { 28 return n 29 } 30 n = fmt.Sprintf("%s(%d)", name, i) 31 i++ 32 } 33 } 34 35 // FileBuilder is a builder used to construct a desc.FileDescriptor. This is the 36 // root of the hierarchy. All other descriptors belong to a file, and thus all 37 // other builders also belong to a file. 38 // 39 // If a builder is *not* associated with a file, the resulting descriptor will 40 // be associated with a synthesized file that contains only the built descriptor 41 // and its ancestors. This means that such descriptors will have no associated 42 // package name. 43 // 44 // To create a new FileBuilder, use NewFile. 45 type FileBuilder struct { 46 name string 47 48 IsProto3 bool 49 Package string 50 Options *dpb.FileOptions 51 52 comments Comments 53 SyntaxComments Comments 54 PackageComments Comments 55 56 messages []*MessageBuilder 57 extensions []*FieldBuilder 58 enums []*EnumBuilder 59 services []*ServiceBuilder 60 symbols map[string]Builder 61 62 explicitDeps map[*FileBuilder]struct{} 63 explicitImports map[*desc.FileDescriptor]struct{} 64 } 65 66 // NewFile creates a new FileBuilder for a file with the given name. The 67 // name can be blank, which indicates a unique name should be generated for it. 68 func NewFile(name string) *FileBuilder { 69 return &FileBuilder{ 70 name: name, 71 symbols: map[string]Builder{}, 72 } 73 } 74 75 // FromFile returns a FileBuilder that is effectively a copy of the given 76 // descriptor. Note that builders do not retain full source code info, even if 77 // the given descriptor included it. Instead, comments are extracted from the 78 // given descriptor's source info (if present) and, when built, the resulting 79 // descriptor will have just the comment info (no location information). 80 func FromFile(fd *desc.FileDescriptor) (*FileBuilder, error) { 81 fb := NewFile(fd.GetName()) 82 fb.IsProto3 = fd.IsProto3() 83 fb.Package = fd.GetPackage() 84 fb.Options = fd.GetFileOptions() 85 setComments(&fb.comments, fd.GetSourceInfo()) 86 87 // find syntax and package comments, too 88 for _, loc := range fd.AsFileDescriptorProto().GetSourceCodeInfo().GetLocation() { 89 if len(loc.Path) == 1 { 90 if loc.Path[0] == internal.File_syntaxTag { 91 setComments(&fb.SyntaxComments, loc) 92 } else if loc.Path[0] == internal.File_packageTag { 93 setComments(&fb.PackageComments, loc) 94 } 95 } 96 } 97 98 // add imports explicitly 99 for _, dep := range fd.GetDependencies() { 100 fb.AddImportedDependency(dep) 101 } 102 103 localMessages := map[*desc.MessageDescriptor]*MessageBuilder{} 104 localEnums := map[*desc.EnumDescriptor]*EnumBuilder{} 105 106 for _, md := range fd.GetMessageTypes() { 107 if mb, err := fromMessage(md, localMessages, localEnums); err != nil { 108 return nil, err 109 } else if err := fb.TryAddMessage(mb); err != nil { 110 return nil, err 111 } 112 } 113 for _, ed := range fd.GetEnumTypes() { 114 if eb, err := fromEnum(ed, localEnums); err != nil { 115 return nil, err 116 } else if err := fb.TryAddEnum(eb); err != nil { 117 return nil, err 118 } 119 } 120 for _, exd := range fd.GetExtensions() { 121 if exb, err := fromField(exd); err != nil { 122 return nil, err 123 } else if err := fb.TryAddExtension(exb); err != nil { 124 return nil, err 125 } 126 } 127 for _, sd := range fd.GetServices() { 128 if sb, err := fromService(sd); err != nil { 129 return nil, err 130 } else if err := fb.TryAddService(sb); err != nil { 131 return nil, err 132 } 133 } 134 135 // we've converted everything, so now we update all foreign type references 136 // to be local type references if possible 137 for _, mb := range fb.messages { 138 updateLocalRefsInMessage(mb, localMessages, localEnums) 139 } 140 for _, exb := range fb.extensions { 141 updateLocalRefsInField(exb, localMessages, localEnums) 142 } 143 for _, sb := range fb.services { 144 for _, mtb := range sb.methods { 145 updateLocalRefsInRpcType(mtb.ReqType, localMessages) 146 updateLocalRefsInRpcType(mtb.RespType, localMessages) 147 } 148 } 149 150 return fb, nil 151 } 152 153 func updateLocalRefsInMessage(mb *MessageBuilder, localMessages map[*desc.MessageDescriptor]*MessageBuilder, localEnums map[*desc.EnumDescriptor]*EnumBuilder) { 154 for _, b := range mb.fieldsAndOneOfs { 155 if flb, ok := b.(*FieldBuilder); ok { 156 updateLocalRefsInField(flb, localMessages, localEnums) 157 } else { 158 oob := b.(*OneOfBuilder) 159 for _, flb := range oob.choices { 160 updateLocalRefsInField(flb, localMessages, localEnums) 161 } 162 } 163 } 164 for _, nmb := range mb.nestedMessages { 165 updateLocalRefsInMessage(nmb, localMessages, localEnums) 166 } 167 for _, exb := range mb.nestedExtensions { 168 updateLocalRefsInField(exb, localMessages, localEnums) 169 } 170 } 171 172 func updateLocalRefsInField(flb *FieldBuilder, localMessages map[*desc.MessageDescriptor]*MessageBuilder, localEnums map[*desc.EnumDescriptor]*EnumBuilder) { 173 if flb.fieldType.foreignMsgType != nil { 174 if mb, ok := localMessages[flb.fieldType.foreignMsgType]; ok { 175 flb.fieldType.foreignMsgType = nil 176 flb.fieldType.localMsgType = mb 177 } 178 } 179 if flb.fieldType.foreignEnumType != nil { 180 if eb, ok := localEnums[flb.fieldType.foreignEnumType]; ok { 181 flb.fieldType.foreignEnumType = nil 182 flb.fieldType.localEnumType = eb 183 } 184 } 185 if flb.foreignExtendee != nil { 186 if mb, ok := localMessages[flb.foreignExtendee]; ok { 187 flb.foreignExtendee = nil 188 flb.localExtendee = mb 189 } 190 } 191 if flb.msgType != nil { 192 updateLocalRefsInMessage(flb.msgType, localMessages, localEnums) 193 } 194 } 195 196 func updateLocalRefsInRpcType(rpcType *RpcType, localMessages map[*desc.MessageDescriptor]*MessageBuilder) { 197 if rpcType.foreignType != nil { 198 if mb, ok := localMessages[rpcType.foreignType]; ok { 199 rpcType.foreignType = nil 200 rpcType.localType = mb 201 } 202 } 203 } 204 205 // GetName returns the name of the file. It may include relative path 206 // information, too. 207 func (fb *FileBuilder) GetName() string { 208 return fb.name 209 } 210 211 // SetName changes this file's name, returning the file builder for method 212 // chaining. 213 func (fb *FileBuilder) SetName(newName string) *FileBuilder { 214 fb.name = newName 215 return fb 216 } 217 218 // TrySetName changes this file's name. It always returns nil since renaming 219 // a file cannot fail. (It is specified to return error to satisfy the Builder 220 // interface.) 221 func (fb *FileBuilder) TrySetName(newName string) error { 222 fb.name = newName 223 return nil 224 } 225 226 // GetParent always returns nil since files are the roots of builder 227 // hierarchies. 228 func (fb *FileBuilder) GetParent() Builder { 229 return nil 230 } 231 232 func (fb *FileBuilder) setParent(parent Builder) { 233 if parent != nil { 234 panic("files cannot have parent elements") 235 } 236 } 237 238 // GetComments returns comments associated with the file itself and not any 239 // particular element therein. (Note that such a comment will not be rendered by 240 // the protoprint package.) 241 func (fb *FileBuilder) GetComments() *Comments { 242 return &fb.comments 243 } 244 245 // SetComments sets the comments associated with the file itself, not any 246 // particular element therein. (Note that such a comment will not be rendered by 247 // the protoprint package.) This method returns the file, for method chaining. 248 func (fb *FileBuilder) SetComments(c Comments) *FileBuilder { 249 fb.comments = c 250 return fb 251 } 252 253 // SetSyntaxComments sets the comments associated with the syntax declaration 254 // element (which, if present, is required to be the first element in a proto 255 // file). This method returns the file, for method chaining. 256 func (fb *FileBuilder) SetSyntaxComments(c Comments) *FileBuilder { 257 fb.SyntaxComments = c 258 return fb 259 } 260 261 // SetPackageComments sets the comments associated with the package declaration 262 // element. (This comment will not be rendered if the file's declared package is 263 // empty.) This method returns the file, for method chaining. 264 func (fb *FileBuilder) SetPackageComments(c Comments) *FileBuilder { 265 fb.PackageComments = c 266 return fb 267 } 268 269 // GetFile implements the Builder interface and always returns this file. 270 func (fb *FileBuilder) GetFile() *FileBuilder { 271 return fb 272 } 273 274 // GetChildren returns builders for all nested elements, including all top-level 275 // messages, enums, extensions, and services. 276 func (fb *FileBuilder) GetChildren() []Builder { 277 var ch []Builder 278 for _, mb := range fb.messages { 279 ch = append(ch, mb) 280 } 281 for _, exb := range fb.extensions { 282 ch = append(ch, exb) 283 } 284 for _, eb := range fb.enums { 285 ch = append(ch, eb) 286 } 287 for _, sb := range fb.services { 288 ch = append(ch, sb) 289 } 290 return ch 291 } 292 293 func (fb *FileBuilder) findChild(name string) Builder { 294 return fb.symbols[name] 295 } 296 297 func (fb *FileBuilder) removeChild(b Builder) { 298 if p, ok := b.GetParent().(*FileBuilder); !ok || p != fb { 299 return 300 } 301 302 switch b.(type) { 303 case *MessageBuilder: 304 fb.messages = deleteBuilder(b.GetName(), fb.messages).([]*MessageBuilder) 305 case *FieldBuilder: 306 fb.extensions = deleteBuilder(b.GetName(), fb.extensions).([]*FieldBuilder) 307 case *EnumBuilder: 308 fb.enums = deleteBuilder(b.GetName(), fb.enums).([]*EnumBuilder) 309 case *ServiceBuilder: 310 fb.services = deleteBuilder(b.GetName(), fb.services).([]*ServiceBuilder) 311 } 312 delete(fb.symbols, b.GetName()) 313 b.setParent(nil) 314 } 315 316 func (fb *FileBuilder) renamedChild(b Builder, oldName string) error { 317 if p, ok := b.GetParent().(*FileBuilder); !ok || p != fb { 318 return nil 319 } 320 321 if err := fb.addSymbol(b); err != nil { 322 return err 323 } 324 delete(fb.symbols, oldName) 325 return nil 326 } 327 328 func (fb *FileBuilder) addSymbol(b Builder) error { 329 if ex, ok := fb.symbols[b.GetName()]; ok { 330 return fmt.Errorf("file %q already contains element (%T) named %q", fb.GetName(), ex, b.GetName()) 331 } 332 fb.symbols[b.GetName()] = b 333 return nil 334 } 335 336 func (fb *FileBuilder) findFullyQualifiedElement(fqn string) Builder { 337 if fb.Package != "" { 338 if !strings.HasPrefix(fqn, fb.Package+".") { 339 return nil 340 } 341 fqn = fqn[len(fb.Package)+1:] 342 } 343 names := strings.Split(fqn, ".") 344 var b Builder = fb 345 for b != nil && len(names) > 0 { 346 b = b.findChild(names[0]) 347 names = names[1:] 348 } 349 return b 350 } 351 352 // GetMessage returns the top-level message with the given name. If no such 353 // message exists in the file, nil is returned. 354 func (fb *FileBuilder) GetMessage(name string) *MessageBuilder { 355 b := fb.symbols[name] 356 if mb, ok := b.(*MessageBuilder); ok { 357 return mb 358 } else { 359 return nil 360 } 361 } 362 363 // RemoveMessage removes the top-level message with the given name. If no such 364 // message exists in the file, this is a no-op. This returns the file builder, 365 // for method chaining. 366 func (fb *FileBuilder) RemoveMessage(name string) *FileBuilder { 367 fb.TryRemoveMessage(name) 368 return fb 369 } 370 371 // TryRemoveMessage removes the top-level message with the given name and 372 // returns false if the file has no such message. 373 func (fb *FileBuilder) TryRemoveMessage(name string) bool { 374 b := fb.symbols[name] 375 if mb, ok := b.(*MessageBuilder); ok { 376 fb.removeChild(mb) 377 return true 378 } 379 return false 380 } 381 382 // AddMessage adds the given message to this file. If an error prevents the 383 // message from being added, this method panics. This returns the file builder, 384 // for method chaining. 385 func (fb *FileBuilder) AddMessage(mb *MessageBuilder) *FileBuilder { 386 if err := fb.TryAddMessage(mb); err != nil { 387 panic(err) 388 } 389 return fb 390 } 391 392 // TryAddMessage adds the given message to this file, returning any error that 393 // prevents the message from being added (such as a name collision with another 394 // element already added to the file). 395 func (fb *FileBuilder) TryAddMessage(mb *MessageBuilder) error { 396 if err := fb.addSymbol(mb); err != nil { 397 return err 398 } 399 Unlink(mb) 400 mb.setParent(fb) 401 fb.messages = append(fb.messages, mb) 402 return nil 403 } 404 405 // GetExtension returns the top-level extension with the given name. If no such 406 // extension exists in the file, nil is returned. 407 func (fb *FileBuilder) GetExtension(name string) *FieldBuilder { 408 b := fb.symbols[name] 409 if exb, ok := b.(*FieldBuilder); ok { 410 return exb 411 } else { 412 return nil 413 } 414 } 415 416 // RemoveExtension removes the top-level extension with the given name. If no 417 // such extension exists in the file, this is a no-op. This returns the file 418 // builder, for method chaining. 419 func (fb *FileBuilder) RemoveExtension(name string) *FileBuilder { 420 fb.TryRemoveExtension(name) 421 return fb 422 } 423 424 // TryRemoveExtension removes the top-level extension with the given name and 425 // returns false if the file has no such extension. 426 func (fb *FileBuilder) TryRemoveExtension(name string) bool { 427 b := fb.symbols[name] 428 if exb, ok := b.(*FieldBuilder); ok { 429 fb.removeChild(exb) 430 return true 431 } 432 return false 433 } 434 435 // AddExtension adds the given extension to this file. If an error prevents the 436 // extension from being added, this method panics. This returns the file 437 // builder, for method chaining. 438 func (fb *FileBuilder) AddExtension(exb *FieldBuilder) *FileBuilder { 439 if err := fb.TryAddExtension(exb); err != nil { 440 panic(err) 441 } 442 return fb 443 } 444 445 // TryAddExtension adds the given extension to this file, returning any error 446 // that prevents the extension from being added (such as a name collision with 447 // another element already added to the file). 448 func (fb *FileBuilder) TryAddExtension(exb *FieldBuilder) error { 449 if !exb.IsExtension() { 450 return fmt.Errorf("field %s is not an extension", exb.GetName()) 451 } 452 if err := fb.addSymbol(exb); err != nil { 453 return err 454 } 455 Unlink(exb) 456 exb.setParent(fb) 457 fb.extensions = append(fb.extensions, exb) 458 return nil 459 } 460 461 // GetEnum returns the top-level enum with the given name. If no such enum 462 // exists in the file, nil is returned. 463 func (fb *FileBuilder) GetEnum(name string) *EnumBuilder { 464 b := fb.symbols[name] 465 if eb, ok := b.(*EnumBuilder); ok { 466 return eb 467 } else { 468 return nil 469 } 470 } 471 472 // RemoveEnum removes the top-level enum with the given name. If no such enum 473 // exists in the file, this is a no-op. This returns the file builder, for 474 // method chaining. 475 func (fb *FileBuilder) RemoveEnum(name string) *FileBuilder { 476 fb.TryRemoveEnum(name) 477 return fb 478 } 479 480 // TryRemoveEnum removes the top-level enum with the given name and returns 481 // false if the file has no such enum. 482 func (fb *FileBuilder) TryRemoveEnum(name string) bool { 483 b := fb.symbols[name] 484 if eb, ok := b.(*EnumBuilder); ok { 485 fb.removeChild(eb) 486 return true 487 } 488 return false 489 } 490 491 // AddEnum adds the given enum to this file. If an error prevents the enum from 492 // being added, this method panics. This returns the file builder, for method 493 // chaining. 494 func (fb *FileBuilder) AddEnum(eb *EnumBuilder) *FileBuilder { 495 if err := fb.TryAddEnum(eb); err != nil { 496 panic(err) 497 } 498 return fb 499 } 500 501 // TryAddEnum adds the given enum to this file, returning any error that 502 // prevents the enum from being added (such as a name collision with another 503 // element already added to the file). 504 func (fb *FileBuilder) TryAddEnum(eb *EnumBuilder) error { 505 if err := fb.addSymbol(eb); err != nil { 506 return err 507 } 508 Unlink(eb) 509 eb.setParent(fb) 510 fb.enums = append(fb.enums, eb) 511 return nil 512 } 513 514 // GetService returns the top-level service with the given name. If no such 515 // service exists in the file, nil is returned. 516 func (fb *FileBuilder) GetService(name string) *ServiceBuilder { 517 b := fb.symbols[name] 518 if sb, ok := b.(*ServiceBuilder); ok { 519 return sb 520 } else { 521 return nil 522 } 523 } 524 525 // RemoveService removes the top-level service with the given name. If no such 526 // service exists in the file, this is a no-op. This returns the file builder, 527 // for method chaining. 528 func (fb *FileBuilder) RemoveService(name string) *FileBuilder { 529 fb.TryRemoveService(name) 530 return fb 531 } 532 533 // TryRemoveService removes the top-level service with the given name and 534 // returns false if the file has no such service. 535 func (fb *FileBuilder) TryRemoveService(name string) bool { 536 b := fb.symbols[name] 537 if sb, ok := b.(*ServiceBuilder); ok { 538 fb.removeChild(sb) 539 return true 540 } 541 return false 542 } 543 544 // AddService adds the given service to this file. If an error prevents the 545 // service from being added, this method panics. This returns the file builder, 546 // for method chaining. 547 func (fb *FileBuilder) AddService(sb *ServiceBuilder) *FileBuilder { 548 if err := fb.TryAddService(sb); err != nil { 549 panic(err) 550 } 551 return fb 552 } 553 554 // TryAddService adds the given service to this file, returning any error that 555 // prevents the service from being added (such as a name collision with another 556 // element already added to the file). 557 func (fb *FileBuilder) TryAddService(sb *ServiceBuilder) error { 558 if err := fb.addSymbol(sb); err != nil { 559 return err 560 } 561 Unlink(sb) 562 sb.setParent(fb) 563 fb.services = append(fb.services, sb) 564 return nil 565 } 566 567 // AddDependency adds the given file as an explicit import. Normally, 568 // dependencies can be inferred during the build process by finding the files 569 // for all referenced types (such as message and enum types used in this file). 570 // However, this does not work for custom options, which must be known in order 571 // to be interpretable. And they aren't known unless an explicit import is added 572 // for the file that contains the custom options. 573 // 574 // Knowledge of custom options can also be provided by using BuildOptions with 575 // an ExtensionRegistry, when building the file. 576 func (fb *FileBuilder) AddDependency(dep *FileBuilder) *FileBuilder { 577 if fb.explicitDeps == nil { 578 fb.explicitDeps = map[*FileBuilder]struct{}{} 579 } 580 fb.explicitDeps[dep] = struct{}{} 581 return fb 582 } 583 584 // AddImportedDependency adds the given file as an explicit import. Normally, 585 // dependencies can be inferred during the build process by finding the files 586 // for all referenced types (such as message and enum types used in this file). 587 // However, this does not work for custom options, which must be known in order 588 // to be interpretable. And they aren't known unless an explicit import is added 589 // for the file that contains the custom options. 590 // 591 // Knowledge of custom options can also be provided by using BuildOptions with 592 // an ExtensionRegistry, when building the file. 593 func (fb *FileBuilder) AddImportedDependency(dep *desc.FileDescriptor) *FileBuilder { 594 if fb.explicitImports == nil { 595 fb.explicitImports = map[*desc.FileDescriptor]struct{}{} 596 } 597 fb.explicitImports[dep] = struct{}{} 598 return fb 599 } 600 601 // SetOptions sets the file options for this file and returns the file, for 602 // method chaining. 603 func (fb *FileBuilder) SetOptions(options *dpb.FileOptions) *FileBuilder { 604 fb.Options = options 605 return fb 606 } 607 608 // SetPackageName sets the name of the package for this file and returns the 609 // file, for method chaining. 610 func (fb *FileBuilder) SetPackageName(pkg string) *FileBuilder { 611 fb.Package = pkg 612 return fb 613 } 614 615 // SetProto3 sets whether this file is declared to use "proto3" syntax or not 616 // and returns the file, for method chaining. 617 func (fb *FileBuilder) SetProto3(isProto3 bool) *FileBuilder { 618 fb.IsProto3 = isProto3 619 return fb 620 } 621 622 func (fb *FileBuilder) buildProto(deps []*desc.FileDescriptor) (*dpb.FileDescriptorProto, error) { 623 name := fb.name 624 if name == "" { 625 name = uniqueFileName() 626 } 627 var syntax *string 628 if fb.IsProto3 { 629 syntax = proto.String("proto3") 630 } 631 var pkg *string 632 if fb.Package != "" { 633 pkg = proto.String(fb.Package) 634 } 635 636 path := make([]int32, 0, 10) 637 sourceInfo := dpb.SourceCodeInfo{} 638 addCommentsTo(&sourceInfo, path, &fb.comments) 639 addCommentsTo(&sourceInfo, append(path, internal.File_syntaxTag), &fb.SyntaxComments) 640 addCommentsTo(&sourceInfo, append(path, internal.File_packageTag), &fb.PackageComments) 641 642 imports := make([]string, 0, len(deps)) 643 for _, dep := range deps { 644 imports = append(imports, dep.GetName()) 645 } 646 sort.Strings(imports) 647 648 messages := make([]*dpb.DescriptorProto, 0, len(fb.messages)) 649 for _, mb := range fb.messages { 650 path := append(path, internal.File_messagesTag, int32(len(messages))) 651 if md, err := mb.buildProto(path, &sourceInfo); err != nil { 652 return nil, err 653 } else { 654 messages = append(messages, md) 655 } 656 } 657 658 enums := make([]*dpb.EnumDescriptorProto, 0, len(fb.enums)) 659 for _, eb := range fb.enums { 660 path := append(path, internal.File_enumsTag, int32(len(enums))) 661 if ed, err := eb.buildProto(path, &sourceInfo); err != nil { 662 return nil, err 663 } else { 664 enums = append(enums, ed) 665 } 666 } 667 668 extensions := make([]*dpb.FieldDescriptorProto, 0, len(fb.extensions)) 669 for _, exb := range fb.extensions { 670 path := append(path, internal.File_extensionsTag, int32(len(extensions))) 671 if exd, err := exb.buildProto(path, &sourceInfo, isExtendeeMessageSet(exb)); err != nil { 672 return nil, err 673 } else { 674 extensions = append(extensions, exd) 675 } 676 } 677 678 services := make([]*dpb.ServiceDescriptorProto, 0, len(fb.services)) 679 for _, sb := range fb.services { 680 path := append(path, internal.File_servicesTag, int32(len(services))) 681 if sd, err := sb.buildProto(path, &sourceInfo); err != nil { 682 return nil, err 683 } else { 684 services = append(services, sd) 685 } 686 } 687 688 return &dpb.FileDescriptorProto{ 689 Name: proto.String(name), 690 Package: pkg, 691 Dependency: imports, 692 Options: fb.Options, 693 Syntax: syntax, 694 MessageType: messages, 695 EnumType: enums, 696 Extension: extensions, 697 Service: services, 698 SourceCodeInfo: &sourceInfo, 699 }, nil 700 } 701 702 func isExtendeeMessageSet(flb *FieldBuilder) bool { 703 if flb.localExtendee != nil { 704 return flb.localExtendee.Options.GetMessageSetWireFormat() 705 } 706 return flb.foreignExtendee.GetMessageOptions().GetMessageSetWireFormat() 707 } 708 709 // Build constructs a file descriptor based on the contents of this file 710 // builder. If there are any problems constructing the descriptor, including 711 // resolving symbols referenced by the builder or failing to meet certain 712 // validation rules, an error is returned. 713 func (fb *FileBuilder) Build() (*desc.FileDescriptor, error) { 714 fd, err := fb.BuildDescriptor() 715 if err != nil { 716 return nil, err 717 } 718 return fd.(*desc.FileDescriptor), nil 719 } 720 721 // BuildDescriptor constructs a file descriptor based on the contents of this 722 // file builder. Most usages will prefer Build() instead, whose return type is a 723 // concrete descriptor type. This method is present to satisfy the Builder 724 // interface. 725 func (fb *FileBuilder) BuildDescriptor() (desc.Descriptor, error) { 726 return doBuild(fb, BuilderOptions{}) 727 }