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 }