1package builder 2 3import ( 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/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. 17type 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). 32func 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. 50func 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 60func 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. 83func (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. 93func (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. 99func (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. 106func (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 114func (eb *EnumBuilder) findChild(name string) Builder { 115 return eb.symbols[name] 116} 117 118func (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 127func (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 139func (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. 149func (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. 156func (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. 163func (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. 170func (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. 181func (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). 191func (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. 204func (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. 215func (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. 222func (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. 229func (eb *EnumBuilder) SetReservedNames(names []string) *EnumBuilder { 230 eb.ReservedNames = names 231 return eb 232} 233 234func (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. 291func (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. 303func (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. 312type 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. 324func 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. 339func 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 349func 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. 362func (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. 372func (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. 378func (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. 385func (evb *EnumValueBuilder) GetChildren() []Builder { 386 // enum values do not have children 387 return nil 388} 389 390func (evb *EnumValueBuilder) findChild(name string) Builder { 391 // enum values do not have children 392 return nil 393} 394 395func (evb *EnumValueBuilder) removeChild(b Builder) { 396 // enum values do not have children 397} 398 399func (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. 406func (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. 413func (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. 419func (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. 427func (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. 435func (evb *EnumValueBuilder) SetNumber(number int32) *EnumValueBuilder { 436 evb.number = number 437 evb.numberSet = true 438 return evb 439} 440 441func (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. 455func (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. 467func (evb *EnumValueBuilder) BuildDescriptor() (desc.Descriptor, error) { 468 return doBuild(evb, BuilderOptions{}) 469} 470