github.com/jhump/protoreflect@v1.16.0/desc/builder/enum.go (about)

     1  package builder
     2  
     3  import (
     4  	"fmt"
     5  	"sort"
     6  
     7  	"google.golang.org/protobuf/proto"
     8  	"google.golang.org/protobuf/types/descriptorpb"
     9  
    10  	"github.com/jhump/protoreflect/desc"
    11  	"github.com/jhump/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        *descriptorpb.EnumOptions
    21  	ReservedRanges []*descriptorpb.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 *descriptorpb.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 := &descriptorpb.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 []*descriptorpb.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 *descriptorpb.SourceCodeInfo) (*descriptorpb.EnumDescriptorProto, error) {
   235  	addCommentsTo(sourceInfo, path, &eb.comments)
   236  
   237  	var needNumbersAssigned []*descriptorpb.EnumValueDescriptorProto
   238  	values := make([]*descriptorpb.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  		tagsIndex := 0
   254  		for _, evb := range eb.values {
   255  			if evb.numberSet {
   256  				tags[tagsIndex] = int(evb.GetNumber())
   257  				tagsIndex++
   258  			}
   259  		}
   260  		sort.Ints(tags)
   261  		t := 0
   262  		ti := sort.Search(len(tags), func(i int) bool {
   263  			return tags[i] >= 0
   264  		})
   265  		if ti < len(tags) {
   266  			tags = tags[ti:]
   267  		}
   268  		for len(needNumbersAssigned) > 0 {
   269  			for len(tags) > 0 && t == tags[0] {
   270  				t++
   271  				tags = tags[1:]
   272  			}
   273  			needNumbersAssigned[0].Number = proto.Int32(int32(t))
   274  			needNumbersAssigned = needNumbersAssigned[1:]
   275  			t++
   276  		}
   277  	}
   278  
   279  	return &descriptorpb.EnumDescriptorProto{
   280  		Name:          proto.String(eb.name),
   281  		Options:       eb.Options,
   282  		Value:         values,
   283  		ReservedRange: eb.ReservedRanges,
   284  		ReservedName:  eb.ReservedNames,
   285  	}, nil
   286  }
   287  
   288  // Build constructs an enum descriptor based on the contents of this enum
   289  // builder. If there are any problems constructing the descriptor, including
   290  // resolving symbols referenced by the builder or failing to meet certain
   291  // validation rules, an error is returned.
   292  func (eb *EnumBuilder) Build() (*desc.EnumDescriptor, error) {
   293  	ed, err := eb.BuildDescriptor()
   294  	if err != nil {
   295  		return nil, err
   296  	}
   297  	return ed.(*desc.EnumDescriptor), nil
   298  }
   299  
   300  // BuildDescriptor constructs an enum descriptor based on the contents of this
   301  // enum builder. Most usages will prefer Build() instead, whose return type
   302  // is a concrete descriptor type. This method is present to satisfy the Builder
   303  // interface.
   304  func (eb *EnumBuilder) BuildDescriptor() (desc.Descriptor, error) {
   305  	return doBuild(eb, BuilderOptions{})
   306  }
   307  
   308  // EnumValueBuilder is a builder used to construct a desc.EnumValueDescriptor.
   309  // A enum value builder *must* be added to an enum before calling its Build()
   310  // method.
   311  //
   312  // To create a new EnumValueBuilder, use NewEnumValue.
   313  type EnumValueBuilder struct {
   314  	baseBuilder
   315  
   316  	number    int32
   317  	numberSet bool
   318  	Options   *descriptorpb.EnumValueOptions
   319  }
   320  
   321  // NewEnumValue creates a new EnumValueBuilder for an enum value with the given
   322  // name. The return value's numeric value will not be set, which means it will
   323  // be auto-assigned when the descriptor is built, unless explicitly set with a
   324  // call to SetNumber.
   325  func NewEnumValue(name string) *EnumValueBuilder {
   326  	return &EnumValueBuilder{baseBuilder: baseBuilderWithName(name)}
   327  }
   328  
   329  // FromEnumValue returns an EnumValueBuilder that is effectively a copy of the
   330  // given descriptor.
   331  //
   332  // Note that it is not just the given enum value that is copied but its entire
   333  // file. So the caller can get the parent element of the returned builder and
   334  // the result would be a builder that is effectively a copy of the enum value
   335  // descriptor's parent enum.
   336  //
   337  // This means that enum value builders created from descriptors do not need to
   338  // be explicitly assigned to a file in order to preserve the original enum
   339  // value's package name.
   340  func FromEnumValue(evd *desc.EnumValueDescriptor) (*EnumValueBuilder, error) {
   341  	if fb, err := FromFile(evd.GetFile()); err != nil {
   342  		return nil, err
   343  	} else if evb, ok := fb.findFullyQualifiedElement(evd.GetFullyQualifiedName()).(*EnumValueBuilder); ok {
   344  		return evb, nil
   345  	} else {
   346  		return nil, fmt.Errorf("could not find enum value %s after converting file %q to builder", evd.GetFullyQualifiedName(), evd.GetFile().GetName())
   347  	}
   348  }
   349  
   350  func fromEnumValue(evd *desc.EnumValueDescriptor) (*EnumValueBuilder, error) {
   351  	evb := NewEnumValue(evd.GetName())
   352  	evb.Options = evd.GetEnumValueOptions()
   353  	evb.number = evd.GetNumber()
   354  	evb.numberSet = true
   355  	setComments(&evb.comments, evd.GetSourceInfo())
   356  
   357  	return evb, nil
   358  }
   359  
   360  // SetName changes this enum value's name, returning the enum value builder for
   361  // method chaining. If the given new name is not valid (e.g. TrySetName would
   362  // have returned an error) then this method will panic.
   363  func (evb *EnumValueBuilder) SetName(newName string) *EnumValueBuilder {
   364  	if err := evb.TrySetName(newName); err != nil {
   365  		panic(err)
   366  	}
   367  	return evb
   368  }
   369  
   370  // TrySetName changes this enum value's name. It will return an error if the
   371  // given new name is not a valid protobuf identifier or if the parent enum
   372  // builder already has an enum value with the given name.
   373  func (evb *EnumValueBuilder) TrySetName(newName string) error {
   374  	return evb.baseBuilder.setName(evb, newName)
   375  }
   376  
   377  // SetComments sets the comments associated with the enum value. This method
   378  // returns the enum value builder, for method chaining.
   379  func (evb *EnumValueBuilder) SetComments(c Comments) *EnumValueBuilder {
   380  	evb.comments = c
   381  	return evb
   382  }
   383  
   384  // GetChildren returns nil, since enum values cannot have child elements. It is
   385  // present to satisfy the Builder interface.
   386  func (evb *EnumValueBuilder) GetChildren() []Builder {
   387  	// enum values do not have children
   388  	return nil
   389  }
   390  
   391  func (evb *EnumValueBuilder) findChild(name string) Builder {
   392  	// enum values do not have children
   393  	return nil
   394  }
   395  
   396  func (evb *EnumValueBuilder) removeChild(b Builder) {
   397  	// enum values do not have children
   398  }
   399  
   400  func (evb *EnumValueBuilder) renamedChild(b Builder, oldName string) error {
   401  	// enum values do not have children
   402  	return nil
   403  }
   404  
   405  // SetOptions sets the enum value options for this enum value and returns the
   406  // enum value, for method chaining.
   407  func (evb *EnumValueBuilder) SetOptions(options *descriptorpb.EnumValueOptions) *EnumValueBuilder {
   408  	evb.Options = options
   409  	return evb
   410  }
   411  
   412  // GetNumber returns the enum value's numeric value. If the number has not been
   413  // set this returns zero.
   414  func (evb *EnumValueBuilder) GetNumber() int32 {
   415  	return evb.number
   416  }
   417  
   418  // HasNumber returns whether or not the enum value's numeric value has been set.
   419  // If it has not been set, it is auto-assigned when the descriptor is built.
   420  func (evb *EnumValueBuilder) HasNumber() bool {
   421  	return evb.numberSet
   422  }
   423  
   424  // ClearNumber clears this enum value's numeric value and then returns the enum
   425  // value builder, for method chaining. After being cleared, the number will be
   426  // auto-assigned when the descriptor is built, unless explicitly set by a
   427  // subsequent call to SetNumber.
   428  func (evb *EnumValueBuilder) ClearNumber() *EnumValueBuilder {
   429  	evb.number = 0
   430  	evb.numberSet = false
   431  	return evb
   432  }
   433  
   434  // SetNumber changes the numeric value for this enum value and then returns the
   435  // enum value, for method chaining.
   436  func (evb *EnumValueBuilder) SetNumber(number int32) *EnumValueBuilder {
   437  	evb.number = number
   438  	evb.numberSet = true
   439  	return evb
   440  }
   441  
   442  func (evb *EnumValueBuilder) buildProto(path []int32, sourceInfo *descriptorpb.SourceCodeInfo) (*descriptorpb.EnumValueDescriptorProto, error) {
   443  	addCommentsTo(sourceInfo, path, &evb.comments)
   444  
   445  	return &descriptorpb.EnumValueDescriptorProto{
   446  		Name:    proto.String(evb.name),
   447  		Number:  proto.Int32(evb.number),
   448  		Options: evb.Options,
   449  	}, nil
   450  }
   451  
   452  // Build constructs an enum value descriptor based on the contents of this enum
   453  // value builder. If there are any problems constructing the descriptor,
   454  // including resolving symbols referenced by the builder or failing to meet
   455  // certain validation rules, an error is returned.
   456  func (evb *EnumValueBuilder) Build() (*desc.EnumValueDescriptor, error) {
   457  	evd, err := evb.BuildDescriptor()
   458  	if err != nil {
   459  		return nil, err
   460  	}
   461  	return evd.(*desc.EnumValueDescriptor), nil
   462  }
   463  
   464  // BuildDescriptor constructs an enum value descriptor based on the contents of
   465  // this enum value builder. Most usages will prefer Build() instead, whose
   466  // return type is a concrete descriptor type. This method is present to satisfy
   467  // the Builder interface.
   468  func (evb *EnumValueBuilder) BuildDescriptor() (desc.Descriptor, error) {
   469  	return doBuild(evb, BuilderOptions{})
   470  }