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 }