github.com/syumai/protoreflect@v1.7.1-0.20200810020253-2ac7e3b3a321/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/syumai/protoreflect/desc" 13 "github.com/syumai/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 var bs []Builder 397 if mb.GetParent() != nil { 398 bs = fb.resolveTypeBuilders(mb) 399 } else { 400 bs = []Builder{mb} 401 } 402 for _, b := range bs { 403 if err := fb.addSymbol(b); err != nil && b != mb { 404 fb.RemoveMessage(b.GetName()) 405 fb.addSymbol(b) 406 } 407 Unlink(b) 408 b.setParent(fb) 409 switch b := b.(type) { 410 case *MessageBuilder: 411 fb.messages = append(fb.messages, b) 412 case *EnumBuilder: 413 fb.enums = append(fb.enums, b) 414 } 415 } 416 return nil 417 } 418 419 func (fb *FileBuilder) resolveTypeBuilders(mb *MessageBuilder) []Builder { 420 rootParentName := mb.GetParent().GetName() 421 fmt.Println(mb.GetName(), rootParentName) 422 bs := []Builder{mb} 423 for _, b := range mb.symbols { 424 switch flb := b.(type) { 425 case *FieldBuilder: 426 if msgType := flb.fieldType.localMsgType; msgType != nil && 427 msgType.GetParent() != nil && msgType.GetParent().GetName() == rootParentName { 428 bs = append(bs, fb.resolveTypeBuilders(msgType)...) 429 continue 430 } 431 if enumType := flb.fieldType.localEnumType; enumType != nil && 432 enumType.GetParent() != nil && enumType.GetParent().GetName() == rootParentName { 433 bs = append(bs, enumType) 434 continue 435 } 436 } 437 } 438 return bs 439 } 440 441 // GetExtension returns the top-level extension with the given name. If no such 442 // extension exists in the file, nil is returned. 443 func (fb *FileBuilder) GetExtension(name string) *FieldBuilder { 444 b := fb.symbols[name] 445 if exb, ok := b.(*FieldBuilder); ok { 446 return exb 447 } else { 448 return nil 449 } 450 } 451 452 // RemoveExtension removes the top-level extension with the given name. If no 453 // such extension exists in the file, this is a no-op. This returns the file 454 // builder, for method chaining. 455 func (fb *FileBuilder) RemoveExtension(name string) *FileBuilder { 456 fb.TryRemoveExtension(name) 457 return fb 458 } 459 460 // TryRemoveExtension removes the top-level extension with the given name and 461 // returns false if the file has no such extension. 462 func (fb *FileBuilder) TryRemoveExtension(name string) bool { 463 b := fb.symbols[name] 464 if exb, ok := b.(*FieldBuilder); ok { 465 fb.removeChild(exb) 466 return true 467 } 468 return false 469 } 470 471 // AddExtension adds the given extension to this file. If an error prevents the 472 // extension from being added, this method panics. This returns the file 473 // builder, for method chaining. 474 func (fb *FileBuilder) AddExtension(exb *FieldBuilder) *FileBuilder { 475 if err := fb.TryAddExtension(exb); err != nil { 476 panic(err) 477 } 478 return fb 479 } 480 481 // TryAddExtension adds the given extension to this file, returning any error 482 // that prevents the extension from being added (such as a name collision with 483 // another element already added to the file). 484 func (fb *FileBuilder) TryAddExtension(exb *FieldBuilder) error { 485 if !exb.IsExtension() { 486 return fmt.Errorf("field %s is not an extension", exb.GetName()) 487 } 488 if err := fb.addSymbol(exb); err != nil { 489 return err 490 } 491 Unlink(exb) 492 exb.setParent(fb) 493 fb.extensions = append(fb.extensions, exb) 494 return nil 495 } 496 497 // GetEnum returns the top-level enum with the given name. If no such enum 498 // exists in the file, nil is returned. 499 func (fb *FileBuilder) GetEnum(name string) *EnumBuilder { 500 b := fb.symbols[name] 501 if eb, ok := b.(*EnumBuilder); ok { 502 return eb 503 } else { 504 return nil 505 } 506 } 507 508 // RemoveEnum removes the top-level enum with the given name. If no such enum 509 // exists in the file, this is a no-op. This returns the file builder, for 510 // method chaining. 511 func (fb *FileBuilder) RemoveEnum(name string) *FileBuilder { 512 fb.TryRemoveEnum(name) 513 return fb 514 } 515 516 // TryRemoveEnum removes the top-level enum with the given name and returns 517 // false if the file has no such enum. 518 func (fb *FileBuilder) TryRemoveEnum(name string) bool { 519 b := fb.symbols[name] 520 if eb, ok := b.(*EnumBuilder); ok { 521 fb.removeChild(eb) 522 return true 523 } 524 return false 525 } 526 527 // AddEnum adds the given enum to this file. If an error prevents the enum from 528 // being added, this method panics. This returns the file builder, for method 529 // chaining. 530 func (fb *FileBuilder) AddEnum(eb *EnumBuilder) *FileBuilder { 531 if err := fb.TryAddEnum(eb); err != nil { 532 panic(err) 533 } 534 return fb 535 } 536 537 // TryAddEnum adds the given enum to this file, returning any error that 538 // prevents the enum from being added (such as a name collision with another 539 // element already added to the file). 540 func (fb *FileBuilder) TryAddEnum(eb *EnumBuilder) error { 541 if err := fb.addSymbol(eb); err != nil { 542 return err 543 } 544 Unlink(eb) 545 eb.setParent(fb) 546 fb.enums = append(fb.enums, eb) 547 return nil 548 } 549 550 // GetService returns the top-level service with the given name. If no such 551 // service exists in the file, nil is returned. 552 func (fb *FileBuilder) GetService(name string) *ServiceBuilder { 553 b := fb.symbols[name] 554 if sb, ok := b.(*ServiceBuilder); ok { 555 return sb 556 } else { 557 return nil 558 } 559 } 560 561 // RemoveService removes the top-level service with the given name. If no such 562 // service exists in the file, this is a no-op. This returns the file builder, 563 // for method chaining. 564 func (fb *FileBuilder) RemoveService(name string) *FileBuilder { 565 fb.TryRemoveService(name) 566 return fb 567 } 568 569 // TryRemoveService removes the top-level service with the given name and 570 // returns false if the file has no such service. 571 func (fb *FileBuilder) TryRemoveService(name string) bool { 572 b := fb.symbols[name] 573 if sb, ok := b.(*ServiceBuilder); ok { 574 fb.removeChild(sb) 575 return true 576 } 577 return false 578 } 579 580 // AddService adds the given service to this file. If an error prevents the 581 // service from being added, this method panics. This returns the file builder, 582 // for method chaining. 583 func (fb *FileBuilder) AddService(sb *ServiceBuilder) *FileBuilder { 584 if err := fb.TryAddService(sb); err != nil { 585 panic(err) 586 } 587 return fb 588 } 589 590 // TryAddService adds the given service to this file, returning any error that 591 // prevents the service from being added (such as a name collision with another 592 // element already added to the file). 593 func (fb *FileBuilder) TryAddService(sb *ServiceBuilder) error { 594 if err := fb.addSymbol(sb); err != nil { 595 return err 596 } 597 Unlink(sb) 598 sb.setParent(fb) 599 fb.services = append(fb.services, sb) 600 return nil 601 } 602 603 // AddDependency adds the given file as an explicit import. Normally, 604 // dependencies can be inferred during the build process by finding the files 605 // for all referenced types (such as message and enum types used in this file). 606 // However, this does not work for custom options, which must be known in order 607 // to be interpretable. And they aren't known unless an explicit import is added 608 // for the file that contains the custom options. 609 // 610 // Knowledge of custom options can also be provided by using BuildOptions with 611 // an ExtensionRegistry, when building the file. 612 func (fb *FileBuilder) AddDependency(dep *FileBuilder) *FileBuilder { 613 if fb.explicitDeps == nil { 614 fb.explicitDeps = map[*FileBuilder]struct{}{} 615 } 616 fb.explicitDeps[dep] = struct{}{} 617 return fb 618 } 619 620 // AddImportedDependency adds the given file as an explicit import. Normally, 621 // dependencies can be inferred during the build process by finding the files 622 // for all referenced types (such as message and enum types used in this file). 623 // However, this does not work for custom options, which must be known in order 624 // to be interpretable. And they aren't known unless an explicit import is added 625 // for the file that contains the custom options. 626 // 627 // Knowledge of custom options can also be provided by using BuildOptions with 628 // an ExtensionRegistry, when building the file. 629 func (fb *FileBuilder) AddImportedDependency(dep *desc.FileDescriptor) *FileBuilder { 630 if fb.explicitImports == nil { 631 fb.explicitImports = map[*desc.FileDescriptor]struct{}{} 632 } 633 fb.explicitImports[dep] = struct{}{} 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 *dpb.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) (*dpb.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 := dpb.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([]*dpb.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([]*dpb.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([]*dpb.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([]*dpb.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 &dpb.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 }