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  }