github.com/jhump/protoreflect@v1.16.0/desc/protoprint/message_literal.go (about)

     1  package protoprint
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"sort"
     7  	"strings"
     8  
     9  	"google.golang.org/protobuf/proto"
    10  	"google.golang.org/protobuf/reflect/protoreflect"
    11  	"google.golang.org/protobuf/reflect/protoregistry"
    12  	"google.golang.org/protobuf/types/dynamicpb"
    13  	"google.golang.org/protobuf/types/known/anypb"
    14  )
    15  
    16  func (p *Printer) printMessageLiteralCompact(msg protoreflect.Message, res *protoregistry.Types, pkg, scope string) string {
    17  	var buf bytes.Buffer
    18  	p.printMessageLiteralToBuffer(&buf, msg, res, pkg, scope, 0, -1)
    19  	return buf.String()
    20  }
    21  
    22  func (p *Printer) printMessageLiteral(msg protoreflect.Message, res *protoregistry.Types, pkg, scope string, threshold, indent int) string {
    23  	var buf bytes.Buffer
    24  	p.printMessageLiteralToBuffer(&buf, msg, res, pkg, scope, threshold, indent)
    25  	return buf.String()
    26  }
    27  
    28  var (
    29  	anyTypeName = (&anypb.Any{}).ProtoReflect().Descriptor().FullName()
    30  )
    31  
    32  const (
    33  	anyTypeUrlTag = 1
    34  	anyValueTag   = 2
    35  )
    36  
    37  func (p *Printer) printMessageLiteralToBuffer(buf *bytes.Buffer, msg protoreflect.Message, res *protoregistry.Types, pkg, scope string, threshold, indent int) {
    38  	if p.maybePrintAnyMessageToBuffer(buf, msg, res, pkg, scope, threshold, indent) {
    39  		return
    40  	}
    41  
    42  	buf.WriteRune('{')
    43  	if indent >= 0 {
    44  		indent++
    45  	}
    46  
    47  	type fieldVal struct {
    48  		fld protoreflect.FieldDescriptor
    49  		val protoreflect.Value
    50  	}
    51  	var fields []fieldVal
    52  	msg.Range(func(fld protoreflect.FieldDescriptor, val protoreflect.Value) bool {
    53  		fields = append(fields, fieldVal{fld: fld, val: val})
    54  		return true
    55  	})
    56  	sort.Slice(fields, func(i, j int) bool {
    57  		return fields[i].fld.Number() < fields[j].fld.Number()
    58  	})
    59  
    60  	for i, fldVal := range fields {
    61  		fld, val := fldVal.fld, fldVal.val
    62  		if i > 0 {
    63  			buf.WriteRune(',')
    64  		}
    65  		p.maybeNewline(buf, indent)
    66  		if fld.IsExtension() {
    67  			buf.WriteRune('[')
    68  			buf.WriteString(p.qualifyExtensionLiteralName(pkg, scope, string(fld.FullName())))
    69  			buf.WriteRune(']')
    70  		} else {
    71  			buf.WriteString(string(fld.Name()))
    72  		}
    73  		buf.WriteString(": ")
    74  		switch {
    75  		case fld.IsList():
    76  			p.printArrayLiteralToBufferMaybeCompact(buf, fld, val.List(), res, pkg, scope, threshold, indent)
    77  		case fld.IsMap():
    78  			p.printMapLiteralToBufferMaybeCompact(buf, fld, val.Map(), res, pkg, scope, threshold, indent)
    79  		case fld.Kind() == protoreflect.MessageKind || fld.Kind() == protoreflect.GroupKind:
    80  			p.printMessageLiteralToBufferMaybeCompact(buf, val.Message(), res, pkg, scope, threshold, indent)
    81  		default:
    82  			p.printValueLiteralToBuffer(buf, fld, val.Interface())
    83  		}
    84  	}
    85  
    86  	if indent >= 0 {
    87  		indent--
    88  	}
    89  	p.maybeNewline(buf, indent)
    90  	buf.WriteRune('}')
    91  }
    92  
    93  func (p *Printer) printMessageLiteralToBufferMaybeCompact(buf *bytes.Buffer, msg protoreflect.Message, res *protoregistry.Types, pkg, scope string, threshold, indent int) {
    94  	if indent >= 0 {
    95  		// first see if the message is compact enough to fit on one line
    96  		str := p.printMessageLiteralCompact(msg, res, pkg, scope)
    97  		fieldCount := strings.Count(str, ",")
    98  		nestedCount := strings.Count(str, "{") - 1
    99  		if fieldCount <= 1 && nestedCount == 0 {
   100  			// can't expand
   101  			buf.WriteString(str)
   102  			return
   103  		}
   104  		if len(str) <= threshold {
   105  			// no need to expand
   106  			buf.WriteString(str)
   107  			return
   108  		}
   109  	}
   110  	p.printMessageLiteralToBuffer(buf, msg, res, pkg, scope, threshold, indent)
   111  }
   112  
   113  func (p *Printer) maybePrintAnyMessageToBuffer(buf *bytes.Buffer, msg protoreflect.Message, res *protoregistry.Types, pkg, scope string, threshold, indent int) bool {
   114  	md := msg.Descriptor()
   115  	if md.FullName() != anyTypeName {
   116  		return false
   117  	}
   118  	typeUrlFld := md.Fields().ByNumber(anyTypeUrlTag)
   119  	if typeUrlFld == nil || typeUrlFld.Kind() != protoreflect.StringKind || typeUrlFld.Cardinality() == protoreflect.Repeated {
   120  		return false
   121  	}
   122  	valueFld := md.Fields().ByNumber(anyValueTag)
   123  	if valueFld == nil || valueFld.Kind() != protoreflect.BytesKind || valueFld.Cardinality() == protoreflect.Repeated {
   124  		return false
   125  	}
   126  	typeUrl := msg.Get(typeUrlFld).String()
   127  	if typeUrl == "" {
   128  		return false
   129  	}
   130  	mt, err := res.FindMessageByURL(typeUrl)
   131  	if err != nil {
   132  		return false
   133  	}
   134  	valueMsg := mt.New()
   135  	valueBytes := msg.Get(valueFld).Bytes()
   136  	if err := (proto.UnmarshalOptions{Resolver: res}).Unmarshal(valueBytes, valueMsg.Interface()); err != nil {
   137  		return false
   138  	}
   139  
   140  	buf.WriteRune('{')
   141  	if indent >= 0 {
   142  		indent++
   143  	}
   144  	p.maybeNewline(buf, indent)
   145  
   146  	buf.WriteRune('[')
   147  	buf.WriteString(typeUrl)
   148  	buf.WriteString("]: ")
   149  	p.printMessageLiteralToBufferMaybeCompact(buf, valueMsg, res, pkg, scope, threshold, indent)
   150  
   151  	if indent >= 0 {
   152  		indent--
   153  	}
   154  	p.maybeNewline(buf, indent)
   155  	buf.WriteRune('}')
   156  
   157  	return true
   158  }
   159  
   160  func (p *Printer) printValueLiteralToBuffer(buf *bytes.Buffer, fld protoreflect.FieldDescriptor, value interface{}) {
   161  	switch val := value.(type) {
   162  	case protoreflect.EnumNumber:
   163  		ev := fld.Enum().Values().ByNumber(val)
   164  		if ev == nil {
   165  			_, _ = fmt.Fprintf(buf, "%v", value)
   166  		} else {
   167  			buf.WriteString(string(ev.Name()))
   168  		}
   169  	case string:
   170  		buf.WriteString(quotedString(val))
   171  	case []byte:
   172  		buf.WriteString(quotedBytes(string(val)))
   173  	case int32, uint32, int64, uint64:
   174  		_, _ = fmt.Fprintf(buf, "%d", val)
   175  	case float32, float64:
   176  		_, _ = fmt.Fprintf(buf, "%f", val)
   177  	default:
   178  		_, _ = fmt.Fprintf(buf, "%v", val)
   179  	}
   180  }
   181  
   182  func (p *Printer) maybeNewline(buf *bytes.Buffer, indent int) {
   183  	if indent < 0 {
   184  		// compact form
   185  		buf.WriteRune(' ')
   186  		return
   187  	}
   188  	buf.WriteRune('\n')
   189  	p.indent(buf, indent)
   190  }
   191  
   192  func (p *Printer) printArrayLiteralToBufferMaybeCompact(buf *bytes.Buffer, fld protoreflect.FieldDescriptor, val protoreflect.List, res *protoregistry.Types, pkg, scope string, threshold, indent int) {
   193  	if indent >= 0 {
   194  		// first see if the array is compact enough to fit on one line
   195  		str := p.printArrayLiteralCompact(fld, val, res, pkg, scope)
   196  		elementCount := strings.Count(str, ",")
   197  		nestedCount := strings.Count(str, "{") - 1
   198  		if elementCount <= 1 && nestedCount == 0 {
   199  			// can't expand
   200  			buf.WriteString(str)
   201  			return
   202  		}
   203  		if len(str) <= threshold {
   204  			// no need to expand
   205  			buf.WriteString(str)
   206  			return
   207  		}
   208  	}
   209  	p.printArrayLiteralToBuffer(buf, fld, val, res, pkg, scope, threshold, indent)
   210  }
   211  
   212  func (p *Printer) printArrayLiteralCompact(fld protoreflect.FieldDescriptor, val protoreflect.List, res *protoregistry.Types, pkg, scope string) string {
   213  	var buf bytes.Buffer
   214  	p.printArrayLiteralToBuffer(&buf, fld, val, res, pkg, scope, 0, -1)
   215  	return buf.String()
   216  }
   217  
   218  func (p *Printer) printArrayLiteralToBuffer(buf *bytes.Buffer, fld protoreflect.FieldDescriptor, val protoreflect.List, res *protoregistry.Types, pkg, scope string, threshold, indent int) {
   219  	buf.WriteRune('[')
   220  	if indent >= 0 {
   221  		indent++
   222  	}
   223  
   224  	for i := 0; i < val.Len(); i++ {
   225  		if i > 0 {
   226  			buf.WriteRune(',')
   227  		}
   228  		p.maybeNewline(buf, indent)
   229  		if fld.Kind() == protoreflect.MessageKind || fld.Kind() == protoreflect.GroupKind {
   230  			p.printMessageLiteralToBufferMaybeCompact(buf, val.Get(i).Message(), res, pkg, scope, threshold, indent)
   231  		} else {
   232  			p.printValueLiteralToBuffer(buf, fld, val.Get(i).Interface())
   233  		}
   234  	}
   235  
   236  	if indent >= 0 {
   237  		indent--
   238  	}
   239  	p.maybeNewline(buf, indent)
   240  	buf.WriteRune(']')
   241  }
   242  
   243  func (p *Printer) printMapLiteralToBufferMaybeCompact(buf *bytes.Buffer, fld protoreflect.FieldDescriptor, val protoreflect.Map, res *protoregistry.Types, pkg, scope string, threshold, indent int) {
   244  	if indent >= 0 {
   245  		// first see if the map is compact enough to fit on one line
   246  		str := p.printMapLiteralCompact(fld, val, res, pkg, scope)
   247  		if len(str) <= threshold {
   248  			buf.WriteString(str)
   249  			return
   250  		}
   251  	}
   252  	p.printMapLiteralToBuffer(buf, fld, val, res, pkg, scope, threshold, indent)
   253  }
   254  
   255  func (p *Printer) printMapLiteralCompact(fld protoreflect.FieldDescriptor, val protoreflect.Map, res *protoregistry.Types, pkg, scope string) string {
   256  	var buf bytes.Buffer
   257  	p.printMapLiteralToBuffer(&buf, fld, val, res, pkg, scope, 0, -1)
   258  	return buf.String()
   259  }
   260  
   261  func (p *Printer) printMapLiteralToBuffer(buf *bytes.Buffer, fld protoreflect.FieldDescriptor, val protoreflect.Map, res *protoregistry.Types, pkg, scope string, threshold, indent int) {
   262  	keys := sortKeys(val)
   263  	l := &mapAsList{
   264  		m:      val,
   265  		entry:  dynamicpb.NewMessageType(fld.Message()),
   266  		keyFld: fld.MapKey(),
   267  		valFld: fld.MapValue(),
   268  		keys:   keys,
   269  	}
   270  	p.printArrayLiteralToBuffer(buf, fld, l, res, pkg, scope, threshold, indent)
   271  }
   272  
   273  type mapAsList struct {
   274  	m              protoreflect.Map
   275  	entry          protoreflect.MessageType
   276  	keyFld, valFld protoreflect.FieldDescriptor
   277  	keys           []protoreflect.MapKey
   278  }
   279  
   280  func (m *mapAsList) Len() int {
   281  	return len(m.keys)
   282  }
   283  
   284  func (m *mapAsList) Get(i int) protoreflect.Value {
   285  	msg := m.entry.New()
   286  	key := m.keys[i]
   287  	msg.Set(m.keyFld, key.Value())
   288  	val := m.m.Get(key)
   289  	msg.Set(m.valFld, val)
   290  	return protoreflect.ValueOfMessage(msg)
   291  }
   292  
   293  func (m *mapAsList) Set(_i int, _ protoreflect.Value) {
   294  	panic("Set is not implemented")
   295  }
   296  
   297  func (m *mapAsList) Append(_ protoreflect.Value) {
   298  	panic("Append is not implemented")
   299  }
   300  
   301  func (m *mapAsList) AppendMutable() protoreflect.Value {
   302  	panic("AppendMutable is not implemented")
   303  }
   304  
   305  func (m *mapAsList) Truncate(_ int) {
   306  	panic("Truncate is not implemented")
   307  }
   308  
   309  func (m *mapAsList) NewElement() protoreflect.Value {
   310  	panic("NewElement is not implemented")
   311  }
   312  
   313  func (m *mapAsList) IsValid() bool {
   314  	return true
   315  }