Copyright 2010 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 proto

import (
	
	
	
	
	
	
	

	
	
	
	
	
)

const wrapTextMarshalV2 = false
TextMarshaler is a configurable text format marshaler.
type TextMarshaler struct {
	Compact   bool // use compact text format (one line)
	ExpandAny bool // expand google.protobuf.Any messages of known types
}
Marshal writes the proto text format of m to w.
func ( *TextMarshaler) ( io.Writer,  Message) error {
	,  := .marshal()
	if len() > 0 {
		if ,  := .Write();  != nil {
			return 
		}
	}
	return 
}
Text returns a proto text formatted string of m.
func ( *TextMarshaler) ( Message) string {
	,  := .marshal()
	return string()
}

func ( *TextMarshaler) ( Message) ([]byte, error) {
	 := MessageReflect()
	if  == nil || !.IsValid() {
		return []byte("<nil>"), nil
	}

	if wrapTextMarshalV2 {
		if ,  := .(encoding.TextMarshaler);  {
			return .MarshalText()
		}

		 := prototext.MarshalOptions{
			AllowPartial: true,
			EmitUnknown:  true,
		}
		if !.Compact {
			.Indent = "  "
		}
		if !.ExpandAny {
			.Resolver = (*protoregistry.Types)(nil)
		}
		return .Marshal(.Interface())
	} else {
		 := &textWriter{
			compact:   .Compact,
			expandAny: .ExpandAny,
			complete:  true,
		}

		if ,  := .(encoding.TextMarshaler);  {
			,  := .MarshalText()
			if  != nil {
				return nil, 
			}
			.Write()
			return .buf, nil
		}

		 := .writeMessage()
		return .buf, 
	}
}

var (
	defaultTextMarshaler = TextMarshaler{}
	compactTextMarshaler = TextMarshaler{Compact: true}
)
MarshalText writes the proto text format of m to w.
MarshalTextString returns a proto text formatted string of m.
CompactText writes the compact proto text format of m to w.
CompactTextString returns a compact proto text formatted string of m.
func ( Message) string { return compactTextMarshaler.Text() }

var (
	newline         = []byte("\n")
	endBraceNewline = []byte("}\n")
	posInf          = []byte("inf")
	negInf          = []byte("-inf")
	nan             = []byte("nan")
)
textWriter is an io.Writer that tracks its indentation level.
type textWriter struct {
	compact   bool // same as TextMarshaler.Compact
	expandAny bool // same as TextMarshaler.ExpandAny
	complete  bool // whether the current position is a complete line
	indent    int  // indentation level; never negative
	buf       []byte
}

func ( *textWriter) ( []byte) ( int,  error) {
	 := bytes.Count(, newline)
	if  == 0 {
		if !.compact && .complete {
			.writeIndent()
		}
		.buf = append(.buf, ...)
		.complete = false
		return len(), nil
	}

	 := bytes.SplitN(, newline, +1)
	if .compact {
		for ,  := range  {
			if  > 0 {
				.buf = append(.buf, ' ')
				++
			}
			.buf = append(.buf, ...)
			 += len()
		}
		return , nil
	}

	for ,  := range  {
		if .complete {
			.writeIndent()
		}
		.buf = append(.buf, ...)
		 += len()
		if +1 < len() {
			.buf = append(.buf, '\n')
			++
		}
	}
	.complete = len([len()-1]) == 0
	return , nil
}

func ( *textWriter) ( byte) error {
	if .compact &&  == '\n' {
		 = ' '
	}
	if !.compact && .complete {
		.writeIndent()
	}
	.buf = append(.buf, )
	.complete =  == '\n'
	return nil
}

func ( *textWriter) ( protoreflect.FieldDescriptor) {
	if !.compact && .complete {
		.writeIndent()
	}
	.complete = false

	if .Kind() != protoreflect.GroupKind {
		.buf = append(.buf, .Name()...)
		.WriteByte(':')
Use message type name for group field name.
		.buf = append(.buf, .Message().Name()...)
	}

	if !.compact {
		.WriteByte(' ')
	}
}

