github.com/jhump/protoreflect@v1.16.0/desc/builder/field.go (about) 1 package builder 2 3 import ( 4 "fmt" 5 "strings" 6 "unicode" 7 8 "google.golang.org/protobuf/proto" 9 "google.golang.org/protobuf/types/descriptorpb" 10 11 "github.com/jhump/protoreflect/desc" 12 "github.com/jhump/protoreflect/desc/internal" 13 ) 14 15 // FieldBuilder is a builder used to construct a desc.FieldDescriptor. A field 16 // builder is used to create fields and extensions as well as map entry 17 // messages. It is also used to link groups (defined via a message builder) into 18 // an enclosing message, associating it with a group field. A non-extension 19 // field builder *must* be added to a message before calling its Build() method. 20 // 21 // To create a new FieldBuilder, use NewField, NewMapField, NewGroupField, 22 // NewExtension, or NewExtensionImported (depending on the type of field being 23 // built). 24 type FieldBuilder struct { 25 baseBuilder 26 number int32 27 28 // msgType is populated for fields that have a "private" message type that 29 // isn't expected to be referenced elsewhere. This happens for map fields, 30 // where the private message type represents the map entry, and for group 31 // fields. 32 msgType *MessageBuilder 33 fieldType *FieldType 34 35 Options *descriptorpb.FieldOptions 36 Label descriptorpb.FieldDescriptorProto_Label 37 38 // Proto3Optional indicates if the field is a proto3 optional field. This 39 // only applies to fields in files with "proto3" syntax whose Cardinality 40 // is set to protoreflect.Optional. 41 // 42 // If the file's syntax is not "proto3", this may not be set to true. 43 // 44 // This allows setting a field in a proto3 file to have explicit field 45 // presence. To manage field presence for fields in files that use 46 // "editions", set the field_presence field of the features option in 47 // Options. 48 Proto3Optional bool 49 50 Default string 51 JsonName string 52 53 foreignExtendee *desc.MessageDescriptor 54 localExtendee *MessageBuilder 55 } 56 57 // NewField creates a new FieldBuilder for a non-extension field with the given 58 // name and type. To create a map or group field, see NewMapField or 59 // NewGroupField respectively. 60 // 61 // The new field will be optional. See SetLabel, SetRepeated, and SetRequired 62 // for changing this aspect of the field. The new field's tag will be zero, 63 // which means it will be auto-assigned when the descriptor is built. Use 64 // SetNumber or TrySetNumber to assign an explicit tag number. 65 func NewField(name string, typ *FieldType) *FieldBuilder { 66 flb := &FieldBuilder{ 67 baseBuilder: baseBuilderWithName(name), 68 fieldType: typ, 69 } 70 return flb 71 } 72 73 // NewMapField creates a new FieldBuilder for a non-extension field with the 74 // given name and whose type is a map of the given key and value types. Map keys 75 // can be any of the scalar integer types, booleans, or strings. If any other 76 // type is specified, this function will panic. Map values cannot be groups: if 77 // a group type is specified, this function will panic. 78 // 79 // When this field is added to a message, the associated map entry message type 80 // will also be added. 81 // 82 // The new field's tag will be zero, which means it will be auto-assigned when 83 // the descriptor is built. Use SetNumber or TrySetNumber to assign an explicit 84 // tag number. 85 func NewMapField(name string, keyTyp, valTyp *FieldType) *FieldBuilder { 86 switch keyTyp.fieldType { 87 case descriptorpb.FieldDescriptorProto_TYPE_BOOL, 88 descriptorpb.FieldDescriptorProto_TYPE_STRING, 89 descriptorpb.FieldDescriptorProto_TYPE_INT32, descriptorpb.FieldDescriptorProto_TYPE_INT64, 90 descriptorpb.FieldDescriptorProto_TYPE_SINT32, descriptorpb.FieldDescriptorProto_TYPE_SINT64, 91 descriptorpb.FieldDescriptorProto_TYPE_UINT32, descriptorpb.FieldDescriptorProto_TYPE_UINT64, 92 descriptorpb.FieldDescriptorProto_TYPE_FIXED32, descriptorpb.FieldDescriptorProto_TYPE_FIXED64, 93 descriptorpb.FieldDescriptorProto_TYPE_SFIXED32, descriptorpb.FieldDescriptorProto_TYPE_SFIXED64: 94 // allowed 95 default: 96 panic(fmt.Sprintf("Map types cannot have keys of type %v", keyTyp.fieldType)) 97 } 98 if valTyp.fieldType == descriptorpb.FieldDescriptorProto_TYPE_GROUP { 99 panic(fmt.Sprintf("Map types cannot have values of type %v", valTyp.fieldType)) 100 } 101 entryMsg := NewMessage(entryTypeName(name)) 102 keyFlb := NewField("key", keyTyp) 103 keyFlb.number = 1 104 valFlb := NewField("value", valTyp) 105 valFlb.number = 2 106 entryMsg.AddField(keyFlb) 107 entryMsg.AddField(valFlb) 108 entryMsg.Options = &descriptorpb.MessageOptions{MapEntry: proto.Bool(true)} 109 110 flb := NewField(name, FieldTypeMessage(entryMsg)). 111 SetLabel(descriptorpb.FieldDescriptorProto_LABEL_REPEATED) 112 flb.msgType = entryMsg 113 entryMsg.setParent(flb) 114 return flb 115 } 116 117 // NewGroupField creates a new FieldBuilder for a non-extension field whose type 118 // is a group with the given definition. The given message's name must start 119 // with a capital letter, and the resulting field will have the same name but 120 // converted to all lower-case. If a message is given with a name that starts 121 // with a lower-case letter, this function will panic. 122 // 123 // When this field is added to a message, the associated group message type will 124 // also be added. 125 // 126 // The new field will be optional. See SetLabel, SetRepeated, and SetRequired 127 // for changing this aspect of the field. The new field's tag will be zero, 128 // which means it will be auto-assigned when the descriptor is built. Use 129 // SetNumber or TrySetNumber to assign an explicit tag number. 130 func NewGroupField(mb *MessageBuilder) *FieldBuilder { 131 if !unicode.IsUpper(rune(mb.name[0])) { 132 panic(fmt.Sprintf("group name %s must start with a capital letter", mb.name)) 133 } 134 Unlink(mb) 135 136 ft := &FieldType{ 137 fieldType: descriptorpb.FieldDescriptorProto_TYPE_GROUP, 138 localMsgType: mb, 139 } 140 fieldName := strings.ToLower(mb.GetName()) 141 flb := NewField(fieldName, ft) 142 flb.msgType = mb 143 mb.setParent(flb) 144 return flb 145 } 146 147 // NewExtension creates a new FieldBuilder for an extension field with the given 148 // name, tag, type, and extendee. The extendee given is a message builder. 149 // 150 // The new field will be optional. See SetLabel and SetRepeated for changing 151 // this aspect of the field. 152 func NewExtension(name string, tag int32, typ *FieldType, extendee *MessageBuilder) *FieldBuilder { 153 if extendee == nil { 154 panic("extendee cannot be nil") 155 } 156 flb := NewField(name, typ).SetNumber(tag) 157 flb.localExtendee = extendee 158 return flb 159 } 160 161 // NewExtensionImported creates a new FieldBuilder for an extension field with 162 // the given name, tag, type, and extendee. The extendee given is a message 163 // descriptor. 164 // 165 // The new field will be optional. See SetLabel and SetRepeated for changing 166 // this aspect of the field. 167 func NewExtensionImported(name string, tag int32, typ *FieldType, extendee *desc.MessageDescriptor) *FieldBuilder { 168 if extendee == nil { 169 panic("extendee cannot be nil") 170 } 171 flb := NewField(name, typ).SetNumber(tag) 172 flb.foreignExtendee = extendee 173 return flb 174 } 175 176 // FromField returns a FieldBuilder that is effectively a copy of the given 177 // descriptor. 178 // 179 // Note that it is not just the given field that is copied but its entire file. 180 // So the caller can get the parent element of the returned builder and the 181 // result would be a builder that is effectively a copy of the field 182 // descriptor's parent. 183 // 184 // This means that field builders created from descriptors do not need to be 185 // explicitly assigned to a file in order to preserve the original field's 186 // package name. 187 func FromField(fld *desc.FieldDescriptor) (*FieldBuilder, error) { 188 if fb, err := FromFile(fld.GetFile()); err != nil { 189 return nil, err 190 } else if flb, ok := fb.findFullyQualifiedElement(fld.GetFullyQualifiedName()).(*FieldBuilder); ok { 191 return flb, nil 192 } else { 193 return nil, fmt.Errorf("could not find field %s after converting file %q to builder", fld.GetFullyQualifiedName(), fld.GetFile().GetName()) 194 } 195 } 196 197 func fromField(fld *desc.FieldDescriptor) (*FieldBuilder, error) { 198 ft := fieldTypeFromDescriptor(fld) 199 flb := NewField(fld.GetName(), ft) 200 flb.Options = fld.GetFieldOptions() 201 flb.Label = fld.GetLabel() 202 flb.Proto3Optional = fld.IsProto3Optional() 203 flb.Default = fld.AsFieldDescriptorProto().GetDefaultValue() 204 flb.JsonName = fld.GetJSONName() 205 setComments(&flb.comments, fld.GetSourceInfo()) 206 207 if fld.IsExtension() { 208 flb.foreignExtendee = fld.GetOwner() 209 } 210 if err := flb.TrySetNumber(fld.GetNumber()); err != nil { 211 return nil, err 212 } 213 return flb, nil 214 } 215 216 // SetName changes this field's name, returning the field builder for method 217 // chaining. If the given new name is not valid (e.g. TrySetName would have 218 // returned an error) then this method will panic. 219 func (flb *FieldBuilder) SetName(newName string) *FieldBuilder { 220 if err := flb.TrySetName(newName); err != nil { 221 panic(err) 222 } 223 return flb 224 } 225 226 // TrySetName changes this field's name. It will return an error if the given 227 // new name is not a valid protobuf identifier or if the parent builder already 228 // has an element with the given name. 229 // 230 // If the field is a non-extension whose parent is a one-of, the one-of's 231 // enclosing message is checked for elements with a conflicting name. Despite 232 // the fact that one-of choices are modeled as children of the one-of builder, 233 // in the protobuf IDL they are actually all defined in the message's namespace. 234 func (flb *FieldBuilder) TrySetName(newName string) error { 235 var oldMsgName string 236 if flb.msgType != nil { 237 if flb.fieldType.fieldType == descriptorpb.FieldDescriptorProto_TYPE_GROUP { 238 return fmt.Errorf("cannot change name of group field %s; change name of group instead", GetFullyQualifiedName(flb)) 239 } else { 240 oldMsgName = flb.msgType.name 241 msgName := entryTypeName(newName) 242 if err := flb.msgType.trySetNameInternal(msgName); err != nil { 243 return err 244 } 245 } 246 } 247 if err := flb.baseBuilder.setName(flb, newName); err != nil { 248 // undo change to map entry name 249 if flb.msgType != nil && flb.fieldType.fieldType != descriptorpb.FieldDescriptorProto_TYPE_GROUP { 250 flb.msgType.setNameInternal(oldMsgName) 251 } 252 return err 253 } 254 return nil 255 } 256 257 func (flb *FieldBuilder) trySetNameInternal(newName string) error { 258 return flb.baseBuilder.setName(flb, newName) 259 } 260 261 func (flb *FieldBuilder) setNameInternal(newName string) { 262 if err := flb.trySetNameInternal(newName); err != nil { 263 panic(err) 264 } 265 } 266 267 // SetComments sets the comments associated with the field. This method returns 268 // the field builder, for method chaining. 269 func (flb *FieldBuilder) SetComments(c Comments) *FieldBuilder { 270 flb.comments = c 271 return flb 272 } 273 274 func (flb *FieldBuilder) setParent(newParent Builder) { 275 flb.baseBuilder.setParent(newParent) 276 } 277 278 // GetChildren returns any builders assigned to this field builder. The only 279 // kind of children a field can have are message types, that correspond to the 280 // field's map entry type or group type (for map and group fields respectively). 281 func (flb *FieldBuilder) GetChildren() []Builder { 282 if flb.msgType != nil { 283 return []Builder{flb.msgType} 284 } 285 return nil 286 } 287 288 func (flb *FieldBuilder) findChild(name string) Builder { 289 if flb.msgType != nil && flb.msgType.name == name { 290 return flb.msgType 291 } 292 return nil 293 } 294 295 func (flb *FieldBuilder) removeChild(b Builder) { 296 if mb, ok := b.(*MessageBuilder); ok && mb == flb.msgType { 297 flb.msgType = nil 298 if p, ok := flb.parent.(*MessageBuilder); ok { 299 delete(p.symbols, mb.GetName()) 300 } 301 } 302 } 303 304 func (flb *FieldBuilder) renamedChild(b Builder, oldName string) error { 305 if flb.msgType != nil { 306 var oldFieldName string 307 if flb.fieldType.fieldType == descriptorpb.FieldDescriptorProto_TYPE_GROUP { 308 if !unicode.IsUpper(rune(b.GetName()[0])) { 309 return fmt.Errorf("group name %s must start with capital letter", b.GetName()) 310 } 311 // change field name to be lower-case form of group name 312 oldFieldName = flb.name 313 fieldName := strings.ToLower(b.GetName()) 314 if err := flb.trySetNameInternal(fieldName); err != nil { 315 return err 316 } 317 } 318 if p, ok := flb.parent.(*MessageBuilder); ok { 319 if err := p.addSymbol(b); err != nil { 320 if flb.fieldType.fieldType == descriptorpb.FieldDescriptorProto_TYPE_GROUP { 321 // revert the field rename 322 flb.setNameInternal(oldFieldName) 323 } 324 return err 325 } 326 } 327 } 328 return nil 329 } 330 331 // GetNumber returns this field's tag number, or zero if the tag number will be 332 // auto-assigned when the field descriptor is built. 333 func (flb *FieldBuilder) GetNumber() int32 { 334 return flb.number 335 } 336 337 // SetNumber changes the numeric tag for this field and then returns the field, 338 // for method chaining. If the given new tag is not valid (e.g. TrySetNumber 339 // would have returned an error) then this method will panic. 340 func (flb *FieldBuilder) SetNumber(tag int32) *FieldBuilder { 341 if err := flb.TrySetNumber(tag); err != nil { 342 panic(err) 343 } 344 return flb 345 } 346 347 // TrySetNumber changes this field's tag number. It will return an error if the 348 // given new tag is out of valid range or (for non-extension fields) if the 349 // enclosing message already includes a field with the given tag. 350 // 351 // Non-extension fields can be set to zero, which means a proper tag number will 352 // be auto-assigned when the descriptor is built. Extension field tags, however, 353 // must be set to a valid non-zero value. 354 func (flb *FieldBuilder) TrySetNumber(tag int32) error { 355 if tag == flb.number { 356 return nil // no change 357 } 358 if tag < 0 { 359 return fmt.Errorf("cannot set tag number for field %s to negative value %d", GetFullyQualifiedName(flb), tag) 360 } 361 if tag == 0 && flb.IsExtension() { 362 return fmt.Errorf("cannot set tag number for extension %s; only regular fields can be auto-assigned", GetFullyQualifiedName(flb)) 363 } 364 if tag >= internal.SpecialReservedStart && tag <= internal.SpecialReservedEnd { 365 return fmt.Errorf("tag for field %s cannot be in special reserved range %d-%d", GetFullyQualifiedName(flb), internal.SpecialReservedStart, internal.SpecialReservedEnd) 366 } 367 if tag > internal.MaxTag { 368 return fmt.Errorf("tag for field %s cannot be above max %d", GetFullyQualifiedName(flb), internal.MaxTag) 369 } 370 oldTag := flb.number 371 flb.number = tag 372 if flb.IsExtension() { 373 // extension tags are not tracked by builders, so no more to do 374 return nil 375 } 376 switch p := flb.parent.(type) { 377 case *OneOfBuilder: 378 m := p.parent() 379 if m != nil { 380 if err := m.addTag(flb); err != nil { 381 flb.number = oldTag 382 return err 383 } 384 delete(m.fieldTags, oldTag) 385 } 386 case *MessageBuilder: 387 if err := p.addTag(flb); err != nil { 388 flb.number = oldTag 389 return err 390 } 391 delete(p.fieldTags, oldTag) 392 } 393 return nil 394 } 395 396 // SetOptions sets the field options for this field and returns the field, for 397 // method chaining. 398 func (flb *FieldBuilder) SetOptions(options *descriptorpb.FieldOptions) *FieldBuilder { 399 flb.Options = options 400 return flb 401 } 402 403 // SetLabel sets the label for this field, which can be optional, repeated, or 404 // required. It returns the field builder, for method chaining. 405 func (flb *FieldBuilder) SetLabel(lbl descriptorpb.FieldDescriptorProto_Label) *FieldBuilder { 406 flb.Label = lbl 407 return flb 408 } 409 410 // SetProto3Optional sets whether this is a proto3 optional field. It returns 411 // the field builder, for method chaining. 412 func (flb *FieldBuilder) SetProto3Optional(p3o bool) *FieldBuilder { 413 flb.Proto3Optional = p3o 414 return flb 415 } 416 417 // SetRepeated sets the label for this field to repeated. It returns the field 418 // builder, for method chaining. 419 func (flb *FieldBuilder) SetRepeated() *FieldBuilder { 420 return flb.SetLabel(descriptorpb.FieldDescriptorProto_LABEL_REPEATED) 421 } 422 423 // SetRequired sets the label for this field to required. It returns the field 424 // builder, for method chaining. 425 func (flb *FieldBuilder) SetRequired() *FieldBuilder { 426 return flb.SetLabel(descriptorpb.FieldDescriptorProto_LABEL_REQUIRED) 427 } 428 429 // SetOptional sets the label for this field to optional. It returns the field 430 // builder, for method chaining. 431 func (flb *FieldBuilder) SetOptional() *FieldBuilder { 432 return flb.SetLabel(descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL) 433 } 434 435 // IsRepeated returns true if this field's label is repeated. Fields created via 436 // NewMapField will be repeated (since map's are represented "under the hood" as 437 // a repeated field of map entry messages). 438 func (flb *FieldBuilder) IsRepeated() bool { 439 return flb.Label == descriptorpb.FieldDescriptorProto_LABEL_REPEATED 440 } 441 442 // IsRequired returns true if this field's label is required. 443 func (flb *FieldBuilder) IsRequired() bool { 444 return flb.Label == descriptorpb.FieldDescriptorProto_LABEL_REQUIRED 445 } 446 447 // IsOptional returns true if this field's label is optional. 448 func (flb *FieldBuilder) IsOptional() bool { 449 return flb.Label == descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL 450 } 451 452 // IsMap returns true if this field is a map field. 453 func (flb *FieldBuilder) IsMap() bool { 454 return flb.IsRepeated() && 455 flb.msgType != nil && 456 flb.fieldType.fieldType != descriptorpb.FieldDescriptorProto_TYPE_GROUP && 457 flb.msgType.Options != nil && 458 flb.msgType.Options.GetMapEntry() 459 } 460 461 // GetType returns the field's type. 462 func (flb *FieldBuilder) GetType() *FieldType { 463 return flb.fieldType 464 } 465 466 // SetType changes the field's type and returns the field builder, for method 467 // chaining. 468 func (flb *FieldBuilder) SetType(ft *FieldType) *FieldBuilder { 469 flb.fieldType = ft 470 if flb.msgType != nil && flb.msgType != ft.localMsgType { 471 Unlink(flb.msgType) 472 } 473 return flb 474 } 475 476 // SetDefaultValue changes the field's type and returns the field builder, for 477 // method chaining. 478 func (flb *FieldBuilder) SetDefaultValue(defValue string) *FieldBuilder { 479 flb.Default = defValue 480 return flb 481 } 482 483 // SetJsonName sets the name used in the field's JSON representation and then 484 // returns the field builder, for method chaining. 485 func (flb *FieldBuilder) SetJsonName(jsonName string) *FieldBuilder { 486 flb.JsonName = jsonName 487 return flb 488 } 489 490 // IsExtension returns true if this is an extension field. 491 func (flb *FieldBuilder) IsExtension() bool { 492 return flb.localExtendee != nil || flb.foreignExtendee != nil 493 } 494 495 // GetExtendeeTypeName returns the fully qualified name of the extended message 496 // or it returns an empty string if this is not an extension field. 497 func (flb *FieldBuilder) GetExtendeeTypeName() string { 498 if flb.foreignExtendee != nil { 499 return flb.foreignExtendee.GetFullyQualifiedName() 500 } else if flb.localExtendee != nil { 501 return GetFullyQualifiedName(flb.localExtendee) 502 } else { 503 return "" 504 } 505 } 506 507 func (flb *FieldBuilder) buildProto(path []int32, sourceInfo *descriptorpb.SourceCodeInfo, isMessageSet bool) (*descriptorpb.FieldDescriptorProto, error) { 508 addCommentsTo(sourceInfo, path, &flb.comments) 509 510 isProto3 := flb.GetFile().IsProto3 511 isEditions := flb.GetFile().Edition > 0 512 if flb.Proto3Optional { 513 if !isProto3 { 514 return nil, fmt.Errorf("field %s is not in a proto3 syntax file but is marked as a proto3 optional field", GetFullyQualifiedName(flb)) 515 } 516 if flb.IsExtension() { 517 return nil, fmt.Errorf("field %s: extensions cannot be proto3 optional fields", GetFullyQualifiedName(flb)) 518 } 519 if _, ok := flb.GetParent().(*OneOfBuilder); ok { 520 return nil, fmt.Errorf("field %s: proto3 optional fields cannot belong to a oneof", GetFullyQualifiedName(flb)) 521 } 522 } 523 524 var lbl *descriptorpb.FieldDescriptorProto_Label 525 if int32(flb.Label) != 0 { 526 if (isProto3 || isEditions) && flb.Label == descriptorpb.FieldDescriptorProto_LABEL_REQUIRED { 527 return nil, fmt.Errorf("field %s: only proto2 allows required fields", GetFullyQualifiedName(flb)) 528 } 529 lbl = flb.Label.Enum() 530 } 531 var typeName *string 532 tn := flb.fieldType.GetTypeName() 533 if tn != "" { 534 typeName = proto.String("." + tn) 535 } 536 var extendee *string 537 if flb.IsExtension() { 538 extendee = proto.String("." + flb.GetExtendeeTypeName()) 539 } 540 jsName := flb.JsonName 541 if jsName == "" { 542 jsName = internal.JsonName(flb.name) 543 } 544 var def *string 545 if flb.Default != "" { 546 def = proto.String(flb.Default) 547 } 548 var proto3Optional *bool 549 if flb.Proto3Optional { 550 proto3Optional = proto.Bool(true) 551 } 552 553 maxTag := internal.GetMaxTag(isMessageSet) 554 if flb.number > maxTag { 555 return nil, fmt.Errorf("tag for field %s cannot be above max %d", GetFullyQualifiedName(flb), maxTag) 556 } 557 558 fd := &descriptorpb.FieldDescriptorProto{ 559 Name: proto.String(flb.name), 560 Number: proto.Int32(flb.number), 561 Options: flb.Options, 562 Label: lbl, 563 Type: flb.fieldType.fieldType.Enum(), 564 TypeName: typeName, 565 JsonName: proto.String(jsName), 566 DefaultValue: def, 567 Extendee: extendee, 568 Proto3Optional: proto3Optional, 569 } 570 return fd, nil 571 } 572 573 // Build constructs a field descriptor based on the contents of this field 574 // builder. If there are any problems constructing the descriptor, including 575 // resolving symbols referenced by the builder or failing to meet certain 576 // validation rules, an error is returned. 577 func (flb *FieldBuilder) Build() (*desc.FieldDescriptor, error) { 578 fld, err := flb.BuildDescriptor() 579 if err != nil { 580 return nil, err 581 } 582 return fld.(*desc.FieldDescriptor), nil 583 } 584 585 // BuildDescriptor constructs a field descriptor based on the contents of this 586 // field builder. Most usages will prefer Build() instead, whose return type is 587 // a concrete descriptor type. This method is present to satisfy the Builder 588 // interface. 589 func (flb *FieldBuilder) BuildDescriptor() (desc.Descriptor, error) { 590 return doBuild(flb, BuilderOptions{}) 591 } 592 593 // OneOfBuilder is a builder used to construct a desc.OneOfDescriptor. A one-of 594 // builder *must* be added to a message before calling its Build() method. 595 // 596 // To create a new OneOfBuilder, use NewOneOf. 597 type OneOfBuilder struct { 598 baseBuilder 599 600 Options *descriptorpb.OneofOptions 601 602 choices []*FieldBuilder 603 symbols map[string]*FieldBuilder 604 } 605 606 // NewOneOf creates a new OneOfBuilder for a one-of with the given name. 607 func NewOneOf(name string) *OneOfBuilder { 608 return &OneOfBuilder{ 609 baseBuilder: baseBuilderWithName(name), 610 symbols: map[string]*FieldBuilder{}, 611 } 612 } 613 614 // FromOneOf returns a OneOfBuilder that is effectively a copy of the given 615 // descriptor. 616 // 617 // Note that it is not just the given one-of that is copied but its entire file. 618 // So the caller can get the parent element of the returned builder and the 619 // result would be a builder that is effectively a copy of the one-of 620 // descriptor's parent message. 621 // 622 // This means that one-of builders created from descriptors do not need to be 623 // explicitly assigned to a file in order to preserve the original one-of's 624 // package name. 625 // 626 // This function returns an error if the given descriptor is synthetic. 627 func FromOneOf(ood *desc.OneOfDescriptor) (*OneOfBuilder, error) { 628 if ood.IsSynthetic() { 629 return nil, fmt.Errorf("one-of %s is synthetic", ood.GetFullyQualifiedName()) 630 } 631 if fb, err := FromFile(ood.GetFile()); err != nil { 632 return nil, err 633 } else if oob, ok := fb.findFullyQualifiedElement(ood.GetFullyQualifiedName()).(*OneOfBuilder); ok { 634 return oob, nil 635 } else { 636 return nil, fmt.Errorf("could not find one-of %s after converting file %q to builder", ood.GetFullyQualifiedName(), ood.GetFile().GetName()) 637 } 638 } 639 640 func fromOneOf(ood *desc.OneOfDescriptor) (*OneOfBuilder, error) { 641 oob := NewOneOf(ood.GetName()) 642 oob.Options = ood.GetOneOfOptions() 643 setComments(&oob.comments, ood.GetSourceInfo()) 644 645 for _, fld := range ood.GetChoices() { 646 if flb, err := fromField(fld); err != nil { 647 return nil, err 648 } else if err := oob.TryAddChoice(flb); err != nil { 649 return nil, err 650 } 651 } 652 653 return oob, nil 654 } 655 656 // SetName changes this one-of's name, returning the one-of builder for method 657 // chaining. If the given new name is not valid (e.g. TrySetName would have 658 // returned an error) then this method will panic. 659 func (oob *OneOfBuilder) SetName(newName string) *OneOfBuilder { 660 if err := oob.TrySetName(newName); err != nil { 661 panic(err) 662 } 663 return oob 664 } 665 666 // TrySetName changes this one-of's name. It will return an error if the given 667 // new name is not a valid protobuf identifier or if the parent message builder 668 // already has an element with the given name. 669 func (oob *OneOfBuilder) TrySetName(newName string) error { 670 return oob.baseBuilder.setName(oob, newName) 671 } 672 673 // SetComments sets the comments associated with the one-of. This method 674 // returns the one-of builder, for method chaining. 675 func (oob *OneOfBuilder) SetComments(c Comments) *OneOfBuilder { 676 oob.comments = c 677 return oob 678 } 679 680 // GetChildren returns any builders assigned to this one-of builder. These will 681 // be choices for the one-of, each of which will be a field builder. 682 func (oob *OneOfBuilder) GetChildren() []Builder { 683 var ch []Builder 684 for _, evb := range oob.choices { 685 ch = append(ch, evb) 686 } 687 return ch 688 } 689 690 func (oob *OneOfBuilder) parent() *MessageBuilder { 691 if oob.baseBuilder.parent == nil { 692 return nil 693 } 694 return oob.baseBuilder.parent.(*MessageBuilder) 695 } 696 697 func (oob *OneOfBuilder) findChild(name string) Builder { 698 // in terms of finding a child by qualified name, fields in the 699 // one-of are considered children of the message, not the one-of 700 return nil 701 } 702 703 func (oob *OneOfBuilder) removeChild(b Builder) { 704 if p, ok := b.GetParent().(*OneOfBuilder); !ok || p != oob { 705 return 706 } 707 708 if oob.parent() != nil { 709 // remove from message's name and tag maps 710 flb := b.(*FieldBuilder) 711 delete(oob.parent().fieldTags, flb.GetNumber()) 712 delete(oob.parent().symbols, flb.GetName()) 713 } 714 715 oob.choices = deleteBuilder(b.GetName(), oob.choices).([]*FieldBuilder) 716 delete(oob.symbols, b.GetName()) 717 b.setParent(nil) 718 } 719 720 func (oob *OneOfBuilder) renamedChild(b Builder, oldName string) error { 721 if p, ok := b.GetParent().(*OneOfBuilder); !ok || p != oob { 722 return nil 723 } 724 725 if err := oob.addSymbol(b.(*FieldBuilder)); err != nil { 726 return err 727 } 728 729 // update message's name map (to make sure new field name doesn't 730 // collide with other kinds of elements in the message) 731 if oob.parent() != nil { 732 if err := oob.parent().addSymbol(b); err != nil { 733 delete(oob.symbols, b.GetName()) 734 return err 735 } 736 delete(oob.parent().symbols, oldName) 737 } 738 739 delete(oob.symbols, oldName) 740 return nil 741 } 742 743 func (oob *OneOfBuilder) addSymbol(b *FieldBuilder) error { 744 if _, ok := oob.symbols[b.GetName()]; ok { 745 return fmt.Errorf("one-of %s already contains field named %q", GetFullyQualifiedName(oob), b.GetName()) 746 } 747 oob.symbols[b.GetName()] = b 748 return nil 749 } 750 751 // GetChoice returns the field with the given name. If no such field exists in 752 // the one-of, nil is returned. 753 func (oob *OneOfBuilder) GetChoice(name string) *FieldBuilder { 754 return oob.symbols[name] 755 } 756 757 // RemoveChoice removes the field with the given name. If no such field exists 758 // in the one-of, this is a no-op. This returns the one-of builder, for method 759 // chaining. 760 func (oob *OneOfBuilder) RemoveChoice(name string) *OneOfBuilder { 761 oob.TryRemoveChoice(name) 762 return oob 763 } 764 765 // TryRemoveChoice removes the field with the given name and returns false if 766 // the one-of has no such field. 767 func (oob *OneOfBuilder) TryRemoveChoice(name string) bool { 768 if flb, ok := oob.symbols[name]; ok { 769 oob.removeChild(flb) 770 return true 771 } 772 return false 773 } 774 775 // AddChoice adds the given field to this one-of. If an error prevents the field 776 // from being added, this method panics. If the given field is an extension, 777 // this method panics. If the given field is a group or map field or if it is 778 // not optional (e.g. it is required or repeated), this method panics. This 779 // returns the one-of builder, for method chaining. 780 func (oob *OneOfBuilder) AddChoice(flb *FieldBuilder) *OneOfBuilder { 781 if err := oob.TryAddChoice(flb); err != nil { 782 panic(err) 783 } 784 return oob 785 } 786 787 // TryAddChoice adds the given field to this one-of, returning any error that 788 // prevents the field from being added (such as a name collision with another 789 // element already added to the enclosing message). An error is returned if the 790 // given field is an extension field, a map or group field, or repeated or 791 // required. 792 func (oob *OneOfBuilder) TryAddChoice(flb *FieldBuilder) error { 793 if flb.IsExtension() { 794 return fmt.Errorf("field %s is an extension, not a regular field", flb.GetName()) 795 } 796 if flb.msgType != nil && flb.fieldType.fieldType != descriptorpb.FieldDescriptorProto_TYPE_GROUP { 797 return fmt.Errorf("cannot add a map field %q to one-of %s", flb.name, GetFullyQualifiedName(oob)) 798 } 799 if flb.IsRepeated() || flb.IsRequired() { 800 return fmt.Errorf("fields in a one-of must be optional, %s is %v", flb.name, flb.Label) 801 } 802 if err := oob.addSymbol(flb); err != nil { 803 return err 804 } 805 mb := oob.parent() 806 if mb != nil { 807 // If we are moving field from a message to a one-of that belongs to the 808 // same message, we have to use different order of operations to prevent 809 // failure (otherwise, it looks like it's being added twice). 810 // (We do similar if moving the other direction, from the one-of into 811 // the message to which one-of belongs.) 812 needToUnlinkFirst := mb.isPresentButNotChild(flb) 813 if needToUnlinkFirst { 814 Unlink(flb) 815 mb.registerField(flb) 816 } else { 817 if err := mb.registerField(flb); err != nil { 818 delete(oob.symbols, flb.GetName()) 819 return err 820 } 821 Unlink(flb) 822 } 823 } 824 flb.setParent(oob) 825 oob.choices = append(oob.choices, flb) 826 return nil 827 } 828 829 // SetOptions sets the one-of options for this one-of and returns the one-of, 830 // for method chaining. 831 func (oob *OneOfBuilder) SetOptions(options *descriptorpb.OneofOptions) *OneOfBuilder { 832 oob.Options = options 833 return oob 834 } 835 836 func (oob *OneOfBuilder) buildProto(path []int32, sourceInfo *descriptorpb.SourceCodeInfo) (*descriptorpb.OneofDescriptorProto, error) { 837 addCommentsTo(sourceInfo, path, &oob.comments) 838 839 for _, flb := range oob.choices { 840 if flb.IsRepeated() || flb.IsRequired() { 841 return nil, fmt.Errorf("fields in a one-of must be optional, %s is %v", GetFullyQualifiedName(flb), flb.Label) 842 } 843 } 844 845 return &descriptorpb.OneofDescriptorProto{ 846 Name: proto.String(oob.name), 847 Options: oob.Options, 848 }, nil 849 } 850 851 // Build constructs a one-of descriptor based on the contents of this one-of 852 // builder. If there are any problems constructing the descriptor, including 853 // resolving symbols referenced by the builder or failing to meet certain 854 // validation rules, an error is returned. 855 func (oob *OneOfBuilder) Build() (*desc.OneOfDescriptor, error) { 856 ood, err := oob.BuildDescriptor() 857 if err != nil { 858 return nil, err 859 } 860 return ood.(*desc.OneOfDescriptor), nil 861 } 862 863 // BuildDescriptor constructs a one-of descriptor based on the contents of this 864 // one-of builder. Most usages will prefer Build() instead, whose return type is 865 // a concrete descriptor type. This method is present to satisfy the Builder 866 // interface. 867 func (oob *OneOfBuilder) BuildDescriptor() (desc.Descriptor, error) { 868 return doBuild(oob, BuilderOptions{}) 869 } 870 871 func entryTypeName(fieldName string) string { 872 return internal.InitCap(internal.JsonName(fieldName)) + "Entry" 873 }