mirror of
https://github.com/superseriousbusiness/gotosocial.git
synced 2024-11-27 22:26:40 +00:00
773 lines
21 KiB
Go
773 lines
21 KiB
Go
// Copyright 2018 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package prototext
|
|
|
|
import (
|
|
"fmt"
|
|
"unicode/utf8"
|
|
|
|
"google.golang.org/protobuf/internal/encoding/messageset"
|
|
"google.golang.org/protobuf/internal/encoding/text"
|
|
"google.golang.org/protobuf/internal/errors"
|
|
"google.golang.org/protobuf/internal/flags"
|
|
"google.golang.org/protobuf/internal/genid"
|
|
"google.golang.org/protobuf/internal/pragma"
|
|
"google.golang.org/protobuf/internal/set"
|
|
"google.golang.org/protobuf/internal/strs"
|
|
"google.golang.org/protobuf/proto"
|
|
"google.golang.org/protobuf/reflect/protoreflect"
|
|
"google.golang.org/protobuf/reflect/protoregistry"
|
|
)
|
|
|
|
// Unmarshal reads the given []byte into the given [proto.Message].
|
|
// The provided message must be mutable (e.g., a non-nil pointer to a message).
|
|
func Unmarshal(b []byte, m proto.Message) error {
|
|
return UnmarshalOptions{}.Unmarshal(b, m)
|
|
}
|
|
|
|
// UnmarshalOptions is a configurable textproto format unmarshaler.
|
|
type UnmarshalOptions struct {
|
|
pragma.NoUnkeyedLiterals
|
|
|
|
// AllowPartial accepts input for messages that will result in missing
|
|
// required fields. If AllowPartial is false (the default), Unmarshal will
|
|
// return error if there are any missing required fields.
|
|
AllowPartial bool
|
|
|
|
// DiscardUnknown specifies whether to ignore unknown fields when parsing.
|
|
// An unknown field is any field whose field name or field number does not
|
|
// resolve to any known or extension field in the message.
|
|
// By default, unmarshal rejects unknown fields as an error.
|
|
DiscardUnknown bool
|
|
|
|
// Resolver is used for looking up types when unmarshaling
|
|
// google.protobuf.Any messages or extension fields.
|
|
// If nil, this defaults to using protoregistry.GlobalTypes.
|
|
Resolver interface {
|
|
protoregistry.MessageTypeResolver
|
|
protoregistry.ExtensionTypeResolver
|
|
}
|
|
}
|
|
|
|
// Unmarshal reads the given []byte and populates the given [proto.Message]
|
|
// using options in the UnmarshalOptions object.
|
|
// The provided message must be mutable (e.g., a non-nil pointer to a message).
|
|
func (o UnmarshalOptions) Unmarshal(b []byte, m proto.Message) error {
|
|
return o.unmarshal(b, m)
|
|
}
|
|
|
|
// unmarshal is a centralized function that all unmarshal operations go through.
|
|
// For profiling purposes, avoid changing the name of this function or
|
|
// introducing other code paths for unmarshal that do not go through this.
|
|
func (o UnmarshalOptions) unmarshal(b []byte, m proto.Message) error {
|
|
proto.Reset(m)
|
|
|
|
if o.Resolver == nil {
|
|
o.Resolver = protoregistry.GlobalTypes
|
|
}
|
|
|
|
dec := decoder{text.NewDecoder(b), o}
|
|
if err := dec.unmarshalMessage(m.ProtoReflect(), false); err != nil {
|
|
return err
|
|
}
|
|
if o.AllowPartial {
|
|
return nil
|
|
}
|
|
return proto.CheckInitialized(m)
|
|
}
|
|
|
|
type decoder struct {
|
|
*text.Decoder
|
|
opts UnmarshalOptions
|
|
}
|
|
|
|
// newError returns an error object with position info.
|
|
func (d decoder) newError(pos int, f string, x ...interface{}) error {
|
|
line, column := d.Position(pos)
|
|
head := fmt.Sprintf("(line %d:%d): ", line, column)
|
|
return errors.New(head+f, x...)
|
|
}
|
|
|
|
// unexpectedTokenError returns a syntax error for the given unexpected token.
|
|
func (d decoder) unexpectedTokenError(tok text.Token) error {
|
|
return d.syntaxError(tok.Pos(), "unexpected token: %s", tok.RawString())
|
|
}
|
|
|
|
// syntaxError returns a syntax error for given position.
|
|
func (d decoder) syntaxError(pos int, f string, x ...interface{}) error {
|
|
line, column := d.Position(pos)
|
|
head := fmt.Sprintf("syntax error (line %d:%d): ", line, column)
|
|
return errors.New(head+f, x...)
|
|
}
|
|
|
|
// unmarshalMessage unmarshals into the given protoreflect.Message.
|
|
func (d decoder) unmarshalMessage(m protoreflect.Message, checkDelims bool) error {
|
|
messageDesc := m.Descriptor()
|
|
if !flags.ProtoLegacy && messageset.IsMessageSet(messageDesc) {
|
|
return errors.New("no support for proto1 MessageSets")
|
|
}
|
|
|
|
if messageDesc.FullName() == genid.Any_message_fullname {
|
|
return d.unmarshalAny(m, checkDelims)
|
|
}
|
|
|
|
if checkDelims {
|
|
tok, err := d.Read()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if tok.Kind() != text.MessageOpen {
|
|
return d.unexpectedTokenError(tok)
|
|
}
|
|
}
|
|
|
|
var seenNums set.Ints
|
|
var seenOneofs set.Ints
|
|
fieldDescs := messageDesc.Fields()
|
|
|
|
for {
|
|
// Read field name.
|
|
tok, err := d.Read()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
switch typ := tok.Kind(); typ {
|
|
case text.Name:
|
|
// Continue below.
|
|
case text.EOF:
|
|
if checkDelims {
|
|
return text.ErrUnexpectedEOF
|
|
}
|
|
return nil
|
|
default:
|
|
if checkDelims && typ == text.MessageClose {
|
|
return nil
|
|
}
|
|
return d.unexpectedTokenError(tok)
|
|
}
|
|
|
|
// Resolve the field descriptor.
|
|
var name protoreflect.Name
|
|
var fd protoreflect.FieldDescriptor
|
|
var xt protoreflect.ExtensionType
|
|
var xtErr error
|
|
var isFieldNumberName bool
|
|
|
|
switch tok.NameKind() {
|
|
case text.IdentName:
|
|
name = protoreflect.Name(tok.IdentName())
|
|
fd = fieldDescs.ByTextName(string(name))
|
|
|
|
case text.TypeName:
|
|
// Handle extensions only. This code path is not for Any.
|
|
xt, xtErr = d.opts.Resolver.FindExtensionByName(protoreflect.FullName(tok.TypeName()))
|
|
|
|
case text.FieldNumber:
|
|
isFieldNumberName = true
|
|
num := protoreflect.FieldNumber(tok.FieldNumber())
|
|
if !num.IsValid() {
|
|
return d.newError(tok.Pos(), "invalid field number: %d", num)
|
|
}
|
|
fd = fieldDescs.ByNumber(num)
|
|
if fd == nil {
|
|
xt, xtErr = d.opts.Resolver.FindExtensionByNumber(messageDesc.FullName(), num)
|
|
}
|
|
}
|
|
|
|
if xt != nil {
|
|
fd = xt.TypeDescriptor()
|
|
if !messageDesc.ExtensionRanges().Has(fd.Number()) || fd.ContainingMessage().FullName() != messageDesc.FullName() {
|
|
return d.newError(tok.Pos(), "message %v cannot be extended by %v", messageDesc.FullName(), fd.FullName())
|
|
}
|
|
} else if xtErr != nil && xtErr != protoregistry.NotFound {
|
|
return d.newError(tok.Pos(), "unable to resolve [%s]: %v", tok.RawString(), xtErr)
|
|
}
|
|
if flags.ProtoLegacy {
|
|
if fd != nil && fd.IsWeak() && fd.Message().IsPlaceholder() {
|
|
fd = nil // reset since the weak reference is not linked in
|
|
}
|
|
}
|
|
|
|
// Handle unknown fields.
|
|
if fd == nil {
|
|
if d.opts.DiscardUnknown || messageDesc.ReservedNames().Has(name) {
|
|
d.skipValue()
|
|
continue
|
|
}
|
|
return d.newError(tok.Pos(), "unknown field: %v", tok.RawString())
|
|
}
|
|
|
|
// Handle fields identified by field number.
|
|
if isFieldNumberName {
|
|
// TODO: Add an option to permit parsing field numbers.
|
|
//
|
|
// This requires careful thought as the MarshalOptions.EmitUnknown
|
|
// option allows formatting unknown fields as the field number and the
|
|
// best-effort textual representation of the field value. In that case,
|
|
// it may not be possible to unmarshal the value from a parser that does
|
|
// have information about the unknown field.
|
|
return d.newError(tok.Pos(), "cannot specify field by number: %v", tok.RawString())
|
|
}
|
|
|
|
switch {
|
|
case fd.IsList():
|
|
kind := fd.Kind()
|
|
if kind != protoreflect.MessageKind && kind != protoreflect.GroupKind && !tok.HasSeparator() {
|
|
return d.syntaxError(tok.Pos(), "missing field separator :")
|
|
}
|
|
|
|
list := m.Mutable(fd).List()
|
|
if err := d.unmarshalList(fd, list); err != nil {
|
|
return err
|
|
}
|
|
|
|
case fd.IsMap():
|
|
mmap := m.Mutable(fd).Map()
|
|
if err := d.unmarshalMap(fd, mmap); err != nil {
|
|
return err
|
|
}
|
|
|
|
default:
|
|
kind := fd.Kind()
|
|
if kind != protoreflect.MessageKind && kind != protoreflect.GroupKind && !tok.HasSeparator() {
|
|
return d.syntaxError(tok.Pos(), "missing field separator :")
|
|
}
|
|
|
|
// If field is a oneof, check if it has already been set.
|
|
if od := fd.ContainingOneof(); od != nil {
|
|
idx := uint64(od.Index())
|
|
if seenOneofs.Has(idx) {
|
|
return d.newError(tok.Pos(), "error parsing %q, oneof %v is already set", tok.RawString(), od.FullName())
|
|
}
|
|
seenOneofs.Set(idx)
|
|
}
|
|
|
|
num := uint64(fd.Number())
|
|
if seenNums.Has(num) {
|
|
return d.newError(tok.Pos(), "non-repeated field %q is repeated", tok.RawString())
|
|
}
|
|
|
|
if err := d.unmarshalSingular(fd, m); err != nil {
|
|
return err
|
|
}
|
|
seenNums.Set(num)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// unmarshalSingular unmarshals a non-repeated field value specified by the
|
|
// given FieldDescriptor.
|
|
func (d decoder) unmarshalSingular(fd protoreflect.FieldDescriptor, m protoreflect.Message) error {
|
|
var val protoreflect.Value
|
|
var err error
|
|
switch fd.Kind() {
|
|
case protoreflect.MessageKind, protoreflect.GroupKind:
|
|
val = m.NewField(fd)
|
|
err = d.unmarshalMessage(val.Message(), true)
|
|
default:
|
|
val, err = d.unmarshalScalar(fd)
|
|
}
|
|
if err == nil {
|
|
m.Set(fd, val)
|
|
}
|
|
return err
|
|
}
|
|
|
|
// unmarshalScalar unmarshals a scalar/enum protoreflect.Value specified by the
|
|
// given FieldDescriptor.
|
|
func (d decoder) unmarshalScalar(fd protoreflect.FieldDescriptor) (protoreflect.Value, error) {
|
|
tok, err := d.Read()
|
|
if err != nil {
|
|
return protoreflect.Value{}, err
|
|
}
|
|
|
|
if tok.Kind() != text.Scalar {
|
|
return protoreflect.Value{}, d.unexpectedTokenError(tok)
|
|
}
|
|
|
|
kind := fd.Kind()
|
|
switch kind {
|
|
case protoreflect.BoolKind:
|
|
if b, ok := tok.Bool(); ok {
|
|
return protoreflect.ValueOfBool(b), nil
|
|
}
|
|
|
|
case protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Sfixed32Kind:
|
|
if n, ok := tok.Int32(); ok {
|
|
return protoreflect.ValueOfInt32(n), nil
|
|
}
|
|
|
|
case protoreflect.Int64Kind, protoreflect.Sint64Kind, protoreflect.Sfixed64Kind:
|
|
if n, ok := tok.Int64(); ok {
|
|
return protoreflect.ValueOfInt64(n), nil
|
|
}
|
|
|
|
case protoreflect.Uint32Kind, protoreflect.Fixed32Kind:
|
|
if n, ok := tok.Uint32(); ok {
|
|
return protoreflect.ValueOfUint32(n), nil
|
|
}
|
|
|
|
case protoreflect.Uint64Kind, protoreflect.Fixed64Kind:
|
|
if n, ok := tok.Uint64(); ok {
|
|
return protoreflect.ValueOfUint64(n), nil
|
|
}
|
|
|
|
case protoreflect.FloatKind:
|
|
if n, ok := tok.Float32(); ok {
|
|
return protoreflect.ValueOfFloat32(n), nil
|
|
}
|
|
|
|
case protoreflect.DoubleKind:
|
|
if n, ok := tok.Float64(); ok {
|
|
return protoreflect.ValueOfFloat64(n), nil
|
|
}
|
|
|
|
case protoreflect.StringKind:
|
|
if s, ok := tok.String(); ok {
|
|
if strs.EnforceUTF8(fd) && !utf8.ValidString(s) {
|
|
return protoreflect.Value{}, d.newError(tok.Pos(), "contains invalid UTF-8")
|
|
}
|
|
return protoreflect.ValueOfString(s), nil
|
|
}
|
|
|
|
case protoreflect.BytesKind:
|
|
if b, ok := tok.String(); ok {
|
|
return protoreflect.ValueOfBytes([]byte(b)), nil
|
|
}
|
|
|
|
case protoreflect.EnumKind:
|
|
if lit, ok := tok.Enum(); ok {
|
|
// Lookup EnumNumber based on name.
|
|
if enumVal := fd.Enum().Values().ByName(protoreflect.Name(lit)); enumVal != nil {
|
|
return protoreflect.ValueOfEnum(enumVal.Number()), nil
|
|
}
|
|
}
|
|
if num, ok := tok.Int32(); ok {
|
|
return protoreflect.ValueOfEnum(protoreflect.EnumNumber(num)), nil
|
|
}
|
|
|
|
default:
|
|
panic(fmt.Sprintf("invalid scalar kind %v", kind))
|
|
}
|
|
|
|
return protoreflect.Value{}, d.newError(tok.Pos(), "invalid value for %v type: %v", kind, tok.RawString())
|
|
}
|
|
|
|
// unmarshalList unmarshals into given protoreflect.List. A list value can
|
|
// either be in [] syntax or simply just a single scalar/message value.
|
|
func (d decoder) unmarshalList(fd protoreflect.FieldDescriptor, list protoreflect.List) error {
|
|
tok, err := d.Peek()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
switch fd.Kind() {
|
|
case protoreflect.MessageKind, protoreflect.GroupKind:
|
|
switch tok.Kind() {
|
|
case text.ListOpen:
|
|
d.Read()
|
|
for {
|
|
tok, err := d.Peek()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
switch tok.Kind() {
|
|
case text.ListClose:
|
|
d.Read()
|
|
return nil
|
|
case text.MessageOpen:
|
|
pval := list.NewElement()
|
|
if err := d.unmarshalMessage(pval.Message(), true); err != nil {
|
|
return err
|
|
}
|
|
list.Append(pval)
|
|
default:
|
|
return d.unexpectedTokenError(tok)
|
|
}
|
|
}
|
|
|
|
case text.MessageOpen:
|
|
pval := list.NewElement()
|
|
if err := d.unmarshalMessage(pval.Message(), true); err != nil {
|
|
return err
|
|
}
|
|
list.Append(pval)
|
|
return nil
|
|
}
|
|
|
|
default:
|
|
switch tok.Kind() {
|
|
case text.ListOpen:
|
|
d.Read()
|
|
for {
|
|
tok, err := d.Peek()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
switch tok.Kind() {
|
|
case text.ListClose:
|
|
d.Read()
|
|
return nil
|
|
case text.Scalar:
|
|
pval, err := d.unmarshalScalar(fd)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
list.Append(pval)
|
|
default:
|
|
return d.unexpectedTokenError(tok)
|
|
}
|
|
}
|
|
|
|
case text.Scalar:
|
|
pval, err := d.unmarshalScalar(fd)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
list.Append(pval)
|
|
return nil
|
|
}
|
|
}
|
|
|
|
return d.unexpectedTokenError(tok)
|
|
}
|
|
|
|
// unmarshalMap unmarshals into given protoreflect.Map. A map value is a
|
|
// textproto message containing {key: <kvalue>, value: <mvalue>}.
|
|
func (d decoder) unmarshalMap(fd protoreflect.FieldDescriptor, mmap protoreflect.Map) error {
|
|
// Determine ahead whether map entry is a scalar type or a message type in
|
|
// order to call the appropriate unmarshalMapValue func inside
|
|
// unmarshalMapEntry.
|
|
var unmarshalMapValue func() (protoreflect.Value, error)
|
|
switch fd.MapValue().Kind() {
|
|
case protoreflect.MessageKind, protoreflect.GroupKind:
|
|
unmarshalMapValue = func() (protoreflect.Value, error) {
|
|
pval := mmap.NewValue()
|
|
if err := d.unmarshalMessage(pval.Message(), true); err != nil {
|
|
return protoreflect.Value{}, err
|
|
}
|
|
return pval, nil
|
|
}
|
|
default:
|
|
unmarshalMapValue = func() (protoreflect.Value, error) {
|
|
return d.unmarshalScalar(fd.MapValue())
|
|
}
|
|
}
|
|
|
|
tok, err := d.Read()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
switch tok.Kind() {
|
|
case text.MessageOpen:
|
|
return d.unmarshalMapEntry(fd, mmap, unmarshalMapValue)
|
|
|
|
case text.ListOpen:
|
|
for {
|
|
tok, err := d.Read()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
switch tok.Kind() {
|
|
case text.ListClose:
|
|
return nil
|
|
case text.MessageOpen:
|
|
if err := d.unmarshalMapEntry(fd, mmap, unmarshalMapValue); err != nil {
|
|
return err
|
|
}
|
|
default:
|
|
return d.unexpectedTokenError(tok)
|
|
}
|
|
}
|
|
|
|
default:
|
|
return d.unexpectedTokenError(tok)
|
|
}
|
|
}
|
|
|
|
// unmarshalMap unmarshals into given protoreflect.Map. A map value is a
|
|
// textproto message containing {key: <kvalue>, value: <mvalue>}.
|
|
func (d decoder) unmarshalMapEntry(fd protoreflect.FieldDescriptor, mmap protoreflect.Map, unmarshalMapValue func() (protoreflect.Value, error)) error {
|
|
var key protoreflect.MapKey
|
|
var pval protoreflect.Value
|
|
Loop:
|
|
for {
|
|
// Read field name.
|
|
tok, err := d.Read()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
switch tok.Kind() {
|
|
case text.Name:
|
|
if tok.NameKind() != text.IdentName {
|
|
if !d.opts.DiscardUnknown {
|
|
return d.newError(tok.Pos(), "unknown map entry field %q", tok.RawString())
|
|
}
|
|
d.skipValue()
|
|
continue Loop
|
|
}
|
|
// Continue below.
|
|
case text.MessageClose:
|
|
break Loop
|
|
default:
|
|
return d.unexpectedTokenError(tok)
|
|
}
|
|
|
|
switch name := protoreflect.Name(tok.IdentName()); name {
|
|
case genid.MapEntry_Key_field_name:
|
|
if !tok.HasSeparator() {
|
|
return d.syntaxError(tok.Pos(), "missing field separator :")
|
|
}
|
|
if key.IsValid() {
|
|
return d.newError(tok.Pos(), "map entry %q cannot be repeated", name)
|
|
}
|
|
val, err := d.unmarshalScalar(fd.MapKey())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
key = val.MapKey()
|
|
|
|
case genid.MapEntry_Value_field_name:
|
|
if kind := fd.MapValue().Kind(); (kind != protoreflect.MessageKind) && (kind != protoreflect.GroupKind) {
|
|
if !tok.HasSeparator() {
|
|
return d.syntaxError(tok.Pos(), "missing field separator :")
|
|
}
|
|
}
|
|
if pval.IsValid() {
|
|
return d.newError(tok.Pos(), "map entry %q cannot be repeated", name)
|
|
}
|
|
pval, err = unmarshalMapValue()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
default:
|
|
if !d.opts.DiscardUnknown {
|
|
return d.newError(tok.Pos(), "unknown map entry field %q", name)
|
|
}
|
|
d.skipValue()
|
|
}
|
|
}
|
|
|
|
if !key.IsValid() {
|
|
key = fd.MapKey().Default().MapKey()
|
|
}
|
|
if !pval.IsValid() {
|
|
switch fd.MapValue().Kind() {
|
|
case protoreflect.MessageKind, protoreflect.GroupKind:
|
|
// If value field is not set for message/group types, construct an
|
|
// empty one as default.
|
|
pval = mmap.NewValue()
|
|
default:
|
|
pval = fd.MapValue().Default()
|
|
}
|
|
}
|
|
mmap.Set(key, pval)
|
|
return nil
|
|
}
|
|
|
|
// unmarshalAny unmarshals an Any textproto. It can either be in expanded form
|
|
// or non-expanded form.
|
|
func (d decoder) unmarshalAny(m protoreflect.Message, checkDelims bool) error {
|
|
var typeURL string
|
|
var bValue []byte
|
|
var seenTypeUrl bool
|
|
var seenValue bool
|
|
var isExpanded bool
|
|
|
|
if checkDelims {
|
|
tok, err := d.Read()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if tok.Kind() != text.MessageOpen {
|
|
return d.unexpectedTokenError(tok)
|
|
}
|
|
}
|
|
|
|
Loop:
|
|
for {
|
|
// Read field name. Can only have 3 possible field names, i.e. type_url,
|
|
// value and type URL name inside [].
|
|
tok, err := d.Read()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if typ := tok.Kind(); typ != text.Name {
|
|
if checkDelims {
|
|
if typ == text.MessageClose {
|
|
break Loop
|
|
}
|
|
} else if typ == text.EOF {
|
|
break Loop
|
|
}
|
|
return d.unexpectedTokenError(tok)
|
|
}
|
|
|
|
switch tok.NameKind() {
|
|
case text.IdentName:
|
|
// Both type_url and value fields require field separator :.
|
|
if !tok.HasSeparator() {
|
|
return d.syntaxError(tok.Pos(), "missing field separator :")
|
|
}
|
|
|
|
switch name := protoreflect.Name(tok.IdentName()); name {
|
|
case genid.Any_TypeUrl_field_name:
|
|
if seenTypeUrl {
|
|
return d.newError(tok.Pos(), "duplicate %v field", genid.Any_TypeUrl_field_fullname)
|
|
}
|
|
if isExpanded {
|
|
return d.newError(tok.Pos(), "conflict with [%s] field", typeURL)
|
|
}
|
|
tok, err := d.Read()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
var ok bool
|
|
typeURL, ok = tok.String()
|
|
if !ok {
|
|
return d.newError(tok.Pos(), "invalid %v field value: %v", genid.Any_TypeUrl_field_fullname, tok.RawString())
|
|
}
|
|
seenTypeUrl = true
|
|
|
|
case genid.Any_Value_field_name:
|
|
if seenValue {
|
|
return d.newError(tok.Pos(), "duplicate %v field", genid.Any_Value_field_fullname)
|
|
}
|
|
if isExpanded {
|
|
return d.newError(tok.Pos(), "conflict with [%s] field", typeURL)
|
|
}
|
|
tok, err := d.Read()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
s, ok := tok.String()
|
|
if !ok {
|
|
return d.newError(tok.Pos(), "invalid %v field value: %v", genid.Any_Value_field_fullname, tok.RawString())
|
|
}
|
|
bValue = []byte(s)
|
|
seenValue = true
|
|
|
|
default:
|
|
if !d.opts.DiscardUnknown {
|
|
return d.newError(tok.Pos(), "invalid field name %q in %v message", tok.RawString(), genid.Any_message_fullname)
|
|
}
|
|
}
|
|
|
|
case text.TypeName:
|
|
if isExpanded {
|
|
return d.newError(tok.Pos(), "cannot have more than one type")
|
|
}
|
|
if seenTypeUrl {
|
|
return d.newError(tok.Pos(), "conflict with type_url field")
|
|
}
|
|
typeURL = tok.TypeName()
|
|
var err error
|
|
bValue, err = d.unmarshalExpandedAny(typeURL, tok.Pos())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
isExpanded = true
|
|
|
|
default:
|
|
if !d.opts.DiscardUnknown {
|
|
return d.newError(tok.Pos(), "invalid field name %q in %v message", tok.RawString(), genid.Any_message_fullname)
|
|
}
|
|
}
|
|
}
|
|
|
|
fds := m.Descriptor().Fields()
|
|
if len(typeURL) > 0 {
|
|
m.Set(fds.ByNumber(genid.Any_TypeUrl_field_number), protoreflect.ValueOfString(typeURL))
|
|
}
|
|
if len(bValue) > 0 {
|
|
m.Set(fds.ByNumber(genid.Any_Value_field_number), protoreflect.ValueOfBytes(bValue))
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (d decoder) unmarshalExpandedAny(typeURL string, pos int) ([]byte, error) {
|
|
mt, err := d.opts.Resolver.FindMessageByURL(typeURL)
|
|
if err != nil {
|
|
return nil, d.newError(pos, "unable to resolve message [%v]: %v", typeURL, err)
|
|
}
|
|
// Create new message for the embedded message type and unmarshal the value
|
|
// field into it.
|
|
m := mt.New()
|
|
if err := d.unmarshalMessage(m, true); err != nil {
|
|
return nil, err
|
|
}
|
|
// Serialize the embedded message and return the resulting bytes.
|
|
b, err := proto.MarshalOptions{
|
|
AllowPartial: true, // Never check required fields inside an Any.
|
|
Deterministic: true,
|
|
}.Marshal(m.Interface())
|
|
if err != nil {
|
|
return nil, d.newError(pos, "error in marshaling message into Any.value: %v", err)
|
|
}
|
|
return b, nil
|
|
}
|
|
|
|
// skipValue makes the decoder parse a field value in order to advance the read
|
|
// to the next field. It relies on Read returning an error if the types are not
|
|
// in valid sequence.
|
|
func (d decoder) skipValue() error {
|
|
tok, err := d.Read()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
// Only need to continue reading for messages and lists.
|
|
switch tok.Kind() {
|
|
case text.MessageOpen:
|
|
return d.skipMessageValue()
|
|
|
|
case text.ListOpen:
|
|
for {
|
|
tok, err := d.Read()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
switch tok.Kind() {
|
|
case text.ListClose:
|
|
return nil
|
|
case text.MessageOpen:
|
|
if err := d.skipMessageValue(); err != nil {
|
|
return err
|
|
}
|
|
default:
|
|
// Skip items. This will not validate whether skipped values are
|
|
// of the same type or not, same behavior as C++
|
|
// TextFormat::Parser::AllowUnknownField(true) version 3.8.0.
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// skipMessageValue makes the decoder parse and skip over all fields in a
|
|
// message. It assumes that the previous read type is MessageOpen.
|
|
func (d decoder) skipMessageValue() error {
|
|
for {
|
|
tok, err := d.Read()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
switch tok.Kind() {
|
|
case text.MessageClose:
|
|
return nil
|
|
case text.Name:
|
|
if err := d.skipValue(); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
}
|