When type URL contains any characters except [0-9A-Za-z./\-]*, it must be quoted.
	for ,  := range  {
		switch {
		case  == '.' ||  == '/' ||  == '_':
			continue
		case '0' <=  &&  <= '9':
			continue
		case 'A' <=  &&  <= 'Z':
			continue
		case 'a' <=  &&  <= 'z':
			continue
		default:
			return true
		}
	}
	return false
}
writeProto3Any writes an expanded google.protobuf.Any message. It returns (false, nil) if sv value can't be unmarshaled (e.g. because required messages are not linked in). It returns (true, error) when sv was written in expanded format or an error was encountered.
func ( *textWriter) ( protoreflect.Message) (bool, error) {
	 := .Descriptor()
	 := .Fields().ByName("type_url")
	 := .Fields().ByName("value")

	 := .Get().String()
	,  := protoregistry.GlobalTypes.FindMessageByURL()
	if  != nil {
		return false, nil
	}

	 := .Get().Bytes()
	 := .New()
	if  := proto.Unmarshal(, .Interface());  != nil {
		return false, nil
	}
	.Write([]byte("["))
	if requiresQuotes() {
		.writeQuotedString()
	} else {
		.Write([]byte())
	}
	if .compact {
		.Write([]byte("]:<"))
	} else {
		.Write([]byte("]: <\n"))
		.indent++
	}
	if  := .writeMessage();  != nil {
		return true, 
	}
	if .compact {
		.Write([]byte("> "))
	} else {
		.indent--
		.Write([]byte(">\n"))
	}
	return true, nil
}

func ( *textWriter) ( protoreflect.Message) error {
	 := .Descriptor()
	if .expandAny && .FullName() == "google.protobuf.Any" {
		if ,  := .writeProto3Any();  {
			return 
		}
	}

	 := .Fields()
	for  := 0;  < .Len(); {
		 := .Get()
		if  := .ContainingOneof();  != nil {
			 = .WhichOneof()
			 += .Fields().Len()
		} else {
			++
		}
		if  == nil || !.Has() {
			continue
		}

		switch {
		case .IsList():
			 := .Get().List()
			for  := 0;  < .Len(); ++ {
				.writeName()
				 := .Get()
				if  := .writeSingularValue(, );  != nil {
					return 
				}
				.WriteByte('\n')
			}
		case .IsMap():
			 := .MapKey()
			 := .MapValue()
			 := .Get().Map()

			type  struct{ ,  protoreflect.Value }
			var  []
			.Range(func( protoreflect.MapKey,  protoreflect.Value) bool {
				 = append(, {.Value(), })
				return true
			})
			sort.Slice(, func(,  int) bool {
				switch .Kind() {
				case protoreflect.BoolKind:
					return ![]..Bool() && []..Bool()
				case protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Sfixed32Kind, protoreflect.Int64Kind, protoreflect.Sint64Kind, protoreflect.Sfixed64Kind:
					return []..Int() < []..Int()
				case protoreflect.Uint32Kind, protoreflect.Fixed32Kind, protoreflect.Uint64Kind, protoreflect.Fixed64Kind:
					return []..Uint() < []..Uint()
				case protoreflect.StringKind:
					return []..String() < []..String()
				default:
					panic("invalid kind")
				}
			})
			for ,  := range  {
				.writeName()
				.WriteByte('<')
				if !.compact {
					.WriteByte('\n')
				}
				.indent++
				.writeName()
				if  := .writeSingularValue(., );  != nil {
					return 
				}
				.WriteByte('\n')
				.writeName()
				if  := .writeSingularValue(., );  != nil {
					return 
				}
				.WriteByte('\n')
				.indent--
				.WriteByte('>')
				.WriteByte('\n')
			}
		default:
			.writeName()
			if  := .writeSingularValue(.Get(), );  != nil {
				return 
			}
			.WriteByte('\n')
		}
	}

	if  := .GetUnknown(); len() > 0 {
		.writeUnknownFields()
	}
	return .writeExtensions()
}

