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  }