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