func ( *textWriter) ( protoreflect.Value,  protoreflect.FieldDescriptor) error {
	switch .Kind() {
	case protoreflect.FloatKind, protoreflect.DoubleKind:
		switch  := .Float(); {
		case math.IsInf(, +1):
			.Write(posInf)
		case math.IsInf(, -1):
			.Write(negInf)
		case math.IsNaN():
			.Write(nan)
		default:
			fmt.Fprint(, .Interface())
		}
NOTE: This does not validate UTF-8 for historical reasons.
		.writeQuotedString(string(.String()))
	case protoreflect.BytesKind:
		.writeQuotedString(string(.Bytes()))
	case protoreflect.MessageKind, protoreflect.GroupKind:
		var ,  byte = '<', '>'
		if .Kind() == protoreflect.GroupKind {
			,  = '{', '}'
		}
		.WriteByte()
		if !.compact {
			.WriteByte('\n')
		}
		.indent++
		 := .Message()
		if ,  := .Interface().(encoding.TextMarshaler);  {
			,  := .MarshalText()
			if  != nil {
				return 
			}
			.Write()
		} else {
			.writeMessage()
		}
		.indent--
		.WriteByte()
	case protoreflect.EnumKind:
		if  := .Enum().Values().ByNumber(.Enum());  != nil {
			fmt.Fprint(, .Name())
		} else {
			fmt.Fprint(, .Enum())
		}
	default:
		fmt.Fprint(, .Interface())
	}
	return nil
}
writeQuotedString writes a quoted string in the protocol buffer text format.
func ( *textWriter) ( string) {
	.WriteByte('"')
	for  := 0;  < len(); ++ {
		switch  := [];  {
		case '\n':
			.buf = append(.buf, `\n`...)
		case '\r':
			.buf = append(.buf, `\r`...)
		case '\t':
			.buf = append(.buf, `\t`...)
		case '"':
			.buf = append(.buf, `\"`...)
		case '\\':
			.buf = append(.buf, `\\`...)
		default:
			if  :=  >= 0x20 &&  < 0x7f;  {
				.buf = append(.buf, )
			} else {
				.buf = append(.buf, fmt.Sprintf(`\%03o`, )...)
			}
		}
	}
	.WriteByte('"')
}

func ( *textWriter) ( []byte) {
	if !.compact {
		fmt.Fprintf(, "/* %d unknown bytes */\n", len())
	}

	for len() > 0 {
		, ,  := protowire.ConsumeTag()
		if  < 0 {
			return
		}
		 = [:]

		if  == protowire.EndGroupType {
			.indent--
			.Write(endBraceNewline)
			continue
		}
		fmt.Fprint(, )
		if  != protowire.StartGroupType {
			.WriteByte(':')
		}
		if !.compact ||  == protowire.StartGroupType {
			.WriteByte(' ')
		}
		switch  {
		case protowire.VarintType:
			,  := protowire.ConsumeVarint()
			if  < 0 {
				return
			}
			 = [:]
			fmt.Fprint(, )
		case protowire.Fixed32Type:
			,  := protowire.ConsumeFixed32()
			if  < 0 {
				return
			}
			 = [:]
			fmt.Fprint(, )
		case protowire.Fixed64Type:
			,  := protowire.ConsumeFixed64()
			if  < 0 {
				return
			}
			 = [:]
			fmt.Fprint(, )
		case protowire.BytesType:
			,  := protowire.ConsumeBytes()
			if  < 0 {
				return
			}
			 = [:]
			fmt.Fprintf(, "%q", )
		case protowire.StartGroupType:
			.WriteByte('{')
			.indent++
		default:
			fmt.Fprintf(, "/* unknown wire type %d */", )
		}
		.WriteByte('\n')
	}
}
writeExtensions writes all the extensions in m.
func ( *textWriter) ( protoreflect.Message) error {
	 := .Descriptor()
	if .ExtensionRanges().Len() == 0 {
		return nil
	}

	type  struct {
		 protoreflect.FieldDescriptor
		  protoreflect.Value
	}
	var  []
	.Range(func( protoreflect.FieldDescriptor,  protoreflect.Value) bool {
		if .IsExtension() {
			 = append(, {, })
		}
		return true
	})
	sort.Slice(, func(,  int) bool {
		return []..Number() < []..Number()
	})

For message set, use the name of the message as the extension name.
		 := string(..FullName())
		if isMessageSet(..ContainingMessage()) {
			 = strings.TrimSuffix(, ".message_set_extension")
		}

		if !..IsList() {
			if  := .writeSingularExtension(, ., .);  != nil {
				return 
			}
		} else {
			 := ..List()
			for  := 0;  < .Len(); ++ {
				if  := .writeSingularExtension(, .Get(), .);  != nil {
					return 
				}
			}
		}
	}
	return nil
}

func ( *textWriter) ( string,  protoreflect.Value,  protoreflect.FieldDescriptor) error {
	fmt.Fprintf(, "[%s]:", )
	if !.compact {
		.WriteByte(' ')
	}
	if  := .writeSingularValue(, );  != nil {
		return 
	}
	.WriteByte('\n')
	return nil
}

func ( *textWriter) () {
	if !.complete {
		return
	}
	for  := 0;  < .indent*2; ++ {
		.buf = append(.buf, ' ')
	}
	.complete = false