blob: d8f7f224d97c8d2c6dcb3d75c22f325071c8c4fb [file] [log] [blame]
Zack Williamse940c7a2019-08-21 14:25:39 -07001package protoprint
2
3import (
4 "bytes"
5 "fmt"
6 "io"
7 "math"
8 "os"
9 "path/filepath"
10 "reflect"
11 "sort"
12 "strings"
13
14 "github.com/golang/protobuf/proto"
15 "github.com/golang/protobuf/protoc-gen-go/descriptor"
16
17 "github.com/jhump/protoreflect/desc"
18 "github.com/jhump/protoreflect/desc/internal"
19 "github.com/jhump/protoreflect/dynamic"
20)
21
22// Printer knows how to format file descriptors as proto source code. Its fields
23// provide some control over how the resulting source file is constructed and
24// formatted.
25type Printer struct {
26 // If true, comments are rendered using "/*" style comments. Otherwise, they
27 // are printed using "//" style line comments.
28 PreferMultiLineStyleComments bool
29
30 // If true, elements are sorted into a canonical order.
31 //
32 // The canonical order for elements in a file follows:
33 // 1. Syntax
34 // 2. Package
35 // 3. Imports (sorted lexically)
36 // 4. Options (sorted by name, standard options before custom options)
37 // 5. Messages (sorted by name)
38 // 6. Enums (sorted by name)
39 // 7. Services (sorted by name)
40 // 8. Extensions (grouped by extendee, sorted by extendee+tag)
41 //
42 // The canonical order of elements in a message follows:
43 // 1. Options (sorted by name, standard options before custom options)
44 // 2. Fields and One-Ofs (sorted by tag; one-ofs interleaved based on the
45 // minimum tag therein)
46 // 3. Nested Messages (sorted by name)
47 // 4. Nested Enums (sorted by name)
48 // 5. Extension ranges (sorted by starting tag number)
49 // 6. Nested Extensions (grouped by extendee, sorted by extendee+tag)
50 // 7. Reserved ranges (sorted by starting tag number)
51 // 8. Reserved names (sorted lexically)
52 //
53 // Methods are sorted within a service by name and appear after any service
54 // options (which are sorted by name, standard options before custom ones).
55 // Enum values are sorted within an enum, first by numeric value then by
56 // name, and also appear after any enum options.
57 //
58 // Options for fields, enum values, and extension ranges are sorted by name,
59 // standard options before custom ones.
60 SortElements bool
61
62 // The indentation used. Any characters other than spaces or tabs will be
63 // replaced with spaces. If unset/empty, two spaces will be used.
64 Indent string
65
66 // If true, detached comments (between elements) will be ignored.
67 //
68 // Deprecated: Use OmitComments bitmask instead.
69 OmitDetachedComments bool
70
71 // A bitmask of comment types to omit. If unset, all comments will be
72 // included. Use CommentsAll to not print any comments.
73 OmitComments CommentType
74
75 // If true, trailing comments that typically appear on the same line as an
76 // element (option, field, enum value, method) will be printed on a separate
77 // line instead.
78 //
79 // So, with this set, you'll get output like so:
80 //
81 // // leading comment for field
82 // repeated string names = 1;
83 // // trailing comment
84 //
85 // If left false, the printer will try to emit trailing comments on the same
86 // line instead:
87 //
88 // // leading comment for field
89 // repeated string names = 1; // trailing comment
90 //
91 // If the trailing comment has more than one line, it will automatically be
92 // forced to the next line. Also, elements that end with "}" instead of ";"
93 // will have trailing comments rendered on the subsequent line.
94 TrailingCommentsOnSeparateLine bool
95
96 // If true, the printed output will eschew any blank lines, which otherwise
97 // appear between descriptor elements and comment blocks. Note that this if
98 // detached comments are being printed, this will cause them to be merged
99 // into the subsequent leading comments. Similarly, any element trailing
100 // comments will be merged into the subsequent leading comments.
101 Compact bool
102
103 // If true, all references to messages, extensions, and enums (such as in
104 // options, field types, and method request and response types) will be
105 // fully-qualified. When left unset, the referenced elements will contain
106 // only as much qualifier as is required.
107 //
108 // For example, if a message is in the same package as the reference, the
109 // simple name can be used. If a message shares some context with the
110 // reference, only the unshared context needs to be included. For example:
111 //
112 // message Foo {
113 // message Bar {
114 // enum Baz {
115 // ZERO = 0;
116 // ONE = 1;
117 // }
118 // }
119 //
120 // // This field shares some context as the enum it references: they are
121 // // both inside of the namespace Foo:
122 // // field is "Foo.my_baz"
123 // // enum is "Foo.Bar.Baz"
124 // // So we only need to qualify the reference with the context that they
125 // // do NOT have in common:
126 // Bar.Baz my_baz = 1;
127 // }
128 //
129 // When printing fully-qualified names, they will be preceded by a dot, to
130 // avoid any ambiguity that they might be relative vs. fully-qualified.
131 ForceFullyQualifiedNames bool
132}
133
134// CommentType is a kind of comments in a proto source file. This can be used
135// as a bitmask.
136type CommentType int
137
138const (
139 // CommentsDetached refers to comments that are not "attached" to any
140 // source element. They are attributed to the subsequent element in the
141 // file as "detached" comments.
142 CommentsDetached CommentType = 1 << iota
143 // CommentsTrailing refers to a comment block immediately following an
144 // element in the source file. If another element immediately follows
145 // the trailing comment, it is instead considered a leading comment for
146 // that subsequent element.
147 CommentsTrailing
148 // CommentsLeading refers to a comment block immediately preceding an
149 // element in the source file. For high-level elements (those that have
150 // their own descriptor), these are used as doc comments for that element.
151 CommentsLeading
152 // CommentsTokens refers to any comments (leading, trailing, or detached)
153 // on low-level elements in the file. "High-level" elements have their own
154 // descriptors, e.g. messages, enums, fields, services, and methods. But
155 // comments can appear anywhere (such as around identifiers and keywords,
156 // sprinkled inside the declarations of a high-level element). This class
157 // of comments are for those extra comments sprinkled into the file.
158 CommentsTokens
159
160 // CommentsNonDoc refers to comments that are *not* doc comments. This is a
161 // bitwise union of everything other than CommentsLeading. If you configure
162 // a printer to omit this, only doc comments on descriptor elements will be
163 // included in the printed output.
164 CommentsNonDoc = CommentsDetached | CommentsTrailing | CommentsTokens
165 // CommentsAll indicates all kinds of comments. If you configure a printer
166 // to omit this, no comments will appear in the printed output, even if the
167 // input descriptors had source info and comments.
168 CommentsAll = -1
169)
170
171// PrintProtoFiles prints all of the given file descriptors. The given open
172// function is given a file name and is responsible for creating the outputs and
173// returning the corresponding writer.
174func (p *Printer) PrintProtoFiles(fds []*desc.FileDescriptor, open func(name string) (io.WriteCloser, error)) error {
175 for _, fd := range fds {
176 w, err := open(fd.GetName())
177 if err != nil {
178 return fmt.Errorf("failed to open %s: %v", fd.GetName(), err)
179 }
180 err = func() error {
181 defer w.Close()
182 return p.PrintProtoFile(fd, w)
183 }()
184 if err != nil {
185 return fmt.Errorf("failed to write %s: %v", fd.GetName(), err)
186 }
187 }
188 return nil
189}
190
191// PrintProtosToFileSystem prints all of the given file descriptors to files in
192// the given directory. If file names in the given descriptors include path
193// information, they will be relative to the given root.
194func (p *Printer) PrintProtosToFileSystem(fds []*desc.FileDescriptor, rootDir string) error {
195 return p.PrintProtoFiles(fds, func(name string) (io.WriteCloser, error) {
196 fullPath := filepath.Join(rootDir, name)
197 dir := filepath.Dir(fullPath)
198 if err := os.MkdirAll(dir, os.ModePerm); err != nil {
199 return nil, err
200 }
201 return os.OpenFile(fullPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
202 })
203}
204
205// pkg represents a package name
206type pkg string
207
208// imp represents an imported file name
209type imp string
210
211// ident represents an identifier
212type ident string
213
214// option represents a resolved descriptor option
215type option struct {
216 name string
217 val interface{}
218}
219
220// reservedRange represents a reserved range from a message or enum
221type reservedRange struct {
222 start, end int32
223}
224
225// PrintProtoFile prints the given single file descriptor to the given writer.
226func (p *Printer) PrintProtoFile(fd *desc.FileDescriptor, out io.Writer) error {
227 return p.printProto(fd, out)
228}
229
230// PrintProto prints the given descriptor and returns the resulting string. This
231// can be used to print proto files, but it can also be used to get the proto
232// "source form" for any kind of descriptor, which can be a more user-friendly
233// way to present descriptors that are intended for human consumption.
234func (p *Printer) PrintProtoToString(dsc desc.Descriptor) (string, error) {
235 var buf bytes.Buffer
236 if err := p.printProto(dsc, &buf); err != nil {
237 return "", err
238 }
239 return buf.String(), nil
240}
241
242func (p *Printer) printProto(dsc desc.Descriptor, out io.Writer) error {
243 w := newWriter(out)
244
245 if p.Indent == "" {
246 // default indent to two spaces
247 p.Indent = " "
248 } else {
249 // indent must be all spaces or tabs, so convert other chars to spaces
250 ind := make([]rune, 0, len(p.Indent))
251 for _, r := range p.Indent {
252 if r == '\t' {
253 ind = append(ind, r)
254 } else {
255 ind = append(ind, ' ')
256 }
257 }
258 p.Indent = string(ind)
259 }
260 if p.OmitDetachedComments {
261 p.OmitComments |= CommentsDetached
262 }
263
264 er := dynamic.ExtensionRegistry{}
265 er.AddExtensionsFromFileRecursively(dsc.GetFile())
266 mf := dynamic.NewMessageFactoryWithExtensionRegistry(&er)
267 fdp := dsc.GetFile().AsFileDescriptorProto()
268 sourceInfo := internal.CreateSourceInfoMap(fdp)
269 extendOptionLocations(sourceInfo)
270
271 path := findElement(dsc)
272 switch d := dsc.(type) {
273 case *desc.FileDescriptor:
274 p.printFile(d, mf, w, sourceInfo)
275 case *desc.MessageDescriptor:
276 p.printMessage(d, mf, w, sourceInfo, path, 0)
277 case *desc.FieldDescriptor:
278 var scope string
279 if md, ok := d.GetParent().(*desc.MessageDescriptor); ok {
280 scope = md.GetFullyQualifiedName()
281 } else {
282 scope = d.GetFile().GetPackage()
283 }
284 if d.IsExtension() {
285 fmt.Fprint(w, "extend ")
286 extNameSi := sourceInfo.Get(append(path, internal.Field_extendeeTag))
287 p.printElementString(extNameSi, w, 0, p.qualifyName(d.GetFile().GetPackage(), scope, d.GetOwner().GetFullyQualifiedName()))
288 fmt.Fprintln(w, "{")
289
290 p.printField(d, mf, w, sourceInfo, path, scope, 1)
291
292 fmt.Fprintln(w, "}")
293 } else {
294 p.printField(d, mf, w, sourceInfo, path, scope, 0)
295 }
296 case *desc.OneOfDescriptor:
297 md := d.GetOwner()
298 elements := elementAddrs{dsc: md}
299 for i := range md.GetFields() {
300 elements.addrs = append(elements.addrs, elementAddr{elementType: internal.Message_fieldsTag, elementIndex: i})
301 }
302 p.printOneOf(d, elements, 0, mf, w, sourceInfo, path[:len(path)-1], 0, path[len(path)-1])
303 case *desc.EnumDescriptor:
304 p.printEnum(d, mf, w, sourceInfo, path, 0)
305 case *desc.EnumValueDescriptor:
306 p.printEnumValue(d, mf, w, sourceInfo, path, 0)
307 case *desc.ServiceDescriptor:
308 p.printService(d, mf, w, sourceInfo, path, 0)
309 case *desc.MethodDescriptor:
310 p.printMethod(d, mf, w, sourceInfo, path, 0)
311 }
312
313 return w.err
314}
315
316func findElement(dsc desc.Descriptor) []int32 {
317 if dsc.GetParent() == nil {
318 return nil
319 }
320 path := findElement(dsc.GetParent())
321 switch d := dsc.(type) {
322 case *desc.MessageDescriptor:
323 if pm, ok := d.GetParent().(*desc.MessageDescriptor); ok {
324 return append(path, internal.Message_nestedMessagesTag, getMessageIndex(d, pm.GetNestedMessageTypes()))
325 }
326 return append(path, internal.File_messagesTag, getMessageIndex(d, d.GetFile().GetMessageTypes()))
327
328 case *desc.FieldDescriptor:
329 if d.IsExtension() {
330 if pm, ok := d.GetParent().(*desc.MessageDescriptor); ok {
331 return append(path, internal.Message_extensionsTag, getFieldIndex(d, pm.GetNestedExtensions()))
332 }
333 return append(path, internal.File_extensionsTag, getFieldIndex(d, d.GetFile().GetExtensions()))
334 }
335 return append(path, internal.Message_fieldsTag, getFieldIndex(d, d.GetOwner().GetFields()))
336
337 case *desc.OneOfDescriptor:
338 return append(path, internal.Message_oneOfsTag, getOneOfIndex(d, d.GetOwner().GetOneOfs()))
339
340 case *desc.EnumDescriptor:
341 if pm, ok := d.GetParent().(*desc.MessageDescriptor); ok {
342 return append(path, internal.Message_enumsTag, getEnumIndex(d, pm.GetNestedEnumTypes()))
343 }
344 return append(path, internal.File_enumsTag, getEnumIndex(d, d.GetFile().GetEnumTypes()))
345
346 case *desc.EnumValueDescriptor:
347 return append(path, internal.Enum_valuesTag, getEnumValueIndex(d, d.GetEnum().GetValues()))
348
349 case *desc.ServiceDescriptor:
350 return append(path, internal.File_servicesTag, getServiceIndex(d, d.GetFile().GetServices()))
351
352 case *desc.MethodDescriptor:
353 return append(path, internal.Service_methodsTag, getMethodIndex(d, d.GetService().GetMethods()))
354
355 default:
356 panic(fmt.Sprintf("unexpected descriptor type: %T", dsc))
357 }
358}
359
360func getMessageIndex(md *desc.MessageDescriptor, list []*desc.MessageDescriptor) int32 {
361 for i := range list {
362 if md == list[i] {
363 return int32(i)
364 }
365 }
366 panic(fmt.Sprintf("unable to determine index of message %s", md.GetFullyQualifiedName()))
367}
368
369func getFieldIndex(fd *desc.FieldDescriptor, list []*desc.FieldDescriptor) int32 {
370 for i := range list {
371 if fd == list[i] {
372 return int32(i)
373 }
374 }
375 panic(fmt.Sprintf("unable to determine index of field %s", fd.GetFullyQualifiedName()))
376}
377
378func getOneOfIndex(ood *desc.OneOfDescriptor, list []*desc.OneOfDescriptor) int32 {
379 for i := range list {
380 if ood == list[i] {
381 return int32(i)
382 }
383 }
384 panic(fmt.Sprintf("unable to determine index of oneof %s", ood.GetFullyQualifiedName()))
385}
386
387func getEnumIndex(ed *desc.EnumDescriptor, list []*desc.EnumDescriptor) int32 {
388 for i := range list {
389 if ed == list[i] {
390 return int32(i)
391 }
392 }
393 panic(fmt.Sprintf("unable to determine index of enum %s", ed.GetFullyQualifiedName()))
394}
395
396func getEnumValueIndex(evd *desc.EnumValueDescriptor, list []*desc.EnumValueDescriptor) int32 {
397 for i := range list {
398 if evd == list[i] {
399 return int32(i)
400 }
401 }
402 panic(fmt.Sprintf("unable to determine index of enum value %s", evd.GetFullyQualifiedName()))
403}
404
405func getServiceIndex(sd *desc.ServiceDescriptor, list []*desc.ServiceDescriptor) int32 {
406 for i := range list {
407 if sd == list[i] {
408 return int32(i)
409 }
410 }
411 panic(fmt.Sprintf("unable to determine index of service %s", sd.GetFullyQualifiedName()))
412}
413
414func getMethodIndex(mtd *desc.MethodDescriptor, list []*desc.MethodDescriptor) int32 {
415 for i := range list {
416 if mtd == list[i] {
417 return int32(i)
418 }
419 }
420 panic(fmt.Sprintf("unable to determine index of method %s", mtd.GetFullyQualifiedName()))
421}
422
423func (p *Printer) newLine(w io.Writer) {
424 if !p.Compact {
425 fmt.Fprintln(w)
426 }
427}
428
429func (p *Printer) printFile(fd *desc.FileDescriptor, mf *dynamic.MessageFactory, w *writer, sourceInfo internal.SourceInfoMap) {
430 opts, err := p.extractOptions(fd, fd.GetOptions(), mf)
431 if err != nil {
432 return
433 }
434
435 fdp := fd.AsFileDescriptorProto()
436 path := make([]int32, 1)
437
438 path[0] = internal.File_packageTag
439 sourceInfo.PutIfAbsent(append(path, 0), sourceInfo.Get(path))
440
441 path[0] = internal.File_syntaxTag
442 si := sourceInfo.Get(path)
443 p.printElement(false, si, w, 0, func(w *writer) {
444 syn := fdp.GetSyntax()
445 if syn == "" {
446 syn = "proto2"
447 }
448 fmt.Fprintf(w, "syntax = %q;", syn)
449 })
450 p.newLine(w)
451
452 elements := elementAddrs{dsc: fd, opts: opts}
453 if fdp.Package != nil {
454 elements.addrs = append(elements.addrs, elementAddr{elementType: internal.File_packageTag, elementIndex: 0, order: -3})
455 }
456 for i := range fd.AsFileDescriptorProto().GetDependency() {
457 elements.addrs = append(elements.addrs, elementAddr{elementType: internal.File_dependencyTag, elementIndex: i, order: -2})
458 }
459 elements.addrs = append(elements.addrs, optionsAsElementAddrs(internal.File_optionsTag, -1, opts)...)
460 for i := range fd.GetMessageTypes() {
461 elements.addrs = append(elements.addrs, elementAddr{elementType: internal.File_messagesTag, elementIndex: i})
462 }
463 for i := range fd.GetEnumTypes() {
464 elements.addrs = append(elements.addrs, elementAddr{elementType: internal.File_enumsTag, elementIndex: i})
465 }
466 for i := range fd.GetServices() {
467 elements.addrs = append(elements.addrs, elementAddr{elementType: internal.File_servicesTag, elementIndex: i})
468 }
469 for i := range fd.GetExtensions() {
470 elements.addrs = append(elements.addrs, elementAddr{elementType: internal.File_extensionsTag, elementIndex: i})
471 }
472
473 p.sort(elements, sourceInfo, nil)
474
475 pkgName := fd.GetPackage()
476
477 var ext *desc.FieldDescriptor
478 for i, el := range elements.addrs {
479 d := elements.at(el)
480 path = []int32{el.elementType, int32(el.elementIndex)}
481 if el.elementType == internal.File_extensionsTag {
482 fld := d.(*desc.FieldDescriptor)
483 if ext == nil || ext.GetOwner() != fld.GetOwner() {
484 // need to open a new extend block
485 if ext != nil {
486 // close preceding extend block
487 fmt.Fprintln(w, "}")
488 }
489 if i > 0 {
490 p.newLine(w)
491 }
492
493 ext = fld
494 fmt.Fprint(w, "extend ")
495 extNameSi := sourceInfo.Get(append(path, internal.Field_extendeeTag))
496 p.printElementString(extNameSi, w, 0, p.qualifyName(pkgName, pkgName, fld.GetOwner().GetFullyQualifiedName()))
497 fmt.Fprintln(w, "{")
498 } else {
499 p.newLine(w)
500 }
501 p.printField(fld, mf, w, sourceInfo, path, pkgName, 1)
502 } else {
503 if ext != nil {
504 // close preceding extend block
505 fmt.Fprintln(w, "}")
506 ext = nil
507 }
508
509 if i > 0 {
510 p.newLine(w)
511 }
512
513 switch d := d.(type) {
514 case pkg:
515 si := sourceInfo.Get(path)
516 p.printElement(false, si, w, 0, func(w *writer) {
517 fmt.Fprintf(w, "package %s;", d)
518 })
519 case imp:
520 si := sourceInfo.Get(path)
521 p.printElement(false, si, w, 0, func(w *writer) {
522 fmt.Fprintf(w, "import %q;", d)
523 })
524 case []option:
525 p.printOptionsLong(d, w, sourceInfo, path, 0)
526 case *desc.MessageDescriptor:
527 p.printMessage(d, mf, w, sourceInfo, path, 0)
528 case *desc.EnumDescriptor:
529 p.printEnum(d, mf, w, sourceInfo, path, 0)
530 case *desc.ServiceDescriptor:
531 p.printService(d, mf, w, sourceInfo, path, 0)
532 }
533 }
534 }
535
536 if ext != nil {
537 // close trailing extend block
538 fmt.Fprintln(w, "}")
539 }
540}
541
542func (p *Printer) sort(elements elementAddrs, sourceInfo internal.SourceInfoMap, path []int32) {
543 if p.SortElements {
544 // canonical sorted order
545 sort.Stable(elements)
546 } else {
547 // use source order (per location information in SourceCodeInfo); or
548 // if that isn't present use declaration order, but grouped by type
549 sort.Stable(elementSrcOrder{
550 elementAddrs: elements,
551 sourceInfo: sourceInfo,
552 prefix: path,
553 })
554 }
555}
556
557func (p *Printer) qualifyName(pkg, scope string, fqn string) string {
558 if p.ForceFullyQualifiedNames {
559 // forcing fully-qualified names; make sure to include preceding dot
560 if fqn[0] == '.' {
561 return fqn
562 }
563 return fmt.Sprintf(".%s", fqn)
564 }
565
566 // compute relative name (so no leading dot)
567 if fqn[0] == '.' {
568 fqn = fqn[1:]
569 }
570 if len(scope) > 0 && scope[len(scope)-1] != '.' {
571 scope = scope + "."
572 }
573 for scope != "" {
574 if strings.HasPrefix(fqn, scope) {
575 return fqn[len(scope):]
576 }
577 if scope == pkg+"." {
578 break
579 }
580 pos := strings.LastIndex(scope[:len(scope)-1], ".")
581 scope = scope[:pos+1]
582 }
583 return fqn
584}
585
586func (p *Printer) typeString(fld *desc.FieldDescriptor, scope string) string {
587 if fld.IsMap() {
588 return fmt.Sprintf("map<%s, %s>", p.typeString(fld.GetMapKeyType(), scope), p.typeString(fld.GetMapValueType(), scope))
589 }
590 switch fld.GetType() {
591 case descriptor.FieldDescriptorProto_TYPE_INT32:
592 return "int32"
593 case descriptor.FieldDescriptorProto_TYPE_INT64:
594 return "int64"
595 case descriptor.FieldDescriptorProto_TYPE_UINT32:
596 return "uint32"
597 case descriptor.FieldDescriptorProto_TYPE_UINT64:
598 return "uint64"
599 case descriptor.FieldDescriptorProto_TYPE_SINT32:
600 return "sint32"
601 case descriptor.FieldDescriptorProto_TYPE_SINT64:
602 return "sint64"
603 case descriptor.FieldDescriptorProto_TYPE_FIXED32:
604 return "fixed32"
605 case descriptor.FieldDescriptorProto_TYPE_FIXED64:
606 return "fixed64"
607 case descriptor.FieldDescriptorProto_TYPE_SFIXED32:
608 return "sfixed32"
609 case descriptor.FieldDescriptorProto_TYPE_SFIXED64:
610 return "sfixed64"
611 case descriptor.FieldDescriptorProto_TYPE_FLOAT:
612 return "float"
613 case descriptor.FieldDescriptorProto_TYPE_DOUBLE:
614 return "double"
615 case descriptor.FieldDescriptorProto_TYPE_BOOL:
616 return "bool"
617 case descriptor.FieldDescriptorProto_TYPE_STRING:
618 return "string"
619 case descriptor.FieldDescriptorProto_TYPE_BYTES:
620 return "bytes"
621 case descriptor.FieldDescriptorProto_TYPE_ENUM:
622 return p.qualifyName(fld.GetFile().GetPackage(), scope, fld.GetEnumType().GetFullyQualifiedName())
623 case descriptor.FieldDescriptorProto_TYPE_MESSAGE:
624 return p.qualifyName(fld.GetFile().GetPackage(), scope, fld.GetMessageType().GetFullyQualifiedName())
625 case descriptor.FieldDescriptorProto_TYPE_GROUP:
626 return fld.GetMessageType().GetName()
627 }
628 panic(fmt.Sprintf("invalid type: %v", fld.GetType()))
629}
630
631func (p *Printer) printMessage(md *desc.MessageDescriptor, mf *dynamic.MessageFactory, w *writer, sourceInfo internal.SourceInfoMap, path []int32, indent int) {
632 si := sourceInfo.Get(path)
633 p.printElement(true, si, w, indent, func(w *writer) {
634 p.indent(w, indent)
635
636 fmt.Fprint(w, "message ")
637 nameSi := sourceInfo.Get(append(path, internal.Message_nameTag))
638 p.printElementString(nameSi, w, indent, md.GetName())
639 fmt.Fprintln(w, "{")
640
641 p.printMessageBody(md, mf, w, sourceInfo, path, indent+1)
642 p.indent(w, indent)
643 fmt.Fprintln(w, "}")
644 })
645}
646
647func (p *Printer) printMessageBody(md *desc.MessageDescriptor, mf *dynamic.MessageFactory, w *writer, sourceInfo internal.SourceInfoMap, path []int32, indent int) {
648 opts, err := p.extractOptions(md, md.GetOptions(), mf)
649 if err != nil {
650 if w.err == nil {
651 w.err = err
652 }
653 return
654 }
655
656 skip := map[interface{}]bool{}
657
658 elements := elementAddrs{dsc: md, opts: opts}
659 elements.addrs = append(elements.addrs, optionsAsElementAddrs(internal.Message_optionsTag, -1, opts)...)
660 for i := range md.AsDescriptorProto().GetReservedRange() {
661 elements.addrs = append(elements.addrs, elementAddr{elementType: internal.Message_reservedRangeTag, elementIndex: i})
662 }
663 for i := range md.AsDescriptorProto().GetReservedName() {
664 elements.addrs = append(elements.addrs, elementAddr{elementType: internal.Message_reservedNameTag, elementIndex: i})
665 }
666 for i := range md.AsDescriptorProto().GetExtensionRange() {
667 elements.addrs = append(elements.addrs, elementAddr{elementType: internal.Message_extensionRangeTag, elementIndex: i})
668 }
669 for i, fld := range md.GetFields() {
670 if fld.IsMap() || fld.GetType() == descriptor.FieldDescriptorProto_TYPE_GROUP {
671 // we don't emit nested messages for map types or groups since
672 // they get special treatment
673 skip[fld.GetMessageType()] = true
674 }
675 elements.addrs = append(elements.addrs, elementAddr{elementType: internal.Message_fieldsTag, elementIndex: i})
676 }
677 for i := range md.GetNestedMessageTypes() {
678 elements.addrs = append(elements.addrs, elementAddr{elementType: internal.Message_nestedMessagesTag, elementIndex: i})
679 }
680 for i := range md.GetNestedEnumTypes() {
681 elements.addrs = append(elements.addrs, elementAddr{elementType: internal.Message_enumsTag, elementIndex: i})
682 }
683 for i := range md.GetNestedExtensions() {
684 elements.addrs = append(elements.addrs, elementAddr{elementType: internal.Message_extensionsTag, elementIndex: i})
685 }
686
687 p.sort(elements, sourceInfo, path)
688
689 pkg := md.GetFile().GetPackage()
690 scope := md.GetFullyQualifiedName()
691
692 var ext *desc.FieldDescriptor
693 for i, el := range elements.addrs {
694 d := elements.at(el)
695 // skip[d] will panic if d is a slice (which it could be for []option),
696 // so just ignore it since we don't try to skip options
697 if reflect.TypeOf(d).Kind() != reflect.Slice && skip[d] {
698 // skip this element
699 continue
700 }
701
702 childPath := append(path, el.elementType, int32(el.elementIndex))
703 if el.elementType == internal.Message_extensionsTag {
704 // extension
705 fld := d.(*desc.FieldDescriptor)
706 if ext == nil || ext.GetOwner() != fld.GetOwner() {
707 // need to open a new extend block
708 if ext != nil {
709 // close preceding extend block
710 p.indent(w, indent)
711 fmt.Fprintln(w, "}")
712 }
713 if i > 0 {
714 p.newLine(w)
715 }
716
717 ext = fld
718 p.indent(w, indent)
719 fmt.Fprint(w, "extend ")
720 extNameSi := sourceInfo.Get(append(childPath, internal.Field_extendeeTag))
721 p.printElementString(extNameSi, w, indent, p.qualifyName(pkg, scope, fld.GetOwner().GetFullyQualifiedName()))
722 fmt.Fprintln(w, "{")
723 } else {
724 p.newLine(w)
725 }
726 p.printField(fld, mf, w, sourceInfo, childPath, scope, indent+1)
727 } else {
728 if ext != nil {
729 // close preceding extend block
730 p.indent(w, indent)
731 fmt.Fprintln(w, "}")
732 ext = nil
733 }
734
735 if i > 0 {
736 p.newLine(w)
737 }
738
739 switch d := d.(type) {
740 case []option:
741 p.printOptionsLong(d, w, sourceInfo, childPath, indent)
742 case *desc.FieldDescriptor:
743 ood := d.GetOneOf()
744 if ood == nil {
745 p.printField(d, mf, w, sourceInfo, childPath, scope, indent)
746 } else if !skip[ood] {
747 // print the one-of, including all of its fields
748 p.printOneOf(ood, elements, i, mf, w, sourceInfo, path, indent, d.AsFieldDescriptorProto().GetOneofIndex())
749 skip[ood] = true
750 }
751 case *desc.MessageDescriptor:
752 p.printMessage(d, mf, w, sourceInfo, childPath, indent)
753 case *desc.EnumDescriptor:
754 p.printEnum(d, mf, w, sourceInfo, childPath, indent)
755 case *descriptor.DescriptorProto_ExtensionRange:
756 // collapse ranges into a single "extensions" block
757 ranges := []*descriptor.DescriptorProto_ExtensionRange{d}
758 addrs := []elementAddr{el}
759 for idx := i + 1; idx < len(elements.addrs); idx++ {
760 elnext := elements.addrs[idx]
761 if elnext.elementType != el.elementType {
762 break
763 }
764 extr := elements.at(elnext).(*descriptor.DescriptorProto_ExtensionRange)
765 if !areEqual(d.Options, extr.Options, mf) {
766 break
767 }
768 ranges = append(ranges, extr)
769 addrs = append(addrs, elnext)
770 skip[extr] = true
771 }
772 p.printExtensionRanges(md, ranges, addrs, mf, w, sourceInfo, path, indent)
773 case reservedRange:
774 // collapse reserved ranges into a single "reserved" block
775 ranges := []reservedRange{d}
776 addrs := []elementAddr{el}
777 for idx := i + 1; idx < len(elements.addrs); idx++ {
778 elnext := elements.addrs[idx]
779 if elnext.elementType != el.elementType {
780 break
781 }
782 rr := elements.at(elnext).(reservedRange)
783 ranges = append(ranges, rr)
784 addrs = append(addrs, elnext)
785 skip[rr] = true
786 }
787 p.printReservedRanges(ranges, false, addrs, w, sourceInfo, path, indent)
788 case string: // reserved name
789 // collapse reserved names into a single "reserved" block
790 names := []string{d}
791 addrs := []elementAddr{el}
792 for idx := i + 1; idx < len(elements.addrs); idx++ {
793 elnext := elements.addrs[idx]
794 if elnext.elementType != el.elementType {
795 break
796 }
797 rn := elements.at(elnext).(string)
798 names = append(names, rn)
799 addrs = append(addrs, elnext)
800 skip[rn] = true
801 }
802 p.printReservedNames(names, addrs, w, sourceInfo, path, indent)
803 }
804 }
805 }
806
807 if ext != nil {
808 // close trailing extend block
809 p.indent(w, indent)
810 fmt.Fprintln(w, "}")
811 }
812}
813
814func areEqual(a, b proto.Message, mf *dynamic.MessageFactory) bool {
815 // proto.Equal doesn't handle unknown extensions very well :(
816 // so we convert to a dynamic message (which should know about all extensions via
817 // extension registry) and then compare
818 return dynamic.MessagesEqual(asDynamicIfPossible(a, mf), asDynamicIfPossible(b, mf))
819}
820
821func asDynamicIfPossible(msg proto.Message, mf *dynamic.MessageFactory) proto.Message {
822 if dm, ok := msg.(*dynamic.Message); ok {
823 return dm
824 } else {
825 md, err := desc.LoadMessageDescriptorForMessage(msg)
826 if err == nil {
827 dm := mf.NewDynamicMessage(md)
828 if dm.ConvertFrom(msg) == nil {
829 return dm
830 }
831 }
832 }
833 return msg
834}
835
836func (p *Printer) printField(fld *desc.FieldDescriptor, mf *dynamic.MessageFactory, w *writer, sourceInfo internal.SourceInfoMap, path []int32, scope string, indent int) {
837 var groupPath []int32
838 var si *descriptor.SourceCodeInfo_Location
839 if isGroup(fld) {
840 // compute path to group message type
841 groupPath = make([]int32, len(path)-2)
842 copy(groupPath, path)
843 var groupMsgIndex int32
844 md := fld.GetParent().(*desc.MessageDescriptor)
845 for i, nmd := range md.GetNestedMessageTypes() {
846 if nmd == fld.GetMessageType() {
847 // found it
848 groupMsgIndex = int32(i)
849 break
850 }
851 }
852 groupPath = append(groupPath, internal.Message_nestedMessagesTag, groupMsgIndex)
853
854 // the group message is where the field's comments and position are stored
855 si = sourceInfo.Get(groupPath)
856 } else {
857 si = sourceInfo.Get(path)
858 }
859
860 p.printElement(true, si, w, indent, func(w *writer) {
861 p.indent(w, indent)
862 if shouldEmitLabel(fld) {
863 locSi := sourceInfo.Get(append(path, internal.Field_labelTag))
864 p.printElementString(locSi, w, indent, labelString(fld.GetLabel()))
865 }
866
867 if isGroup(fld) {
868 fmt.Fprint(w, "group ")
869
870 typeSi := sourceInfo.Get(append(path, internal.Field_typeTag))
871 p.printElementString(typeSi, w, indent, p.typeString(fld, scope))
872 fmt.Fprint(w, "= ")
873
874 numSi := sourceInfo.Get(append(path, internal.Field_numberTag))
875 p.printElementString(numSi, w, indent, fmt.Sprintf("%d", fld.GetNumber()))
876
877 fmt.Fprintln(w, "{")
878 p.printMessageBody(fld.GetMessageType(), mf, w, sourceInfo, groupPath, indent+1)
879
880 p.indent(w, indent)
881 fmt.Fprintln(w, "}")
882 } else {
883 typeSi := sourceInfo.Get(append(path, internal.Field_typeTag))
884 p.printElementString(typeSi, w, indent, p.typeString(fld, scope))
885
886 nameSi := sourceInfo.Get(append(path, internal.Field_nameTag))
887 p.printElementString(nameSi, w, indent, fld.GetName())
888 fmt.Fprint(w, "= ")
889
890 numSi := sourceInfo.Get(append(path, internal.Field_numberTag))
891 p.printElementString(numSi, w, indent, fmt.Sprintf("%d", fld.GetNumber()))
892
893 opts, err := p.extractOptions(fld, fld.GetOptions(), mf)
894 if err != nil {
895 if w.err == nil {
896 w.err = err
897 }
898 return
899 }
900
901 // we use negative values for "extras" keys so they can't collide
902 // with legit option tags
903
904 if !fld.GetFile().IsProto3() && fld.AsFieldDescriptorProto().DefaultValue != nil {
905 defVal := fld.GetDefaultValue()
906 if fld.GetEnumType() != nil {
907 defVal = fld.GetEnumType().FindValueByNumber(defVal.(int32))
908 }
909 opts[-internal.Field_defaultTag] = []option{{name: "default", val: defVal}}
910 }
911
912 jsn := fld.AsFieldDescriptorProto().GetJsonName()
913 if jsn != "" && jsn != internal.JsonName(fld.GetName()) {
914 opts[-internal.Field_jsonNameTag] = []option{{name: "json_name", val: jsn}}
915 }
916
917 elements := elementAddrs{dsc: fld, opts: opts}
918 elements.addrs = optionsAsElementAddrs(internal.Field_optionsTag, 0, opts)
919 p.sort(elements, sourceInfo, path)
920 p.printOptionElementsShort(elements, w, sourceInfo, path, indent)
921
922 fmt.Fprint(w, ";")
923 }
924 })
925}
926
927func shouldEmitLabel(fld *desc.FieldDescriptor) bool {
928 return !fld.IsMap() && fld.GetOneOf() == nil && (fld.GetLabel() != descriptor.FieldDescriptorProto_LABEL_OPTIONAL || !fld.GetFile().IsProto3())
929}
930
931func labelString(lbl descriptor.FieldDescriptorProto_Label) string {
932 switch lbl {
933 case descriptor.FieldDescriptorProto_LABEL_OPTIONAL:
934 return "optional"
935 case descriptor.FieldDescriptorProto_LABEL_REQUIRED:
936 return "required"
937 case descriptor.FieldDescriptorProto_LABEL_REPEATED:
938 return "repeated"
939 }
940 panic(fmt.Sprintf("invalid label: %v", lbl))
941}
942
943func isGroup(fld *desc.FieldDescriptor) bool {
944 return fld.GetType() == descriptor.FieldDescriptorProto_TYPE_GROUP
945}
946
947func (p *Printer) printOneOf(ood *desc.OneOfDescriptor, parentElements elementAddrs, startFieldIndex int, mf *dynamic.MessageFactory, w *writer, sourceInfo internal.SourceInfoMap, parentPath []int32, indent int, ooIndex int32) {
948 oopath := append(parentPath, internal.Message_oneOfsTag, ooIndex)
949 oosi := sourceInfo.Get(oopath)
950 p.printElement(true, oosi, w, indent, func(w *writer) {
951 p.indent(w, indent)
952 fmt.Fprint(w, "oneof ")
953 extNameSi := sourceInfo.Get(append(oopath, internal.OneOf_nameTag))
954 p.printElementString(extNameSi, w, indent, ood.GetName())
955 fmt.Fprintln(w, "{")
956
957 indent++
958 opts, err := p.extractOptions(ood, ood.GetOptions(), mf)
959 if err != nil {
960 if w.err == nil {
961 w.err = err
962 }
963 return
964 }
965
966 elements := elementAddrs{dsc: ood, opts: opts}
967 elements.addrs = append(elements.addrs, optionsAsElementAddrs(internal.OneOf_optionsTag, -1, opts)...)
968
969 count := len(ood.GetChoices())
970 for idx := startFieldIndex; count > 0 && idx < len(parentElements.addrs); idx++ {
971 el := parentElements.addrs[idx]
972 if el.elementType != internal.Message_fieldsTag {
973 continue
974 }
975 if parentElements.at(el).(*desc.FieldDescriptor).GetOneOf() == ood {
976 // negative tag indicates that this element is actually a sibling, not a child
977 elements.addrs = append(elements.addrs, elementAddr{elementType: -internal.Message_fieldsTag, elementIndex: el.elementIndex})
978 count--
979 }
980 }
981
982 p.sort(elements, sourceInfo, oopath)
983
984 scope := ood.GetOwner().GetFullyQualifiedName()
985
986 for i, el := range elements.addrs {
987 if i > 0 {
988 p.newLine(w)
989 }
990
991 switch d := elements.at(el).(type) {
992 case []option:
993 childPath := append(oopath, el.elementType, int32(el.elementIndex))
994 p.printOptionsLong(d, w, sourceInfo, childPath, indent)
995 case *desc.FieldDescriptor:
996 childPath := append(parentPath, -el.elementType, int32(el.elementIndex))
997 p.printField(d, mf, w, sourceInfo, childPath, scope, indent)
998 }
999 }
1000
1001 p.indent(w, indent-1)
1002 fmt.Fprintln(w, "}")
1003 })
1004}
1005
1006func (p *Printer) printExtensionRanges(parent *desc.MessageDescriptor, ranges []*descriptor.DescriptorProto_ExtensionRange, addrs []elementAddr, mf *dynamic.MessageFactory, w *writer, sourceInfo internal.SourceInfoMap, parentPath []int32, indent int) {
1007 p.indent(w, indent)
1008 fmt.Fprint(w, "extensions ")
1009
1010 var opts *descriptor.ExtensionRangeOptions
1011 var elPath []int32
1012 first := true
1013 for i, extr := range ranges {
1014 if first {
1015 first = false
1016 } else {
1017 fmt.Fprint(w, ", ")
1018 }
1019 opts = extr.Options
1020 el := addrs[i]
1021 elPath = append(parentPath, el.elementType, int32(el.elementIndex))
1022 si := sourceInfo.Get(elPath)
1023 p.printElement(true, si, w, inline(indent), func(w *writer) {
1024 if extr.GetStart() == extr.GetEnd()-1 {
1025 fmt.Fprintf(w, "%d ", extr.GetStart())
1026 } else if extr.GetEnd()-1 == internal.MaxTag {
1027 fmt.Fprintf(w, "%d to max ", extr.GetStart())
1028 } else {
1029 fmt.Fprintf(w, "%d to %d ", extr.GetStart(), extr.GetEnd()-1)
1030 }
1031 })
1032 }
1033 dsc := extensionRange{owner: parent, extRange: ranges[0]}
1034 p.printOptionsShort(dsc, opts, mf, internal.ExtensionRange_optionsTag, w, sourceInfo, elPath, indent)
1035
1036 fmt.Fprintln(w, ";")
1037}
1038
1039func (p *Printer) printReservedRanges(ranges []reservedRange, isEnum bool, addrs []elementAddr, w *writer, sourceInfo internal.SourceInfoMap, parentPath []int32, indent int) {
1040 p.indent(w, indent)
1041 fmt.Fprint(w, "reserved ")
1042
1043 first := true
1044 for i, rr := range ranges {
1045 if first {
1046 first = false
1047 } else {
1048 fmt.Fprint(w, ", ")
1049 }
1050 el := addrs[i]
1051 si := sourceInfo.Get(append(parentPath, el.elementType, int32(el.elementIndex)))
1052 p.printElement(false, si, w, inline(indent), func(w *writer) {
1053 if rr.start == rr.end {
1054 fmt.Fprintf(w, "%d ", rr.start)
1055 } else if (rr.end == internal.MaxTag && !isEnum) ||
1056 (rr.end == math.MaxInt32 && isEnum) {
1057 fmt.Fprintf(w, "%d to max ", rr.start)
1058 } else {
1059 fmt.Fprintf(w, "%d to %d ", rr.start, rr.end)
1060 }
1061 })
1062 }
1063
1064 fmt.Fprintln(w, ";")
1065}
1066
1067func (p *Printer) printReservedNames(names []string, addrs []elementAddr, w *writer, sourceInfo internal.SourceInfoMap, parentPath []int32, indent int) {
1068 p.indent(w, indent)
1069 fmt.Fprint(w, "reserved ")
1070
1071 first := true
1072 for i, name := range names {
1073 if first {
1074 first = false
1075 } else {
1076 fmt.Fprint(w, ", ")
1077 }
1078 el := addrs[i]
1079 si := sourceInfo.Get(append(parentPath, el.elementType, int32(el.elementIndex)))
1080 p.printElementString(si, w, indent, quotedString(name))
1081 }
1082
1083 fmt.Fprintln(w, ";")
1084}
1085
1086func (p *Printer) printEnum(ed *desc.EnumDescriptor, mf *dynamic.MessageFactory, w *writer, sourceInfo internal.SourceInfoMap, path []int32, indent int) {
1087 si := sourceInfo.Get(path)
1088 p.printElement(true, si, w, indent, func(w *writer) {
1089 p.indent(w, indent)
1090
1091 fmt.Fprint(w, "enum ")
1092 nameSi := sourceInfo.Get(append(path, internal.Enum_nameTag))
1093 p.printElementString(nameSi, w, indent, ed.GetName())
1094 fmt.Fprintln(w, "{")
1095
1096 indent++
1097 opts, err := p.extractOptions(ed, ed.GetOptions(), mf)
1098 if err != nil {
1099 if w.err == nil {
1100 w.err = err
1101 }
1102 return
1103 }
1104
1105 skip := map[interface{}]bool{}
1106
1107 elements := elementAddrs{dsc: ed, opts: opts}
1108 elements.addrs = append(elements.addrs, optionsAsElementAddrs(internal.Enum_optionsTag, -1, opts)...)
1109 for i := range ed.GetValues() {
1110 elements.addrs = append(elements.addrs, elementAddr{elementType: internal.Enum_valuesTag, elementIndex: i})
1111 }
1112 for i := range ed.AsEnumDescriptorProto().GetReservedRange() {
1113 elements.addrs = append(elements.addrs, elementAddr{elementType: internal.Enum_reservedRangeTag, elementIndex: i})
1114 }
1115 for i := range ed.AsEnumDescriptorProto().GetReservedName() {
1116 elements.addrs = append(elements.addrs, elementAddr{elementType: internal.Enum_reservedNameTag, elementIndex: i})
1117 }
1118
1119 p.sort(elements, sourceInfo, path)
1120
1121 for i, el := range elements.addrs {
1122 d := elements.at(el)
1123
1124 // skip[d] will panic if d is a slice (which it could be for []option),
1125 // so just ignore it since we don't try to skip options
1126 if reflect.TypeOf(d).Kind() != reflect.Slice && skip[d] {
1127 // skip this element
1128 continue
1129 }
1130
1131 if i > 0 {
1132 p.newLine(w)
1133 }
1134
1135 childPath := append(path, el.elementType, int32(el.elementIndex))
1136
1137 switch d := d.(type) {
1138 case []option:
1139 p.printOptionsLong(d, w, sourceInfo, childPath, indent)
1140 case *desc.EnumValueDescriptor:
1141 p.printEnumValue(d, mf, w, sourceInfo, childPath, indent)
1142 case reservedRange:
1143 // collapse reserved ranges into a single "reserved" block
1144 ranges := []reservedRange{d}
1145 addrs := []elementAddr{el}
1146 for idx := i + 1; idx < len(elements.addrs); idx++ {
1147 elnext := elements.addrs[idx]
1148 if elnext.elementType != el.elementType {
1149 break
1150 }
1151 rr := elements.at(elnext).(reservedRange)
1152 ranges = append(ranges, rr)
1153 addrs = append(addrs, elnext)
1154 skip[rr] = true
1155 }
1156 p.printReservedRanges(ranges, true, addrs, w, sourceInfo, path, indent)
1157 case string: // reserved name
1158 // collapse reserved names into a single "reserved" block
1159 names := []string{d}
1160 addrs := []elementAddr{el}
1161 for idx := i + 1; idx < len(elements.addrs); idx++ {
1162 elnext := elements.addrs[idx]
1163 if elnext.elementType != el.elementType {
1164 break
1165 }
1166 rn := elements.at(elnext).(string)
1167 names = append(names, rn)
1168 addrs = append(addrs, elnext)
1169 skip[rn] = true
1170 }
1171 p.printReservedNames(names, addrs, w, sourceInfo, path, indent)
1172 }
1173 }
1174
1175 p.indent(w, indent-1)
1176 fmt.Fprintln(w, "}")
1177 })
1178}
1179
1180func (p *Printer) printEnumValue(evd *desc.EnumValueDescriptor, mf *dynamic.MessageFactory, w *writer, sourceInfo internal.SourceInfoMap, path []int32, indent int) {
1181 si := sourceInfo.Get(path)
1182 p.printElement(true, si, w, indent, func(w *writer) {
1183 p.indent(w, indent)
1184
1185 nameSi := sourceInfo.Get(append(path, internal.EnumVal_nameTag))
1186 p.printElementString(nameSi, w, indent, evd.GetName())
1187 fmt.Fprint(w, "= ")
1188
1189 numSi := sourceInfo.Get(append(path, internal.EnumVal_numberTag))
1190 p.printElementString(numSi, w, indent, fmt.Sprintf("%d", evd.GetNumber()))
1191
1192 p.printOptionsShort(evd, evd.GetOptions(), mf, internal.EnumVal_optionsTag, w, sourceInfo, path, indent)
1193
1194 fmt.Fprint(w, ";")
1195 })
1196}
1197
1198func (p *Printer) printService(sd *desc.ServiceDescriptor, mf *dynamic.MessageFactory, w *writer, sourceInfo internal.SourceInfoMap, path []int32, indent int) {
1199 si := sourceInfo.Get(path)
1200 p.printElement(true, si, w, indent, func(w *writer) {
1201 p.indent(w, indent)
1202
1203 fmt.Fprint(w, "service ")
1204 nameSi := sourceInfo.Get(append(path, internal.Service_nameTag))
1205 p.printElementString(nameSi, w, indent, sd.GetName())
1206 fmt.Fprintln(w, "{")
1207
1208 indent++
1209
1210 opts, err := p.extractOptions(sd, sd.GetOptions(), mf)
1211 if err != nil {
1212 if w.err == nil {
1213 w.err = err
1214 }
1215 return
1216 }
1217
1218 elements := elementAddrs{dsc: sd, opts: opts}
1219 elements.addrs = append(elements.addrs, optionsAsElementAddrs(internal.Service_optionsTag, -1, opts)...)
1220 for i := range sd.GetMethods() {
1221 elements.addrs = append(elements.addrs, elementAddr{elementType: internal.Service_methodsTag, elementIndex: i})
1222 }
1223
1224 p.sort(elements, sourceInfo, path)
1225
1226 for i, el := range elements.addrs {
1227 if i > 0 {
1228 p.newLine(w)
1229 }
1230
1231 childPath := append(path, el.elementType, int32(el.elementIndex))
1232
1233 switch d := elements.at(el).(type) {
1234 case []option:
1235 p.printOptionsLong(d, w, sourceInfo, childPath, indent)
1236 case *desc.MethodDescriptor:
1237 p.printMethod(d, mf, w, sourceInfo, childPath, indent)
1238 }
1239 }
1240
1241 p.indent(w, indent-1)
1242 fmt.Fprintln(w, "}")
1243 })
1244}
1245
1246func (p *Printer) printMethod(mtd *desc.MethodDescriptor, mf *dynamic.MessageFactory, w *writer, sourceInfo internal.SourceInfoMap, path []int32, indent int) {
1247 si := sourceInfo.Get(path)
1248 pkg := mtd.GetFile().GetPackage()
1249 p.printElement(true, si, w, indent, func(w *writer) {
1250 p.indent(w, indent)
1251
1252 fmt.Fprint(w, "rpc ")
1253 nameSi := sourceInfo.Get(append(path, internal.Method_nameTag))
1254 p.printElementString(nameSi, w, indent, mtd.GetName())
1255
1256 fmt.Fprint(w, "( ")
1257 inSi := sourceInfo.Get(append(path, internal.Method_inputTag))
1258 inName := p.qualifyName(pkg, pkg, mtd.GetInputType().GetFullyQualifiedName())
1259 if mtd.IsClientStreaming() {
1260 inName = "stream " + inName
1261 }
1262 p.printElementString(inSi, w, indent, inName)
1263
1264 fmt.Fprint(w, ") returns ( ")
1265
1266 outSi := sourceInfo.Get(append(path, internal.Method_outputTag))
1267 outName := p.qualifyName(pkg, pkg, mtd.GetOutputType().GetFullyQualifiedName())
1268 if mtd.IsServerStreaming() {
1269 outName = "stream " + outName
1270 }
1271 p.printElementString(outSi, w, indent, outName)
1272 fmt.Fprint(w, ") ")
1273
1274 opts, err := p.extractOptions(mtd, mtd.GetOptions(), mf)
1275 if err != nil {
1276 if w.err == nil {
1277 w.err = err
1278 }
1279 return
1280 }
1281
1282 if len(opts) > 0 {
1283 fmt.Fprintln(w, "{")
1284 indent++
1285
1286 elements := elementAddrs{dsc: mtd, opts: opts}
1287 elements.addrs = optionsAsElementAddrs(internal.Method_optionsTag, 0, opts)
1288 p.sort(elements, sourceInfo, path)
1289 path = append(path, internal.Method_optionsTag)
1290
1291 for i, addr := range elements.addrs {
1292 if i > 0 {
1293 p.newLine(w)
1294 }
1295 o := elements.at(addr).([]option)
1296 p.printOptionsLong(o, w, sourceInfo, path, indent)
1297 }
1298
1299 p.indent(w, indent-1)
1300 fmt.Fprintln(w, "}")
1301 } else {
1302 fmt.Fprint(w, ";")
1303 }
1304 })
1305}
1306
1307func (p *Printer) printOptionsLong(opts []option, w *writer, sourceInfo internal.SourceInfoMap, path []int32, indent int) {
1308 p.printOptions(opts, w, indent,
1309 func(i int32) *descriptor.SourceCodeInfo_Location {
1310 return sourceInfo.Get(append(path, i))
1311 },
1312 func(w *writer, indent int, opt option) {
1313 p.indent(w, indent)
1314 fmt.Fprint(w, "option ")
1315 p.printOption(opt.name, opt.val, w, indent)
1316 fmt.Fprint(w, ";")
1317 })
1318}
1319
1320func (p *Printer) printOptionsShort(dsc interface{}, optsMsg proto.Message, mf *dynamic.MessageFactory, optsTag int32, w *writer, sourceInfo internal.SourceInfoMap, path []int32, indent int) {
1321 d, ok := dsc.(desc.Descriptor)
1322 if !ok {
1323 d = dsc.(extensionRange).owner
1324 }
1325 opts, err := p.extractOptions(d, optsMsg, mf)
1326 if err != nil {
1327 if w.err == nil {
1328 w.err = err
1329 }
1330 return
1331 }
1332
1333 elements := elementAddrs{dsc: dsc, opts: opts}
1334 elements.addrs = optionsAsElementAddrs(optsTag, 0, opts)
1335 p.sort(elements, sourceInfo, path)
1336 p.printOptionElementsShort(elements, w, sourceInfo, path, indent)
1337}
1338
1339func (p *Printer) printOptionElementsShort(addrs elementAddrs, w *writer, sourceInfo internal.SourceInfoMap, path []int32, indent int) {
1340 if len(addrs.addrs) == 0 {
1341 return
1342 }
1343 first := true
1344 fmt.Fprint(w, "[")
1345 for _, addr := range addrs.addrs {
1346 opts := addrs.at(addr).([]option)
1347 var childPath []int32
1348 if addr.elementIndex < 0 {
1349 // pseudo-option
1350 childPath = append(path, int32(-addr.elementIndex))
1351 } else {
1352 childPath = append(path, addr.elementType, int32(addr.elementIndex))
1353 }
1354 p.printOptions(opts, w, inline(indent),
1355 func(i int32) *descriptor.SourceCodeInfo_Location {
1356 p := childPath
1357 if addr.elementIndex >= 0 {
1358 p = append(p, i)
1359 }
1360 return sourceInfo.Get(p)
1361 },
1362 func(w *writer, indent int, opt option) {
1363 if first {
1364 first = false
1365 } else {
1366 fmt.Fprint(w, ", ")
1367 }
1368 p.printOption(opt.name, opt.val, w, indent)
1369 fmt.Fprint(w, " ") // trailing space
1370 })
1371 }
1372 fmt.Fprint(w, "]")
1373}
1374
1375func (p *Printer) printOptions(opts []option, w *writer, indent int, siFetch func(i int32) *descriptor.SourceCodeInfo_Location, fn func(w *writer, indent int, opt option)) {
1376 for i, opt := range opts {
1377 si := siFetch(int32(i))
1378 p.printElement(false, si, w, indent, func(w *writer) {
1379 fn(w, indent, opt)
1380 })
1381 }
1382}
1383
1384func inline(indent int) int {
1385 if indent < 0 {
1386 // already inlined
1387 return indent
1388 }
1389 // negative indent means inline; indent 2 stops further in case value wraps
1390 return -indent - 2
1391}
1392
1393func sortKeys(m map[interface{}]interface{}) []interface{} {
1394 res := make(sortedKeys, len(m))
1395 i := 0
1396 for k := range m {
1397 res[i] = k
1398 i++
1399 }
1400 sort.Sort(res)
1401 return ([]interface{})(res)
1402}
1403
1404type sortedKeys []interface{}
1405
1406func (k sortedKeys) Len() int {
1407 return len(k)
1408}
1409
1410func (k sortedKeys) Swap(i, j int) {
1411 k[i], k[j] = k[j], k[i]
1412}
1413
1414func (k sortedKeys) Less(i, j int) bool {
1415 switch i := k[i].(type) {
1416 case int32:
1417 return i < k[j].(int32)
1418 case uint32:
1419 return i < k[j].(uint32)
1420 case int64:
1421 return i < k[j].(int64)
1422 case uint64:
1423 return i < k[j].(uint64)
1424 case string:
1425 return i < k[j].(string)
1426 case bool:
1427 return !i && k[j].(bool)
1428 default:
1429 panic(fmt.Sprintf("invalid type for map key: %T", i))
1430 }
1431}
1432
1433func (p *Printer) printOption(name string, optVal interface{}, w *writer, indent int) {
1434 fmt.Fprintf(w, "%s = ", name)
1435
1436 switch optVal := optVal.(type) {
1437 case int32, uint32, int64, uint64:
1438 fmt.Fprintf(w, "%d", optVal)
1439 case float32, float64:
1440 fmt.Fprintf(w, "%f", optVal)
1441 case string:
1442 fmt.Fprintf(w, "%s", quotedString(optVal))
1443 case []byte:
1444 fmt.Fprintf(w, "%s", quotedString(string(optVal)))
1445 case bool:
1446 fmt.Fprintf(w, "%v", optVal)
1447 case ident:
1448 fmt.Fprintf(w, "%s", optVal)
1449 case *desc.EnumValueDescriptor:
1450 fmt.Fprintf(w, "%s", optVal.GetName())
1451 case proto.Message:
1452 // TODO: if value is too long, marshal to text format with indentation to
1453 // make output prettier (also requires correctly indenting subsequent lines)
1454
1455 // TODO: alternate approach so we can apply p.ForceFullyQualifiedNames
1456 // inside the resulting value?
1457
1458 fmt.Fprintf(w, "{ %s }", proto.CompactTextString(optVal))
1459 default:
1460 panic(fmt.Sprintf("unknown type of value %T for field %s", optVal, name))
1461 }
1462}
1463
1464type edgeKind int
1465
1466const (
1467 edgeKindOption edgeKind = iota
1468 edgeKindFile
1469 edgeKindMessage
1470 edgeKindField
1471 edgeKindOneOf
1472 edgeKindExtensionRange
1473 edgeKindEnum
1474 edgeKindEnumVal
1475 edgeKindService
1476 edgeKindMethod
1477)
1478
1479// edges in simple state machine for matching options paths
1480// whose prefix should be included in source info to handle
1481// the way options are printed (which cannot always include
1482// the full path from original source)
1483var edges = map[edgeKind]map[int32]edgeKind{
1484 edgeKindFile: {
1485 internal.File_optionsTag: edgeKindOption,
1486 internal.File_messagesTag: edgeKindMessage,
1487 internal.File_enumsTag: edgeKindEnum,
1488 internal.File_extensionsTag: edgeKindField,
1489 internal.File_servicesTag: edgeKindService,
1490 },
1491 edgeKindMessage: {
1492 internal.Message_optionsTag: edgeKindOption,
1493 internal.Message_fieldsTag: edgeKindField,
1494 internal.Message_oneOfsTag: edgeKindOneOf,
1495 internal.Message_nestedMessagesTag: edgeKindMessage,
1496 internal.Message_enumsTag: edgeKindEnum,
1497 internal.Message_extensionsTag: edgeKindField,
1498 internal.Message_extensionRangeTag: edgeKindExtensionRange,
1499 // TODO: reserved range tag
1500 },
1501 edgeKindField: {
1502 internal.Field_optionsTag: edgeKindOption,
1503 },
1504 edgeKindOneOf: {
1505 internal.OneOf_optionsTag: edgeKindOption,
1506 },
1507 edgeKindExtensionRange: {
1508 internal.ExtensionRange_optionsTag: edgeKindOption,
1509 },
1510 edgeKindEnum: {
1511 internal.Enum_optionsTag: edgeKindOption,
1512 internal.Enum_valuesTag: edgeKindEnumVal,
1513 },
1514 edgeKindEnumVal: {
1515 internal.EnumVal_optionsTag: edgeKindOption,
1516 },
1517 edgeKindService: {
1518 internal.Service_optionsTag: edgeKindOption,
1519 internal.Service_methodsTag: edgeKindMethod,
1520 },
1521 edgeKindMethod: {
1522 internal.Method_optionsTag: edgeKindOption,
1523 },
1524}
1525
1526func extendOptionLocations(sc internal.SourceInfoMap) {
1527 for _, loc := range sc {
1528 allowed := edges[edgeKindFile]
1529 for i := 0; i+1 < len(loc.Path); i += 2 {
1530 nextKind, ok := allowed[loc.Path[i]]
1531 if !ok {
1532 break
1533 }
1534 if nextKind == edgeKindOption {
1535 // We've found an option entry. This could be arbitrarily
1536 // deep (for options that nested messages) or it could end
1537 // abruptly (for non-repeated fields). But we need a path
1538 // that is exactly the path-so-far plus two: the option tag
1539 // and an optional index for repeated option fields (zero
1540 // for non-repeated option fields). This is used for
1541 // querying source info when printing options.
1542 // for sorting elements
1543 newPath := make([]int32, i+3)
1544 copy(newPath, loc.Path)
1545 sc.PutIfAbsent(newPath, loc)
1546 // we do another path of path-so-far plus two, but with
1547 // explicit zero index -- just in case this actual path has
1548 // an extra path element, but it's not an index (e.g the
1549 // option field is not repeated, but the source info we are
1550 // looking at indicates a tag of a nested field)
1551 newPath[len(newPath)-1] = 0
1552 sc.PutIfAbsent(newPath, loc)
1553 // finally, we need the path-so-far plus one, just the option
1554 // tag, for sorting option groups
1555 newPath = newPath[:len(newPath)-1]
1556 sc.PutIfAbsent(newPath, loc)
1557
1558 break
1559 } else {
1560 allowed = edges[nextKind]
1561 }
1562 }
1563 }
1564}
1565
1566func (p *Printer) extractOptions(dsc desc.Descriptor, opts proto.Message, mf *dynamic.MessageFactory) (map[int32][]option, error) {
1567 md, err := desc.LoadMessageDescriptorForMessage(opts)
1568 if err != nil {
1569 return nil, err
1570 }
1571 dm := mf.NewDynamicMessage(md)
1572 if err = dm.ConvertFrom(opts); err != nil {
1573 return nil, fmt.Errorf("failed convert %s to dynamic message: %v", md.GetFullyQualifiedName(), err)
1574 }
1575
1576 pkg := dsc.GetFile().GetPackage()
1577 var scope string
1578 if _, ok := dsc.(*desc.FileDescriptor); ok {
1579 scope = pkg
1580 } else {
1581 scope = dsc.GetFullyQualifiedName()
1582 }
1583
1584 options := map[int32][]option{}
1585 var uninterpreted []interface{}
1586 for _, fldset := range [][]*desc.FieldDescriptor{md.GetFields(), mf.GetExtensionRegistry().AllExtensionsForType(md.GetFullyQualifiedName())} {
1587 for _, fld := range fldset {
1588 if dm.HasField(fld) {
1589 val := dm.GetField(fld)
1590 var opts []option
1591 var name string
1592 if fld.IsExtension() {
1593 name = fmt.Sprintf("(%s)", p.qualifyName(pkg, scope, fld.GetFullyQualifiedName()))
1594 } else {
1595 name = fld.GetName()
1596 }
1597 switch val := val.(type) {
1598 case []interface{}:
1599 if fld.GetNumber() == internal.UninterpretedOptionsTag {
1600 // we handle uninterpreted options differently
1601 uninterpreted = val
1602 continue
1603 }
1604
1605 for _, e := range val {
1606 if fld.GetType() == descriptor.FieldDescriptorProto_TYPE_ENUM {
1607 ev := fld.GetEnumType().FindValueByNumber(e.(int32))
1608 if ev == nil {
1609 // have to skip unknown enum values :(
1610 continue
1611 }
1612 e = ev
1613 }
1614 var name string
1615 if fld.IsExtension() {
1616 name = fmt.Sprintf("(%s)", p.qualifyName(pkg, scope, fld.GetFullyQualifiedName()))
1617 } else {
1618 name = fld.GetName()
1619 }
1620 opts = append(opts, option{name: name, val: e})
1621 }
1622 case map[interface{}]interface{}:
1623 for k := range sortKeys(val) {
1624 v := val[k]
1625 vf := fld.GetMapValueType()
1626 if vf.GetType() == descriptor.FieldDescriptorProto_TYPE_ENUM {
1627 ev := vf.GetEnumType().FindValueByNumber(v.(int32))
1628 if ev == nil {
1629 // have to skip unknown enum values :(
1630 continue
1631 }
1632 v = ev
1633 }
1634 entry := mf.NewDynamicMessage(fld.GetMessageType())
1635 entry.SetFieldByNumber(1, k)
1636 entry.SetFieldByNumber(2, v)
1637 opts = append(opts, option{name: name, val: entry})
1638 }
1639 default:
1640 if fld.GetType() == descriptor.FieldDescriptorProto_TYPE_ENUM {
1641 ev := fld.GetEnumType().FindValueByNumber(val.(int32))
1642 if ev == nil {
1643 // have to skip unknown enum values :(
1644 continue
1645 }
1646 val = ev
1647 }
1648 opts = append(opts, option{name: name, val: val})
1649 }
1650 if len(opts) > 0 {
1651 options[fld.GetNumber()] = opts
1652 }
1653 }
1654 }
1655 }
1656
1657 // if there are uninterpreted options, add those too
1658 if len(uninterpreted) > 0 {
1659 opts := make([]option, len(uninterpreted))
1660 for i, u := range uninterpreted {
1661 var unint *descriptor.UninterpretedOption
1662 if un, ok := u.(*descriptor.UninterpretedOption); ok {
1663 unint = un
1664 } else {
1665 dm := u.(*dynamic.Message)
1666 unint = &descriptor.UninterpretedOption{}
1667 if err := dm.ConvertTo(unint); err != nil {
1668 return nil, err
1669 }
1670 }
1671
1672 var buf bytes.Buffer
1673 for ni, n := range unint.Name {
1674 if ni > 0 {
1675 buf.WriteByte('.')
1676 }
1677 if n.GetIsExtension() {
1678 fmt.Fprintf(&buf, "(%s)", n.GetNamePart())
1679 } else {
1680 buf.WriteString(n.GetNamePart())
1681 }
1682 }
1683
1684 var v interface{}
1685 switch {
1686 case unint.IdentifierValue != nil:
1687 v = ident(unint.GetIdentifierValue())
1688 case unint.StringValue != nil:
1689 v = string(unint.GetStringValue())
1690 case unint.DoubleValue != nil:
1691 v = unint.GetDoubleValue()
1692 case unint.PositiveIntValue != nil:
1693 v = unint.GetPositiveIntValue()
1694 case unint.NegativeIntValue != nil:
1695 v = unint.GetNegativeIntValue()
1696 case unint.AggregateValue != nil:
1697 v = ident(unint.GetAggregateValue())
1698 }
1699
1700 opts[i] = option{name: buf.String(), val: v}
1701 }
1702 options[internal.UninterpretedOptionsTag] = opts
1703 }
1704
1705 return options, nil
1706}
1707
1708func optionsAsElementAddrs(optionsTag int32, order int, opts map[int32][]option) []elementAddr {
1709 var optAddrs []elementAddr
1710 for tag := range opts {
1711 optAddrs = append(optAddrs, elementAddr{elementType: optionsTag, elementIndex: int(tag), order: order})
1712 }
1713 sort.Sort(optionsByName{addrs: optAddrs, opts: opts})
1714 return optAddrs
1715}
1716
1717// quotedString implements the text format for string literals for protocol
1718// buffers. This form is also acceptable for string literals in option values
1719// by the protocol buffer compiler, protoc.
1720func quotedString(s string) string {
1721 var b bytes.Buffer
1722 // use WriteByte here to get any needed indent
1723 b.WriteByte('"')
1724 // Loop over the bytes, not the runes.
1725 for i := 0; i < len(s); i++ {
1726 // Divergence from C++: we don't escape apostrophes.
1727 // There's no need to escape them, and the C++ parser
1728 // copes with a naked apostrophe.
1729 switch c := s[i]; c {
1730 case '\n':
1731 b.WriteString("\\n")
1732 case '\r':
1733 b.WriteString("\\r")
1734 case '\t':
1735 b.WriteString("\\t")
1736 case '"':
1737 b.WriteString("\\")
1738 case '\\':
1739 b.WriteString("\\\\")
1740 default:
1741 if c >= 0x20 && c < 0x7f {
1742 b.WriteByte(c)
1743 } else {
1744 fmt.Fprintf(&b, "\\%03o", c)
1745 }
1746 }
1747 }
1748 b.WriteByte('"')
1749
1750 return b.String()
1751}
1752
1753type elementAddr struct {
1754 elementType int32
1755 elementIndex int
1756 order int
1757}
1758
1759type elementAddrs struct {
1760 addrs []elementAddr
1761 dsc interface{}
1762 opts map[int32][]option
1763}
1764
1765func (a elementAddrs) Len() int {
1766 return len(a.addrs)
1767}
1768
1769func (a elementAddrs) Less(i, j int) bool {
1770 // explicit order is considered first
1771 if a.addrs[i].order < a.addrs[j].order {
1772 return true
1773 } else if a.addrs[i].order > a.addrs[j].order {
1774 return false
1775 }
1776 // if order is equal, sort by element type
1777 if a.addrs[i].elementType < a.addrs[j].elementType {
1778 return true
1779 } else if a.addrs[i].elementType > a.addrs[j].elementType {
1780 return false
1781 }
1782
1783 di := a.at(a.addrs[i])
1784 dj := a.at(a.addrs[j])
1785
1786 switch vi := di.(type) {
1787 case *desc.FieldDescriptor:
1788 // fields are ordered by tag number
1789 vj := dj.(*desc.FieldDescriptor)
1790 // regular fields before extensions; extensions grouped by extendee
1791 if !vi.IsExtension() && vj.IsExtension() {
1792 return true
1793 } else if vi.IsExtension() && !vj.IsExtension() {
1794 return false
1795 } else if vi.IsExtension() && vj.IsExtension() {
1796 if vi.GetOwner() != vj.GetOwner() {
1797 return vi.GetOwner().GetFullyQualifiedName() < vj.GetOwner().GetFullyQualifiedName()
1798 }
1799 }
1800 return vi.GetNumber() < vj.GetNumber()
1801
1802 case *desc.EnumValueDescriptor:
1803 // enum values ordered by number then name
1804 vj := dj.(*desc.EnumValueDescriptor)
1805 if vi.GetNumber() == vj.GetNumber() {
1806 return vi.GetName() < vj.GetName()
1807 }
1808 return vi.GetNumber() < vj.GetNumber()
1809
1810 case *descriptor.DescriptorProto_ExtensionRange:
1811 // extension ranges ordered by tag
1812 return vi.GetStart() < dj.(*descriptor.DescriptorProto_ExtensionRange).GetStart()
1813
1814 case reservedRange:
1815 // reserved ranges ordered by tag, too
1816 return vi.start < dj.(reservedRange).start
1817
1818 case string:
1819 // reserved names lexically sorted
1820 return vi < dj.(string)
1821
1822 case pkg:
1823 // reserved names lexically sorted
1824 return vi < dj.(pkg)
1825
1826 case imp:
1827 // reserved names lexically sorted
1828 return vi < dj.(imp)
1829
1830 case []option:
1831 // options sorted by name, extensions last
1832 return optionLess(vi, dj.([]option))
1833
1834 default:
1835 // all other descriptors ordered by name
1836 return di.(desc.Descriptor).GetName() < dj.(desc.Descriptor).GetName()
1837 }
1838}
1839
1840func (a elementAddrs) Swap(i, j int) {
1841 a.addrs[i], a.addrs[j] = a.addrs[j], a.addrs[i]
1842}
1843
1844func (a elementAddrs) at(addr elementAddr) interface{} {
1845 switch dsc := a.dsc.(type) {
1846 case *desc.FileDescriptor:
1847 switch addr.elementType {
1848 case internal.File_packageTag:
1849 return pkg(dsc.GetPackage())
1850 case internal.File_dependencyTag:
1851 return imp(dsc.AsFileDescriptorProto().GetDependency()[addr.elementIndex])
1852 case internal.File_optionsTag:
1853 return a.opts[int32(addr.elementIndex)]
1854 case internal.File_messagesTag:
1855 return dsc.GetMessageTypes()[addr.elementIndex]
1856 case internal.File_enumsTag:
1857 return dsc.GetEnumTypes()[addr.elementIndex]
1858 case internal.File_servicesTag:
1859 return dsc.GetServices()[addr.elementIndex]
1860 case internal.File_extensionsTag:
1861 return dsc.GetExtensions()[addr.elementIndex]
1862 }
1863 case *desc.MessageDescriptor:
1864 switch addr.elementType {
1865 case internal.Message_optionsTag:
1866 return a.opts[int32(addr.elementIndex)]
1867 case internal.Message_fieldsTag:
1868 return dsc.GetFields()[addr.elementIndex]
1869 case internal.Message_nestedMessagesTag:
1870 return dsc.GetNestedMessageTypes()[addr.elementIndex]
1871 case internal.Message_enumsTag:
1872 return dsc.GetNestedEnumTypes()[addr.elementIndex]
1873 case internal.Message_extensionsTag:
1874 return dsc.GetNestedExtensions()[addr.elementIndex]
1875 case internal.Message_extensionRangeTag:
1876 return dsc.AsDescriptorProto().GetExtensionRange()[addr.elementIndex]
1877 case internal.Message_reservedRangeTag:
1878 rng := dsc.AsDescriptorProto().GetReservedRange()[addr.elementIndex]
1879 return reservedRange{start: rng.GetStart(), end: rng.GetEnd() - 1}
1880 case internal.Message_reservedNameTag:
1881 return dsc.AsDescriptorProto().GetReservedName()[addr.elementIndex]
1882 }
1883 case *desc.FieldDescriptor:
1884 if addr.elementType == internal.Field_optionsTag {
1885 return a.opts[int32(addr.elementIndex)]
1886 }
1887 case *desc.OneOfDescriptor:
1888 switch addr.elementType {
1889 case internal.OneOf_optionsTag:
1890 return a.opts[int32(addr.elementIndex)]
1891 case -internal.Message_fieldsTag:
1892 return dsc.GetOwner().GetFields()[addr.elementIndex]
1893 }
1894 case *desc.EnumDescriptor:
1895 switch addr.elementType {
1896 case internal.Enum_optionsTag:
1897 return a.opts[int32(addr.elementIndex)]
1898 case internal.Enum_valuesTag:
1899 return dsc.GetValues()[addr.elementIndex]
1900 case internal.Enum_reservedRangeTag:
1901 rng := dsc.AsEnumDescriptorProto().GetReservedRange()[addr.elementIndex]
1902 return reservedRange{start: rng.GetStart(), end: rng.GetEnd()}
1903 case internal.Enum_reservedNameTag:
1904 return dsc.AsEnumDescriptorProto().GetReservedName()[addr.elementIndex]
1905 }
1906 case *desc.EnumValueDescriptor:
1907 if addr.elementType == internal.EnumVal_optionsTag {
1908 return a.opts[int32(addr.elementIndex)]
1909 }
1910 case *desc.ServiceDescriptor:
1911 switch addr.elementType {
1912 case internal.Service_optionsTag:
1913 return a.opts[int32(addr.elementIndex)]
1914 case internal.Service_methodsTag:
1915 return dsc.GetMethods()[addr.elementIndex]
1916 }
1917 case *desc.MethodDescriptor:
1918 if addr.elementType == internal.Method_optionsTag {
1919 return a.opts[int32(addr.elementIndex)]
1920 }
1921 case extensionRange:
1922 if addr.elementType == internal.ExtensionRange_optionsTag {
1923 return a.opts[int32(addr.elementIndex)]
1924 }
1925 }
1926
1927 panic(fmt.Sprintf("location for unknown field %d of %T", addr.elementType, a.dsc))
1928}
1929
1930type extensionRange struct {
1931 owner *desc.MessageDescriptor
1932 extRange *descriptor.DescriptorProto_ExtensionRange
1933}
1934
1935type elementSrcOrder struct {
1936 elementAddrs
1937 sourceInfo internal.SourceInfoMap
1938 prefix []int32
1939}
1940
1941func (a elementSrcOrder) Less(i, j int) bool {
1942 ti := a.addrs[i].elementType
1943 ei := a.addrs[i].elementIndex
1944
1945 tj := a.addrs[j].elementType
1946 ej := a.addrs[j].elementIndex
1947
1948 var si, sj *descriptor.SourceCodeInfo_Location
1949 if ei < 0 {
1950 si = a.sourceInfo.Get(append(a.prefix, -int32(ei)))
1951 } else if ti < 0 {
1952 p := make([]int32, len(a.prefix)-2)
1953 copy(p, a.prefix)
1954 si = a.sourceInfo.Get(append(p, ti, int32(ei)))
1955 } else {
1956 si = a.sourceInfo.Get(append(a.prefix, ti, int32(ei)))
1957 }
1958 if ej < 0 {
1959 sj = a.sourceInfo.Get(append(a.prefix, -int32(ej)))
1960 } else if tj < 0 {
1961 p := make([]int32, len(a.prefix)-2)
1962 copy(p, a.prefix)
1963 sj = a.sourceInfo.Get(append(p, tj, int32(ej)))
1964 } else {
1965 sj = a.sourceInfo.Get(append(a.prefix, tj, int32(ej)))
1966 }
1967
1968 if (si == nil) != (sj == nil) {
1969 // generally, we put unknown elements after known ones;
1970 // except package and option elements go first
1971
1972 // i will be unknown and j will be known
1973 swapped := false
1974 if si != nil {
1975 si, sj = sj, si
1976 // no need to swap ti and tj because we don't use tj anywhere below
1977 ti = tj
1978 swapped = true
1979 }
1980 switch a.dsc.(type) {
1981 case *desc.FileDescriptor:
1982 if ti == internal.File_packageTag || ti == internal.File_optionsTag {
1983 return !swapped
1984 }
1985 case *desc.MessageDescriptor:
1986 if ti == internal.Message_optionsTag {
1987 return !swapped
1988 }
1989 case *desc.EnumDescriptor:
1990 if ti == internal.Enum_optionsTag {
1991 return !swapped
1992 }
1993 case *desc.ServiceDescriptor:
1994 if ti == internal.Service_optionsTag {
1995 return !swapped
1996 }
1997 }
1998 return swapped
1999
2000 } else if si == nil || sj == nil {
2001 // let stable sort keep unknown elements in same relative order
2002 return false
2003 }
2004
2005 for idx := 0; idx < len(sj.Span); idx++ {
2006 if idx >= len(si.Span) {
2007 return true
2008 }
2009 if si.Span[idx] < sj.Span[idx] {
2010 return true
2011 }
2012 if si.Span[idx] > sj.Span[idx] {
2013 return false
2014 }
2015 }
2016 return false
2017}
2018
2019type optionsByName struct {
2020 addrs []elementAddr
2021 opts map[int32][]option
2022}
2023
2024func (o optionsByName) Len() int {
2025 return len(o.addrs)
2026}
2027
2028func (o optionsByName) Less(i, j int) bool {
2029 oi := o.opts[int32(o.addrs[i].elementIndex)]
2030 oj := o.opts[int32(o.addrs[j].elementIndex)]
2031 return optionLess(oi, oj)
2032}
2033
2034func optionLess(i, j []option) bool {
2035 ni := i[0].name
2036 nj := j[0].name
2037 if ni[0] != '(' && nj[0] == '(' {
2038 return true
2039 } else if ni[0] == '(' && nj[0] != '(' {
2040 return false
2041 }
2042 return ni < nj
2043}
2044
2045func (o optionsByName) Swap(i, j int) {
2046 o.addrs[i], o.addrs[j] = o.addrs[j], o.addrs[i]
2047}
2048
2049func (p *Printer) printElement(isDecriptor bool, si *descriptor.SourceCodeInfo_Location, w *writer, indent int, el func(*writer)) {
2050 includeComments := isDecriptor || p.includeCommentType(CommentsTokens)
2051
2052 if includeComments && si != nil {
2053 p.printLeadingComments(si, w, indent)
2054 }
2055 el(w)
2056 if includeComments && si != nil {
2057 p.printTrailingComments(si, w, indent)
2058 }
2059 if indent >= 0 && !w.newline {
2060 // if we're not printing inline but element did not have trailing newline, add one now
2061 fmt.Fprintln(w)
2062 }
2063}
2064
2065func (p *Printer) printElementString(si *descriptor.SourceCodeInfo_Location, w *writer, indent int, str string) {
2066 p.printElement(false, si, w, inline(indent), func(w *writer) {
2067 fmt.Fprintf(w, "%s ", str)
2068 })
2069}
2070
2071func (p *Printer) includeCommentType(c CommentType) bool {
2072 return (p.OmitComments & c) == 0
2073}
2074
2075func (p *Printer) printLeadingComments(si *descriptor.SourceCodeInfo_Location, w *writer, indent int) bool {
2076 endsInNewLine := false
2077
2078 if p.includeCommentType(CommentsDetached) {
2079 for _, c := range si.GetLeadingDetachedComments() {
2080 if p.printComment(c, w, indent, true) {
2081 // if comment ended in newline, add another newline to separate
2082 // this comment from the next
2083 p.newLine(w)
2084 endsInNewLine = true
2085 } else if indent < 0 {
2086 // comment did not end in newline and we are trying to inline?
2087 // just add a space to separate this comment from what follows
2088 fmt.Fprint(w, " ")
2089 endsInNewLine = false
2090 } else {
2091 // comment did not end in newline and we are *not* trying to inline?
2092 // add newline to end of comment and add another to separate this
2093 // comment from what follows
2094 fmt.Fprintln(w) // needed to end comment, regardless of p.Compact
2095 p.newLine(w)
2096 endsInNewLine = true
2097 }
2098 }
2099 }
2100
2101 if p.includeCommentType(CommentsLeading) && si.GetLeadingComments() != "" {
2102 endsInNewLine = p.printComment(si.GetLeadingComments(), w, indent, true)
2103 if !endsInNewLine {
2104 if indent >= 0 {
2105 // leading comment didn't end with newline but needs one
2106 // (because we're *not* inlining)
2107 fmt.Fprintln(w) // needed to end comment, regardless of p.Compact
2108 endsInNewLine = true
2109 } else {
2110 // space between comment and following element when inlined
2111 fmt.Fprint(w, " ")
2112 }
2113 }
2114 }
2115
2116 return endsInNewLine
2117}
2118
2119func (p *Printer) printTrailingComments(si *descriptor.SourceCodeInfo_Location, w *writer, indent int) {
2120 if p.includeCommentType(CommentsTrailing) && si.GetTrailingComments() != "" {
2121 if !p.printComment(si.GetTrailingComments(), w, indent, p.TrailingCommentsOnSeparateLine) && indent >= 0 {
2122 // trailing comment didn't end with newline but needs one
2123 // (because we're *not* inlining)
2124 fmt.Fprintln(w) // needed to end comment, regardless of p.Compact
2125 } else if indent < 0 {
2126 fmt.Fprint(w, " ")
2127 }
2128 }
2129}
2130
2131func (p *Printer) printComment(comments string, w *writer, indent int, forceNextLine bool) bool {
2132 if comments == "" {
2133 return false
2134 }
2135
2136 var multiLine bool
2137 if indent < 0 {
2138 // use multi-line style when inlining
2139 multiLine = true
2140 } else {
2141 multiLine = p.PreferMultiLineStyleComments
2142 }
2143 if multiLine && strings.Contains(comments, "*/") {
2144 // can't emit '*/' in a multi-line style comment
2145 multiLine = false
2146 }
2147
2148 lines := strings.Split(comments, "\n")
2149
2150 // first, remove leading and trailing blank lines
2151 if lines[0] == "" {
2152 lines = lines[1:]
2153 }
2154 if lines[len(lines)-1] == "" {
2155 lines = lines[:len(lines)-1]
2156 }
2157 if len(lines) == 0 {
2158 return false
2159 }
2160
2161 if indent >= 0 && !w.newline {
2162 // last element did not have trailing newline, so we
2163 // either need to tack on newline or, if comment is
2164 // just one line, inline it on the end
2165 if forceNextLine || len(lines) > 1 {
2166 fmt.Fprintln(w)
2167 } else {
2168 if !w.space {
2169 fmt.Fprint(w, " ")
2170 }
2171 indent = inline(indent)
2172 }
2173 }
2174
2175 if len(lines) == 1 && multiLine {
2176 p.indent(w, indent)
2177 line := lines[0]
2178 if line[0] == ' ' && line[len(line)-1] != ' ' {
2179 // add trailing space for symmetry
2180 line += " "
2181 }
2182 fmt.Fprintf(w, "/*%s*/", line)
2183 if indent >= 0 {
2184 fmt.Fprintln(w)
2185 return true
2186 }
2187 return false
2188 }
2189
2190 if multiLine {
2191 // multi-line style comments that actually span multiple lines
2192 // get a blank line before and after so that comment renders nicely
2193 lines = append(lines, "", "")
2194 copy(lines[1:], lines)
2195 lines[0] = ""
2196 }
2197
2198 for i, l := range lines {
2199 p.maybeIndent(w, indent, i > 0)
2200 if multiLine {
2201 if i == 0 {
2202 // first line
2203 fmt.Fprintf(w, "/*%s\n", strings.TrimRight(l, " \t"))
2204 } else if i == len(lines)-1 {
2205 // last line
2206 if l == "" {
2207 fmt.Fprint(w, " */")
2208 } else {
2209 fmt.Fprintf(w, " *%s*/", l)
2210 }
2211 if indent >= 0 {
2212 fmt.Fprintln(w)
2213 }
2214 } else {
2215 fmt.Fprintf(w, " *%s\n", strings.TrimRight(l, " \t"))
2216 }
2217 } else {
2218 fmt.Fprintf(w, "//%s\n", strings.TrimRight(l, " \t"))
2219 }
2220 }
2221
2222 // single-line comments always end in newline; multi-line comments only
2223 // end in newline for non-negative (e.g. non-inlined) indentation
2224 return !multiLine || indent >= 0
2225}
2226
2227func (p *Printer) indent(w io.Writer, indent int) {
2228 for i := 0; i < indent; i++ {
2229 fmt.Fprint(w, p.Indent)
2230 }
2231}
2232
2233func (p *Printer) maybeIndent(w io.Writer, indent int, requireIndent bool) {
2234 if indent < 0 && requireIndent {
2235 p.indent(w, -indent)
2236 } else {
2237 p.indent(w, indent)
2238 }
2239}
2240
2241type writer struct {
2242 io.Writer
2243 err error
2244 space bool
2245 newline bool
2246}
2247
2248func newWriter(w io.Writer) *writer {
2249 return &writer{Writer: w, newline: true}
2250}
2251
2252func (w *writer) Write(p []byte) (int, error) {
2253 if len(p) == 0 {
2254 return 0, nil
2255 }
2256
2257 w.newline = false
2258
2259 if w.space {
2260 // skip any trailing space if the following
2261 // character is semicolon, comma, or close bracket
2262 if p[0] != ';' && p[0] != ',' && p[0] != ']' {
2263 _, err := w.Writer.Write([]byte{' '})
2264 if err != nil {
2265 w.err = err
2266 return 0, err
2267 }
2268 }
2269 w.space = false
2270 }
2271
2272 if p[len(p)-1] == ' ' {
2273 w.space = true
2274 p = p[:len(p)-1]
2275 }
2276 if len(p) > 0 && p[len(p)-1] == '\n' {
2277 w.newline = true
2278 }
2279
2280 num, err := w.Writer.Write(p)
2281 if err != nil {
2282 w.err = err
2283 } else if w.space {
2284 // pretend space was written
2285 num++
2286 }
2287 return num, err
2288}