1package builder
2
3import (
4	"fmt"
5	"sort"
6	"strings"
7	"sync/atomic"
8
9	"github.com/golang/protobuf/proto"
10	dpb "github.com/golang/protobuf/protoc-gen-go/descriptor"
11
12	"github.com/jhump/protoreflect/desc"
13	"github.com/jhump/protoreflect/desc/internal"
14)
15
16var uniqueFileCounter uint64
17
18func uniqueFileName() string {
19	i := atomic.AddUint64(&uniqueFileCounter, 1)
20	return fmt.Sprintf("{generated-file-%04x}.proto", i)
21}
22
23func makeUnique(name string, existingNames map[string]struct{}) string {
24	i := 1
25	n := name
26	for {
27		if _, ok := existingNames[n]; !ok {
28			return n
29		}
30		n = fmt.Sprintf("%s(%d)", name, i)
31		i++
32	}
33}
34
35// FileBuilder is a builder used to construct a desc.FileDescriptor. This is the
36// root of the hierarchy. All other descriptors belong to a file, and thus all
37// other builders also belong to a file.
38//
39// If a builder is *not* associated with a file, the resulting descriptor will
40// be associated with a synthesized file that contains only the built descriptor
41// and its ancestors. This means that such descriptors will have no associated
42// package name.
43//
44// To create a new FileBuilder, use NewFile.
45type FileBuilder struct {
46	name string
47
48	IsProto3 bool
49	Package  string
50	Options  *dpb.FileOptions
51
52	comments        Comments
53	SyntaxComments  Comments
54	PackageComments Comments
55
56	messages   []*MessageBuilder
57	extensions []*FieldBuilder
58	enums      []*EnumBuilder
59	services   []*ServiceBuilder
60	symbols    map[string]Builder
61
62	explicitDeps    map[*FileBuilder]struct{}
63	explicitImports map[*desc.FileDescriptor]struct{}
64}
65
66// NewFile creates a new FileBuilder for a file with the given name. The
67// name can be blank, which indicates a unique name should be generated for it.
68func NewFile(name string) *FileBuilder {
69	return &FileBuilder{
70		name:    name,
71		symbols: map[string]Builder{},
72	}
73}
74
75// FromFile returns a FileBuilder that is effectively a copy of the given
76// descriptor. Note that builders do not retain full source code info, even if
77// the given descriptor included it. Instead, comments are extracted from the
78// given descriptor's source info (if present) and, when built, the resulting
79// descriptor will have just the comment info (no location information).
80func FromFile(fd *desc.FileDescriptor) (*FileBuilder, error) {
81	fb := NewFile(fd.GetName())
82	fb.IsProto3 = fd.IsProto3()
83	fb.Package = fd.GetPackage()
84	fb.Options = fd.GetFileOptions()
85	setComments(&fb.comments, fd.GetSourceInfo())
86
87	// find syntax and package comments, too
88	for _, loc := range fd.AsFileDescriptorProto().GetSourceCodeInfo().GetLocation() {
89		if len(loc.Path) == 1 {
90			if loc.Path[0] == internal.File_syntaxTag {
91				setComments(&fb.SyntaxComments, loc)
92			} else if loc.Path[0] == internal.File_packageTag {
93				setComments(&fb.PackageComments, loc)
94			}
95		}
96	}
97
98	// add imports explicitly
99	for _, dep := range fd.GetDependencies() {
100		fb.AddImportedDependency(dep)
101	}
102
103	localMessages := map[*desc.MessageDescriptor]*MessageBuilder{}
104	localEnums := map[*desc.EnumDescriptor]*EnumBuilder{}
105
106	for _, md := range fd.GetMessageTypes() {
107		if mb, err := fromMessage(md, localMessages, localEnums); err != nil {
108			return nil, err
109		} else if err := fb.TryAddMessage(mb); err != nil {
110			return nil, err
111		}
112	}
113	for _, ed := range fd.GetEnumTypes() {
114		if eb, err := fromEnum(ed, localEnums); err != nil {
115			return nil, err
116		} else if err := fb.TryAddEnum(eb); err != nil {
117			return nil, err
118		}
119	}
120	for _, exd := range fd.GetExtensions() {
121		if exb, err := fromField(exd); err != nil {
122			return nil, err
123		} else if err := fb.TryAddExtension(exb); err != nil {
124			return nil, err
125		}
126	}
127	for _, sd := range fd.GetServices() {
128		if sb, err := fromService(sd); err != nil {
129			return nil, err
130		} else if err := fb.TryAddService(sb); err != nil {
131			return nil, err
132		}
133	}
134
135	// we've converted everything, so now we update all foreign type references
136	// to be local type references if possible
137	for _, mb := range fb.messages {
138		updateLocalRefsInMessage(mb, localMessages, localEnums)
139	}
140	for _, exb := range fb.extensions {
141		updateLocalRefsInField(exb, localMessages, localEnums)
142	}
143	for _, sb := range fb.services {
144		for _, mtb := range sb.methods {
145			updateLocalRefsInRpcType(mtb.ReqType, localMessages)
146			updateLocalRefsInRpcType(mtb.RespType, localMessages)
147		}
148	}
149
150	return fb, nil
151}
152
153func updateLocalRefsInMessage(mb *MessageBuilder, localMessages map[*desc.MessageDescriptor]*MessageBuilder, localEnums map[*desc.EnumDescriptor]*EnumBuilder) {
154	for _, b := range mb.fieldsAndOneOfs {
155		if flb, ok := b.(*FieldBuilder); ok {
156			updateLocalRefsInField(flb, localMessages, localEnums)
157		} else {
158			oob := b.(*OneOfBuilder)
159			for _, flb := range oob.choices {
160				updateLocalRefsInField(flb, localMessages, localEnums)
161			}
162		}
163	}
164	for _, nmb := range mb.nestedMessages {
165		updateLocalRefsInMessage(nmb, localMessages, localEnums)
166	}
167	for _, exb := range mb.nestedExtensions {
168		updateLocalRefsInField(exb, localMessages, localEnums)
169	}
170}
171
172func updateLocalRefsInField(flb *FieldBuilder, localMessages map[*desc.MessageDescriptor]*MessageBuilder, localEnums map[*desc.EnumDescriptor]*EnumBuilder) {
173	if flb.fieldType.foreignMsgType != nil {
174		if mb, ok := localMessages[flb.fieldType.foreignMsgType]; ok {
175			flb.fieldType.foreignMsgType = nil
176			flb.fieldType.localMsgType = mb
177		}
178	}
179	if flb.fieldType.foreignEnumType != nil {
180		if eb, ok := localEnums[flb.fieldType.foreignEnumType]; ok {
181			flb.fieldType.foreignEnumType = nil
182			flb.fieldType.localEnumType = eb
183		}
184	}
185	if flb.foreignExtendee != nil {
186		if mb, ok := localMessages[flb.foreignExtendee]; ok {
187			flb.foreignExtendee = nil
188			flb.localExtendee = mb
189		}
190	}
191	if flb.msgType != nil {
192		updateLocalRefsInMessage(flb.msgType, localMessages, localEnums)
193	}
194}
195
196func updateLocalRefsInRpcType(rpcType *RpcType, localMessages map[*desc.MessageDescriptor]*MessageBuilder) {
197	if rpcType.foreignType != nil {
198		if mb, ok := localMessages[rpcType.foreignType]; ok {
199			rpcType.foreignType = nil
200			rpcType.localType = mb
201		}
202	}
203}
204
205// GetName returns the name of the file. It may include relative path
206// information, too.
207func (fb *FileBuilder) GetName() string {
208	return fb.name
209}
210
211// SetName changes this file's name, returning the file builder for method
212// chaining.
213func (fb *FileBuilder) SetName(newName string) *FileBuilder {
214	fb.name = newName
215	return fb
216}
217
218// TrySetName changes this file's name. It always returns nil since renaming
219// a file cannot fail. (It is specified to return error to satisfy the Builder
220// interface.)
221func (fb *FileBuilder) TrySetName(newName string) error {
222	fb.name = newName
223	return nil
224}
225
226// GetParent always returns nil since files are the roots of builder
227// hierarchies.
228func (fb *FileBuilder) GetParent() Builder {
229	return nil
230}
231
232func (fb *FileBuilder) setParent(parent Builder) {
233	if parent != nil {
234		panic("files cannot have parent elements")
235	}
236}
237
238// GetComments returns comments associated with the file itself and not any
239// particular element therein. (Note that such a comment will not be rendered by
240// the protoprint package.)
241func (fb *FileBuilder) GetComments() *Comments {
242	return &fb.comments
243}
244
245// SetComments sets the comments associated with the file itself, not any
246// particular element therein. (Note that such a comment will not be rendered by
247// the protoprint package.) This method returns the file, for method chaining.
248func (fb *FileBuilder) SetComments(c Comments) *FileBuilder {
249	fb.comments = c
250	return fb
251}
252
253// SetSyntaxComments sets the comments associated with the syntax declaration
254// element (which, if present, is required to be the first element in a proto
255// file). This method returns the file, for method chaining.
256func (fb *FileBuilder) SetSyntaxComments(c Comments) *FileBuilder {
257	fb.SyntaxComments = c
258	return fb
259}
260
261// SetPackageComments sets the comments associated with the package declaration
262// element. (This comment will not be rendered if the file's declared package is
263// empty.) This method returns the file, for method chaining.
264func (fb *FileBuilder) SetPackageComments(c Comments) *FileBuilder {
265	fb.PackageComments = c
266	return fb
267}
268
269// GetFile implements the Builder interface and always returns this file.
270func (fb *FileBuilder) GetFile() *FileBuilder {
271	return fb
272}
273
274// GetChildren returns builders for all nested elements, including all top-level
275// messages, enums, extensions, and services.
276func (fb *FileBuilder) GetChildren() []Builder {
277	var ch []Builder
278	for _, mb := range fb.messages {
279		ch = append(ch, mb)
280	}
281	for _, exb := range fb.extensions {
282		ch = append(ch, exb)
283	}
284	for _, eb := range fb.enums {
285		ch = append(ch, eb)
286	}
287	for _, sb := range fb.services {
288		ch = append(ch, sb)
289	}
290	return ch
291}
292
293func (fb *FileBuilder) findChild(name string) Builder {
294	return fb.symbols[name]
295}
296
297func (fb *FileBuilder) removeChild(b Builder) {
298	if p, ok := b.GetParent().(*FileBuilder); !ok || p != fb {
299		return
300	}
301
302	switch b.(type) {
303	case *MessageBuilder:
304		fb.messages = deleteBuilder(b.GetName(), fb.messages).([]*MessageBuilder)
305	case *FieldBuilder:
306		fb.extensions = deleteBuilder(b.GetName(), fb.extensions).([]*FieldBuilder)
307	case *EnumBuilder:
308		fb.enums = deleteBuilder(b.GetName(), fb.enums).([]*EnumBuilder)
309	case *ServiceBuilder:
310		fb.services = deleteBuilder(b.GetName(), fb.services).([]*ServiceBuilder)
311	}
312	delete(fb.symbols, b.GetName())
313	b.setParent(nil)
314}
315
316func (fb *FileBuilder) renamedChild(b Builder, oldName string) error {
317	if p, ok := b.GetParent().(*FileBuilder); !ok || p != fb {
318		return nil
319	}
320
321	if err := fb.addSymbol(b); err != nil {
322		return err
323	}
324	delete(fb.symbols, oldName)
325	return nil
326}
327
328func (fb *FileBuilder) addSymbol(b Builder) error {
329	if ex, ok := fb.symbols[b.GetName()]; ok {
330		return fmt.Errorf("file %q already contains element (%T) named %q", fb.GetName(), ex, b.GetName())
331	}
332	fb.symbols[b.GetName()] = b
333	return nil
334}
335
336func (fb *FileBuilder) findFullyQualifiedElement(fqn string) Builder {
337	if fb.Package != "" {
338		if !strings.HasPrefix(fqn, fb.Package+".") {
339			return nil
340		}
341		fqn = fqn[len(fb.Package)+1:]
342	}
343	names := strings.Split(fqn, ".")
344	var b Builder = fb
345	for b != nil && len(names) > 0 {
346		b = b.findChild(names[0])
347		names = names[1:]
348	}
349	return b
350}
351
352// GetMessage returns the top-level message with the given name. If no such
353// message exists in the file, nil is returned.
354func (fb *FileBuilder) GetMessage(name string) *MessageBuilder {
355	b := fb.symbols[name]
356	if mb, ok := b.(*MessageBuilder); ok {
357		return mb
358	} else {
359		return nil
360	}
361}
362
363// RemoveMessage removes the top-level message with the given name. If no such
364// message exists in the file, this is a no-op. This returns the file builder,
365// for method chaining.
366func (fb *FileBuilder) RemoveMessage(name string) *FileBuilder {
367	fb.TryRemoveMessage(name)
368	return fb
369}
370
371// TryRemoveMessage removes the top-level message with the given name and
372// returns false if the file has no such message.
373func (fb *FileBuilder) TryRemoveMessage(name string) bool {
374	b := fb.symbols[name]
375	if mb, ok := b.(*MessageBuilder); ok {
376		fb.removeChild(mb)
377		return true
378	}
379	return false
380}
381
382// AddMessage adds the given message to this file. If an error prevents the
383// message from being added, this method panics. This returns the file builder,
384// for method chaining.
385func (fb *FileBuilder) AddMessage(mb *MessageBuilder) *FileBuilder {
386	if err := fb.TryAddMessage(mb); err != nil {
387		panic(err)
388	}
389	return fb
390}
391
392// TryAddMessage adds the given message to this file, returning any error that
393// prevents the message from being added (such as a name collision with another
394// element already added to the file).
395func (fb *FileBuilder) TryAddMessage(mb *MessageBuilder) error {
396	if err := fb.addSymbol(mb); err != nil {
397		return err
398	}
399	Unlink(mb)
400	mb.setParent(fb)
401	fb.messages = append(fb.messages, mb)
402	return nil
403}
404
405// GetExtension returns the top-level extension with the given name. If no such
406// extension exists in the file, nil is returned.
407func (fb *FileBuilder) GetExtension(name string) *FieldBuilder {
408	b := fb.symbols[name]
409	if exb, ok := b.(*FieldBuilder); ok {
410		return exb
411	} else {
412		return nil
413	}
414}
415
416// RemoveExtension removes the top-level extension with the given name. If no
417// such extension exists in the file, this is a no-op. This returns the file
418// builder, for method chaining.
419func (fb *FileBuilder) RemoveExtension(name string) *FileBuilder {
420	fb.TryRemoveExtension(name)
421	return fb
422}
423
424// TryRemoveExtension removes the top-level extension with the given name and
425// returns false if the file has no such extension.
426func (fb *FileBuilder) TryRemoveExtension(name string) bool {
427	b := fb.symbols[name]
428	if exb, ok := b.(*FieldBuilder); ok {
429		fb.removeChild(exb)
430		return true
431	}
432	return false
433}
434
435// AddExtension adds the given extension to this file. If an error prevents the
436// extension from being added, this method panics. This returns the file
437// builder, for method chaining.
438func (fb *FileBuilder) AddExtension(exb *FieldBuilder) *FileBuilder {
439	if err := fb.TryAddExtension(exb); err != nil {
440		panic(err)
441	}
442	return fb
443}
444
445// TryAddExtension adds the given extension to this file, returning any error
446// that prevents the extension from being added (such as a name collision with
447// another element already added to the file).
448func (fb *FileBuilder) TryAddExtension(exb *FieldBuilder) error {
449	if !exb.IsExtension() {
450		return fmt.Errorf("field %s is not an extension", exb.GetName())
451	}
452	if err := fb.addSymbol(exb); err != nil {
453		return err
454	}
455	Unlink(exb)
456	exb.setParent(fb)
457	fb.extensions = append(fb.extensions, exb)
458	return nil
459}
460
461// GetEnum returns the top-level enum with the given name. If no such enum
462// exists in the file, nil is returned.
463func (fb *FileBuilder) GetEnum(name string) *EnumBuilder {
464	b := fb.symbols[name]
465	if eb, ok := b.(*EnumBuilder); ok {
466		return eb
467	} else {
468		return nil
469	}
470}
471
472// RemoveEnum removes the top-level enum with the given name. If no such enum
473// exists in the file, this is a no-op. This returns the file builder, for
474// method chaining.
475func (fb *FileBuilder) RemoveEnum(name string) *FileBuilder {
476	fb.TryRemoveEnum(name)
477	return fb
478}
479
480// TryRemoveEnum removes the top-level enum with the given name and returns
481// false if the file has no such enum.
482func (fb *FileBuilder) TryRemoveEnum(name string) bool {
483	b := fb.symbols[name]
484	if eb, ok := b.(*EnumBuilder); ok {
485		fb.removeChild(eb)
486		return true
487	}
488	return false
489}
490
491// AddEnum adds the given enum to this file. If an error prevents the enum from
492// being added, this method panics. This returns the file builder, for method
493// chaining.
494func (fb *FileBuilder) AddEnum(eb *EnumBuilder) *FileBuilder {
495	if err := fb.TryAddEnum(eb); err != nil {
496		panic(err)
497	}
498	return fb
499}
500
501// TryAddEnum adds the given enum to this file, returning any error that
502// prevents the enum from being added (such as a name collision with another
503// element already added to the file).
504func (fb *FileBuilder) TryAddEnum(eb *EnumBuilder) error {
505	if err := fb.addSymbol(eb); err != nil {
506		return err
507	}
508	Unlink(eb)
509	eb.setParent(fb)
510	fb.enums = append(fb.enums, eb)
511	return nil
512}
513
514// GetService returns the top-level service with the given name. If no such
515// service exists in the file, nil is returned.
516func (fb *FileBuilder) GetService(name string) *ServiceBuilder {
517	b := fb.symbols[name]
518	if sb, ok := b.(*ServiceBuilder); ok {
519		return sb
520	} else {
521		return nil
522	}
523}
524
525// RemoveService removes the top-level service with the given name. If no such
526// service exists in the file, this is a no-op. This returns the file builder,
527// for method chaining.
528func (fb *FileBuilder) RemoveService(name string) *FileBuilder {
529	fb.TryRemoveService(name)
530	return fb
531}
532
533// TryRemoveService removes the top-level service with the given name and
534// returns false if the file has no such service.
535func (fb *FileBuilder) TryRemoveService(name string) bool {
536	b := fb.symbols[name]
537	if sb, ok := b.(*ServiceBuilder); ok {
538		fb.removeChild(sb)
539		return true
540	}
541	return false
542}
543
544// AddService adds the given service to this file. If an error prevents the
545// service from being added, this method panics. This returns the file builder,
546// for method chaining.
547func (fb *FileBuilder) AddService(sb *ServiceBuilder) *FileBuilder {
548	if err := fb.TryAddService(sb); err != nil {
549		panic(err)
550	}
551	return fb
552}
553
554// TryAddService adds the given service to this file, returning any error that
555// prevents the service from being added (such as a name collision with another
556// element already added to the file).
557func (fb *FileBuilder) TryAddService(sb *ServiceBuilder) error {
558	if err := fb.addSymbol(sb); err != nil {
559		return err
560	}
561	Unlink(sb)
562	sb.setParent(fb)
563	fb.services = append(fb.services, sb)
564	return nil
565}
566
567// AddDependency adds the given file as an explicit import. Normally,
568// dependencies can be inferred during the build process by finding the files
569// for all referenced types (such as message and enum types used in this file).
570// However, this does not work for custom options, which must be known in order
571// to be interpretable. And they aren't known unless an explicit import is added
572// for the file that contains the custom options.
573//
574// Knowledge of custom options can also be provided by using BuildOptions with
575// an ExtensionRegistry, when building the file.
576func (fb *FileBuilder) AddDependency(dep *FileBuilder) *FileBuilder {
577	if fb.explicitDeps == nil {
578		fb.explicitDeps = map[*FileBuilder]struct{}{}
579	}
580	fb.explicitDeps[dep] = struct{}{}
581	return fb
582}
583
584// AddImportedDependency adds the given file as an explicit import. Normally,
585// dependencies can be inferred during the build process by finding the files
586// for all referenced types (such as message and enum types used in this file).
587// However, this does not work for custom options, which must be known in order
588// to be interpretable. And they aren't known unless an explicit import is added
589// for the file that contains the custom options.
590//
591// Knowledge of custom options can also be provided by using BuildOptions with
592// an ExtensionRegistry, when building the file.
593func (fb *FileBuilder) AddImportedDependency(dep *desc.FileDescriptor) *FileBuilder {
594	if fb.explicitImports == nil {
595		fb.explicitImports = map[*desc.FileDescriptor]struct{}{}
596	}
597	fb.explicitImports[dep] = struct{}{}
598	return fb
599}
600
601// SetOptions sets the file options for this file and returns the file, for
602// method chaining.
603func (fb *FileBuilder) SetOptions(options *dpb.FileOptions) *FileBuilder {
604	fb.Options = options
605	return fb
606}
607
608// SetPackageName sets the name of the package for this file and returns the
609// file, for method chaining.
610func (fb *FileBuilder) SetPackageName(pkg string) *FileBuilder {
611	fb.Package = pkg
612	return fb
613}
614
615// SetProto3 sets whether this file is declared to use "proto3" syntax or not
616// and returns the file, for method chaining.
617func (fb *FileBuilder) SetProto3(isProto3 bool) *FileBuilder {
618	fb.IsProto3 = isProto3
619	return fb
620}
621
622func (fb *FileBuilder) buildProto(deps []*desc.FileDescriptor) (*dpb.FileDescriptorProto, error) {
623	name := fb.name
624	if name == "" {
625		name = uniqueFileName()
626	}
627	var syntax *string
628	if fb.IsProto3 {
629		syntax = proto.String("proto3")
630	}
631	var pkg *string
632	if fb.Package != "" {
633		pkg = proto.String(fb.Package)
634	}
635
636	path := make([]int32, 0, 10)
637	sourceInfo := dpb.SourceCodeInfo{}
638	addCommentsTo(&sourceInfo, path, &fb.comments)
639	addCommentsTo(&sourceInfo, append(path, internal.File_syntaxTag), &fb.SyntaxComments)
640	addCommentsTo(&sourceInfo, append(path, internal.File_packageTag), &fb.PackageComments)
641
642	imports := make([]string, 0, len(deps))
643	for _, dep := range deps {
644		imports = append(imports, dep.GetName())
645	}
646	sort.Strings(imports)
647
648	messages := make([]*dpb.DescriptorProto, 0, len(fb.messages))
649	for _, mb := range fb.messages {
650		path := append(path, internal.File_messagesTag, int32(len(messages)))
651		if md, err := mb.buildProto(path, &sourceInfo); err != nil {
652			return nil, err
653		} else {
654			messages = append(messages, md)
655		}
656	}
657
658	enums := make([]*dpb.EnumDescriptorProto, 0, len(fb.enums))
659	for _, eb := range fb.enums {
660		path := append(path, internal.File_enumsTag, int32(len(enums)))
661		if ed, err := eb.buildProto(path, &sourceInfo); err != nil {
662			return nil, err
663		} else {
664			enums = append(enums, ed)
665		}
666	}
667
668	extensions := make([]*dpb.FieldDescriptorProto, 0, len(fb.extensions))
669	for _, exb := range fb.extensions {
670		path := append(path, internal.File_extensionsTag, int32(len(extensions)))
671		if exd, err := exb.buildProto(path, &sourceInfo); err != nil {
672			return nil, err
673		} else {
674			extensions = append(extensions, exd)
675		}
676	}
677
678	services := make([]*dpb.ServiceDescriptorProto, 0, len(fb.services))
679	for _, sb := range fb.services {
680		path := append(path, internal.File_servicesTag, int32(len(services)))
681		if sd, err := sb.buildProto(path, &sourceInfo); err != nil {
682			return nil, err
683		} else {
684			services = append(services, sd)
685		}
686	}
687
688	return &dpb.FileDescriptorProto{
689		Name:           proto.String(name),
690		Package:        pkg,
691		Dependency:     imports,
692		Options:        fb.Options,
693		Syntax:         syntax,
694		MessageType:    messages,
695		EnumType:       enums,
696		Extension:      extensions,
697		Service:        services,
698		SourceCodeInfo: &sourceInfo,
699	}, nil
700}
701
702// Build constructs a file descriptor based on the contents of this file
703// builder. If there are any problems constructing the descriptor, including
704// resolving symbols referenced by the builder or failing to meet certain
705// validation rules, an error is returned.
706func (fb *FileBuilder) Build() (*desc.FileDescriptor, error) {
707	fd, err := fb.BuildDescriptor()
708	if err != nil {
709		return nil, err
710	}
711	return fd.(*desc.FileDescriptor), nil
712}
713
714// BuildDescriptor constructs a file descriptor based on the contents of this
715// file builder. Most usages will prefer Build() instead, whose return type is a
716// concrete descriptor type. This method is present to satisfy the Builder
717// interface.
718func (fb *FileBuilder) BuildDescriptor() (desc.Descriptor, error) {
719	return doBuild(fb, BuilderOptions{})
720}
721