github.com/hoveychen/protoreflect@v1.4.7-0.20221103114119-0b4b3385ec76/desc/builder/enum.go (about)

     1  package builder
     2  
     3  import (
     4  	"fmt"
     5  	"sort"
     6  
     7  	"github.com/golang/protobuf/proto"
     8  	dpb "github.com/golang/protobuf/protoc-gen-go/descriptor"
     9  
    10  	"github.com/hoveychen/protoreflect/desc"
    11  	"github.com/hoveychen/protoreflect/desc/internal"
    12  )
    13  
    14  // EnumBuilder is a builder used to construct a desc.EnumDescriptor.
    15  //
    16  // To create a new EnumBuilder, use NewEnum.
    17  type EnumBuilder struct {
    18  	baseBuilder
    19  
    20  	Options        *dpb.EnumOptions
    21  	ReservedRanges []*dpb.EnumDescriptorProto_EnumReservedRange
    22  	ReservedNames  []string
    23  
    24  	values  []*EnumValueBuilder
    25  	symbols map[string]*EnumValueBuilder
    26  }
    27  
    28  // NewEnum creates a new EnumBuilder for an enum with the given name. Since the
    29  // new message has no parent element, it also has no package name (e.g. it is in
    30  // the unnamed package, until it is assigned to a file builder that defines a
    31  // package name).
    32  func NewEnum(name string) *EnumBuilder {
    33  	return &EnumBuilder{
    34  		baseBuilder: baseBuilderWithName(name),
    35  		symbols:     map[string]*EnumValueBuilder{},
    36  	}
    37  }
    38  
    39  // FromEnum returns an EnumBuilder that is effectively a copy of the given
    40  // descriptor.
    41  //
    42  // Note that it is not just the given enum that is copied but its entire file.
    43  // So the caller can get the parent element of the returned builder and the
    44  // result would be a builder that is effectively a copy of the enum descriptor's
    45  // parent.
    46  //
    47  // This means that enum builders created from descriptors do not need to be
    48  // explicitly assigned to a file in order to preserve the original enum's
    49  // package name.
    50  func FromEnum(ed *desc.EnumDescriptor) (*EnumBuilder, error) {
    51  	if fb, err := FromFile(ed.GetFile()); err != nil {
    52  		return nil, err
    53  	} else if eb, ok := fb.findFullyQualifiedElement(ed.GetFullyQualifiedName()).(*EnumBuilder); ok {
    54  		return eb, nil
    55  	} else {
    56  		return nil, fmt.Errorf("could not find enum %s after converting file %q to builder", ed.GetFullyQualifiedName(), ed.GetFile().GetName())
    57  	}
    58  }
    59  
    60  func fromEnum(ed *desc.EnumDescriptor, localEnums map[*desc.EnumDescriptor]*EnumBuilder) (*EnumBuilder, error) {
    61  	eb := NewEnum(ed.GetName())
    62  	eb.Options = ed.GetEnumOptions()
    63  	eb.ReservedRanges = ed.AsEnumDescriptorProto().GetReservedRange()
    64  	eb.ReservedNames = ed.AsEnumDescriptorProto().GetReservedName()
    65  	setComments(&eb.comments, ed.GetSourceInfo())
    66  
    67  	localEnums[ed] = eb
    68  
    69  	for _, evd := range ed.GetValues() {
    70  		if evb, err := fromEnumValue(evd); err != nil {
    71  			return nil, err
    72  		} else if err := eb.TryAddValue(evb); err != nil {
    73  			return nil, err
    74  		}
    75  	}
    76  
    77  	return eb, nil
    78  }
    79  
    80  // SetName changes this enum's name, returning the enum builder for method
    81  // chaining. If the given new name is not valid (e.g. TrySetName would have
    82  // returned an error) then this method will panic.
    83  func (eb *EnumBuilder) SetName(newName string) *EnumBuilder {
    84  	if err := eb.TrySetName(newName); err != nil {
    85  		panic(err)
    86  	}
    87  	return eb
    88  }
    89  
    90  // TrySetName changes this enum's name. It will return an error if the given new
    91  // name is not a valid protobuf identifier or if the parent builder already has
    92  // an element with the given name.
    93  func (eb *EnumBuilder) TrySetName(newName string) error {
    94  	return eb.baseBuilder.setName(eb, newName)
    95  }
    96  
    97  // SetComments sets the comments associated with the enum. This method returns
    98  // the enum builder, for method chaining.
    99  func (eb *EnumBuilder) SetComments(c Comments) *EnumBuilder {
   100  	eb.comments = c
   101  	return eb
   102  }
   103  
   104  // GetChildren returns any builders assigned to this enum builder. These will be
   105  // the enum's values.
   106  func (eb *EnumBuilder) GetChildren() []Builder {
   107  	var ch []Builder
   108  	for _, evb := range eb.values {
   109  		ch = append(ch, evb)
   110  	}
   111  	return ch
   112  }
   113  
   114  func (eb *EnumBuilder) findChild(name string) Builder {
   115  	return eb.symbols[name]
   116  }
   117  
   118  func (eb *EnumBuilder) removeChild(b Builder) {
   119  	if p, ok := b.GetParent().(*EnumBuilder); !ok || p != eb {
   120  		return
   121  	}
   122  	eb.values = deleteBuilder(b.GetName(), eb.values).([]*EnumValueBuilder)
   123  	delete(eb.symbols, b.GetName())
   124  	b.setParent(nil)
   125  }
   126  
   127  func (eb *EnumBuilder) renamedChild(b Builder, oldName string) error {
   128  	if p, ok := b.GetParent().(*EnumBuilder); !ok || p != eb {
   129  		return nil
   130  	}
   131  
   132  	if err := eb.addSymbol(b.(*EnumValueBuilder)); err != nil {
   133  		return err
   134  	}
   135  	delete(eb.symbols, oldName)
   136  	return nil
   137  }
   138  
   139  func (eb *EnumBuilder) addSymbol(b *EnumValueBuilder) error {
   140  	if _, ok := eb.symbols[b.GetName()]; ok {
   141  		return fmt.Errorf("enum %s already contains value named %q", GetFullyQualifiedName(eb), b.GetName())
   142  	}
   143  	eb.symbols[b.GetName()] = b
   144  	return nil
   145  }
   146  
   147  // SetOptions sets the enum options for this enum and returns the enum, for
   148  // method chaining.
   149  func (eb *EnumBuilder) SetOptions(options *dpb.EnumOptions) *EnumBuilder {
   150  	eb.Options = options
   151  	return eb
   152  }
   153  
   154  // GetValue returns the enum value with the given name. If no such value exists
   155  // in the enum, nil is returned.
   156  func (eb *EnumBuilder) GetValue(name string) *EnumValueBuilder {
   157  	return eb.symbols[name]
   158  }
   159  
   160  // RemoveValue removes the enum value with the given name. If no such value
   161  // exists in the enum, this is a no-op. This returns the enum builder, for
   162  // method chaining.
   163  func (eb *EnumBuilder) RemoveValue(name string) *EnumBuilder {
   164  	eb.TryRemoveValue(name)
   165  	return eb
   166  }
   167  
   168  // TryRemoveValue removes the enum value with the given name and returns false
   169  // if the enum has no such value.
   170  func (eb *EnumBuilder) TryRemoveValue(name string) bool {
   171  	if evb, ok := eb.symbols[name]; ok {
   172  		eb.removeChild(evb)
   173  		return true
   174  	}
   175  	return false
   176  }
   177  
   178  // AddValue adds the given enum value to this enum. If an error prevents the
   179  // value from being added, this method panics. This returns the enum builder,
   180  // for method chaining.
   181  func (eb *EnumBuilder) AddValue(evb *EnumValueBuilder) *EnumBuilder {
   182  	if err := eb.TryAddValue(evb); err != nil {
   183  		panic(err)
   184  	}
   185  	return eb
   186  }
   187  
   188  // TryAddValue adds the given enum value to this enum, returning any error that
   189  // prevents the value from being added (such as a name collision with another
   190  // value already added to the enum).
   191  func (eb *EnumBuilder) TryAddValue(evb *EnumValueBuilder) error {
   192  	if err := eb.addSymbol(evb); err != nil {
   193  		return err
   194  	}
   195  	Unlink(evb)
   196  	evb.setParent(eb)
   197  	eb.values = append(eb.values, evb)
   198  	return nil
   199  }
   200  
   201  // AddReservedRange adds the given reserved range to this message. The range is
   202  // inclusive of both the start and end, just like defining a range in proto IDL
   203  // source. This returns the message, for method chaining.
   204  func (eb *EnumBuilder) AddReservedRange(start, end int32) *EnumBuilder {
   205  	rr := &dpb.EnumDescriptorProto_EnumReservedRange{
   206  		Start: proto.Int32(start),
   207  		End:   proto.Int32(end),
   208  	}
   209  	eb.ReservedRanges = append(eb.ReservedRanges, rr)
   210  	return eb
   211  }
   212  
   213  // SetReservedRanges replaces all of this enum's reserved ranges with the
   214  // given slice of ranges. This returns the enum, for method chaining.
   215  func (eb *EnumBuilder) SetReservedRanges(ranges []*dpb.EnumDescriptorProto_EnumReservedRange) *EnumBuilder {
   216  	eb.ReservedRanges = ranges
   217  	return eb
   218  }
   219  
   220  // AddReservedName adds the given name to the list of reserved value names for
   221  // this enum. This returns the enum, for method chaining.
   222  func (eb *EnumBuilder) AddReservedName(name string) *EnumBuilder {
   223  	eb.ReservedNames = append(eb.ReservedNames, name)
   224  	return eb
   225  }
   226  
   227  // SetReservedNames replaces all of this enum's reserved value names with the
   228  // given slice of names. This returns the enum, for method chaining.
   229  func (eb *EnumBuilder) SetReservedNames(names []string) *EnumBuilder {
   230  	eb.ReservedNames = names
   231  	return eb
   232  }
   233  
   234  func (eb *EnumBuilder) buildProto(path []int32, sourceInfo *dpb.SourceCodeInfo) (*dpb.EnumDescriptorProto, error) {
   235  	addCommentsTo(sourceInfo, path, &eb.comments)
   236  
   237  	var needNumbersAssigned []*dpb.EnumValueDescriptorProto
   238  	values := make([]*dpb.EnumValueDescriptorProto, 0, len(eb.values))
   239  	for _, evb := range eb.values {
   240  		path := append(path, internal.Enum_valuesTag, int32(len(values)))
   241  		evp, err := evb.buildProto(path, sourceInfo)
   242  		if err != nil {
   243  			return nil, err
   244  		}
   245  		values = append(values, evp)
   246  		if !evb.numberSet {
   247  			needNumbersAssigned = append(needNumbersAssigned, evp)
   248  		}
   249  	}
   250  
   251  	if len(needNumbersAssigned) > 0 {
   252  		tags := make([]int, len(values)-len(needNumbersAssigned))
   253  		for i, ev := range values {
   254  			tag := ev.GetNumber()
   255  			if tag != 0 {
   256  				tags[i] = int(tag)
   257  			}
   258  		}
   259  		sort.Ints(tags)
   260  		t := 0
   261  		ti := sort.Search(len(tags), func(i int) bool {
   262  			return tags[i] >= 0
   263  		})
   264  		if ti < len(tags) {
   265  			tags = tags[ti:]
   266  		}
   267  		for len(needNumbersAssigned) > 0 {
   268  			for len(tags) > 0 && t == tags[0] {
   269  				t++
   270  				tags = tags[1:]
   271  			}
   272  			needNumbersAssigned[0].Number = proto.Int32(int32(t))
   273  			needNumbersAssigned = needNumbersAssigned[1:]
   274  			t++
   275  		}
   276  	}
   277  
   278  	return &dpb.EnumDescriptorProto{
   279  		Name:          proto.String(eb.name),
   280  		Options:       eb.Options,
   281  		Value:         values,
   282  		ReservedRange: eb.ReservedRanges,
   283  		ReservedName:  eb.ReservedNames,
   284  	}, nil
   285  }
   286  
   287  // Build constructs an enum descriptor based on the contents of this enum
   288  // builder. If there are any problems constructing the descriptor, including
   289  // resolving symbols referenced by the builder or failing to meet certain
   290  // validation rules, an error is returned.
   291  func (eb *EnumBuilder) Build() (*desc.EnumDescriptor, error) {
   292  	ed, err := eb.BuildDescriptor()
   293  	if err != nil {
   294  		return nil, err
   295  	}
   296  	return ed.(*desc.EnumDescriptor), nil
   297  }
   298  
   299  // BuildDescriptor constructs an enum descriptor based on the contents of this
   300  // enum builder. Most usages will prefer Build() instead, whose return type
   301  // is a concrete descriptor type. This method is present to satisfy the Builder
   302  // interface.
   303  func (eb *EnumBuilder) BuildDescriptor() (desc.Descriptor, error) {
   304  	return doBuild(eb, BuilderOptions{})
   305  }
   306  
   307  // EnumValueBuilder is a builder used to construct a desc.EnumValueDescriptor.
   308  // A enum value builder *must* be added to an enum before calling its Build()
   309  // method.
   310  //
   311  // To create a new EnumValueBuilder, use NewEnumValue.
   312  type EnumValueBuilder struct {
   313  	baseBuilder
   314  
   315  	number    int32
   316  	numberSet bool
   317  	Options   *dpb.EnumValueOptions
   318  }
   319  
   320  // NewEnumValue creates a new EnumValueBuilder for an enum value with the given
   321  // name. The return value's numeric value will not be set, which means it will
   322  // be auto-assigned when the descriptor is built, unless explicitly set with a
   323  // call to SetNumber.
   324  func NewEnumValue(name string) *EnumValueBuilder {
   325  	return &EnumValueBuilder{baseBuilder: baseBuilderWithName(name)}
   326  }
   327  
   328  // FromEnumValue returns an EnumValueBuilder that is effectively a copy of the
   329  // given descriptor.
   330  //
   331  // Note that it is not just the given enum value that is copied but its entire
   332  // file. So the caller can get the parent element of the returned builder and
   333  // the result would be a builder that is effectively a copy of the enum value
   334  // descriptor's parent enum.
   335  //
   336  // This means that enum value builders created from descriptors do not need to
   337  // be explicitly assigned to a file in order to preserve the original enum
   338  // value's package name.
   339  func FromEnumValue(evd *desc.EnumValueDescriptor) (*EnumValueBuilder, error) {
   340  	if fb, err := FromFile(evd.GetFile()); err != nil {
   341  		return nil, err
   342  	} else if evb, ok := fb.findFullyQualifiedElement(evd.GetFullyQualifiedName()).(*EnumValueBuilder); ok {
   343  		return evb, nil
   344  	} else {
   345  		return nil, fmt.Errorf("could not find enum value %s after converting file %q to builder", evd.GetFullyQualifiedName(), evd.GetFile().GetName())
   346  	}
   347  }
   348  
   349  func fromEnumValue(evd *desc.EnumValueDescriptor) (*EnumValueBuilder, error) {
   350  	evb := NewEnumValue(evd.GetName())
   351  	evb.Options = evd.GetEnumValueOptions()
   352  	evb.number = evd.GetNumber()
   353  	evb.numberSet = true
   354  	setComments(&evb.comments, evd.GetSourceInfo())
   355  
   356  	return evb, nil
   357  }
   358  
   359  // SetName changes this enum value's name, returning the enum value builder for
   360  // method chaining. If the given new name is not valid (e.g. TrySetName would
   361  // have returned an error) then this method will panic.
   362  func (evb *EnumValueBuilder) SetName(newName string) *EnumValueBuilder {
   363  	if err := evb.TrySetName(newName); err != nil {
   364  		panic(err)
   365  	}
   366  	return evb
   367  }
   368  
   369  // TrySetName changes this enum value's name. It will return an error if the
   370  // given new name is not a valid protobuf identifier or if the parent enum
   371  // builder already has an enum value with the given name.
   372  func (evb *EnumValueBuilder) TrySetName(newName string) error {
   373  	return evb.baseBuilder.setName(evb, newName)
   374  }
   375  
   376  // SetComments sets the comments associated with the enum value. This method
   377  // returns the enum value builder, for method chaining.
   378  func (evb *EnumValueBuilder) SetComments(c Comments) *EnumValueBuilder {
   379  	evb.comments = c
   380  	return evb
   381  }
   382  
   383  // GetChildren returns nil, since enum values cannot have child elements. It is
   384  // present to satisfy the Builder interface.
   385  func (evb *EnumValueBuilder) GetChildren() []Builder {
   386  	// enum values do not have children
   387  	return nil
   388  }
   389  
   390  func (evb *EnumValueBuilder) findChild(name string) Builder {
   391  	// enum values do not have children
   392  	return nil
   393  }
   394  
   395  func (evb *EnumValueBuilder) removeChild(b Builder) {
   396  	// enum values do not have children
   397  }
   398  
   399  func (evb *EnumValueBuilder) renamedChild(b Builder, oldName string) error {
   400  	// enum values do not have children
   401  	return nil
   402  }
   403  
   404  // SetOptions sets the enum value options for this enum value and returns the
   405  // enum value, for method chaining.
   406  func (evb *EnumValueBuilder) SetOptions(options *dpb.EnumValueOptions) *EnumValueBuilder {
   407  	evb.Options = options
   408  	return evb
   409  }
   410  
   411  // GetNumber returns the enum value's numeric value. If the number has not been
   412  // set this returns zero.
   413  func (evb *EnumValueBuilder) GetNumber() int32 {
   414  	return evb.number
   415  }
   416  
   417  // HasNumber returns whether or not the enum value's numeric value has been set.
   418  // If it has not been set, it is auto-assigned when the descriptor is built.
   419  func (evb *EnumValueBuilder) HasNumber() bool {
   420  	return evb.numberSet
   421  }
   422  
   423  // ClearNumber clears this enum value's numeric value and then returns the enum
   424  // value builder, for method chaining. After being cleared, the number will be
   425  // auto-assigned when the descriptor is built, unless explicitly set by a
   426  // subsequent call to SetNumber.
   427  func (evb *EnumValueBuilder) ClearNumber() *EnumValueBuilder {
   428  	evb.number = 0
   429  	evb.numberSet = false
   430  	return evb
   431  }
   432  
   433  // SetNumber changes the numeric value for this enum value and then returns the
   434  // enum value, for method chaining.
   435  func (evb *EnumValueBuilder) SetNumber(number int32) *EnumValueBuilder {
   436  	evb.number = number
   437  	evb.numberSet = true
   438  	return evb
   439  }
   440  
   441  func (evb *EnumValueBuilder) buildProto(path []int32, sourceInfo *dpb.SourceCodeInfo) (*dpb.EnumValueDescriptorProto, error) {
   442  	addCommentsTo(sourceInfo, path, &evb.comments)
   443  
   444  	return &dpb.EnumValueDescriptorProto{
   445  		Name:    proto.String(evb.name),
   446  		Number:  proto.Int32(evb.number),
   447  		Options: evb.Options,
   448  	}, nil
   449  }
   450  
   451  // Build constructs an enum value descriptor based on the contents of this enum
   452  // value builder. If there are any problems constructing the descriptor,
   453  // including resolving symbols referenced by the builder or failing to meet
   454  // certain validation rules, an error is returned.
   455  func (evb *EnumValueBuilder) Build() (*desc.EnumValueDescriptor, error) {
   456  	evd, err := evb.BuildDescriptor()
   457  	if err != nil {
   458  		return nil, err
   459  	}
   460  	return evd.(*desc.EnumValueDescriptor), nil
   461  }
   462  
   463  // BuildDescriptor constructs an enum value descriptor based on the contents of
   464  // this enum value builder. Most usages will prefer Build() instead, whose
   465  // return type is a concrete descriptor type. This method is present to satisfy
   466  // the Builder interface.
   467  func (evb *EnumValueBuilder) BuildDescriptor() (desc.Descriptor, error) {
   468  	return doBuild(evb, BuilderOptions{})
   469  }