| 
									
										
										
										
											2019-10-19 18:28:36 +09:00
										 |  |  | package yaml | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							| 
									
										
										
										
											2020-11-11 19:27:58 +09:00
										 |  |  | 	"context" | 
					
						
							| 
									
										
										
										
											2020-01-12 02:50:51 +09:00
										 |  |  | 	"encoding" | 
					
						
							| 
									
										
										
										
											2019-10-19 18:28:36 +09:00
										 |  |  | 	"fmt" | 
					
						
							|  |  |  | 	"io" | 
					
						
							|  |  |  | 	"math" | 
					
						
							|  |  |  | 	"reflect" | 
					
						
							|  |  |  | 	"sort" | 
					
						
							|  |  |  | 	"strconv" | 
					
						
							|  |  |  | 	"strings" | 
					
						
							| 
									
										
										
										
											2019-11-07 21:14:36 +09:00
										 |  |  | 	"time" | 
					
						
							| 
									
										
										
										
											2019-10-19 18:28:36 +09:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	"github.com/goccy/go-yaml/ast" | 
					
						
							| 
									
										
										
										
											2019-10-24 11:07:40 +09:00
										 |  |  | 	"github.com/goccy/go-yaml/internal/errors" | 
					
						
							| 
									
										
										
										
											2019-10-25 02:38:32 +09:00
										 |  |  | 	"github.com/goccy/go-yaml/parser" | 
					
						
							| 
									
										
										
										
											2019-10-19 18:28:36 +09:00
										 |  |  | 	"github.com/goccy/go-yaml/printer" | 
					
						
							|  |  |  | 	"github.com/goccy/go-yaml/token" | 
					
						
							|  |  |  | 	"golang.org/x/xerrors" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const ( | 
					
						
							| 
									
										
										
										
											2019-10-21 12:53:30 +09:00
										 |  |  | 	// DefaultIndentSpaces default number of space for indent | 
					
						
							| 
									
										
										
										
											2019-10-19 18:28:36 +09:00
										 |  |  | 	DefaultIndentSpaces = 2 | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-21 12:53:30 +09:00
										 |  |  | // Encoder writes YAML values to an output stream. | 
					
						
							| 
									
										
										
										
											2019-10-19 18:28:36 +09:00
										 |  |  | type Encoder struct { | 
					
						
							| 
									
										
										
										
											2019-10-20 13:05:03 +09:00
										 |  |  | 	writer             io.Writer | 
					
						
							|  |  |  | 	opts               []EncodeOption | 
					
						
							|  |  |  | 	indent             int | 
					
						
							| 
									
										
										
										
											2019-10-31 13:29:43 +09:00
										 |  |  | 	isFlowStyle        bool | 
					
						
							| 
									
										
										
										
											2020-06-02 19:56:02 +09:00
										 |  |  | 	isJSONStyle        bool | 
					
						
							| 
									
										
										
										
											2020-10-26 19:00:49 +09:00
										 |  |  | 	useJSONMarshaler   bool | 
					
						
							| 
									
										
										
										
											2020-01-09 13:33:10 +09:00
										 |  |  | 	anchorCallback     func(*ast.AnchorNode, interface{}) error | 
					
						
							| 
									
										
										
										
											2019-10-20 13:05:03 +09:00
										 |  |  | 	anchorPtrToNameMap map[uintptr]string | 
					
						
							| 
									
										
										
										
											2019-10-19 18:28:36 +09:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	line        int | 
					
						
							|  |  |  | 	column      int | 
					
						
							|  |  |  | 	offset      int | 
					
						
							|  |  |  | 	indentNum   int | 
					
						
							|  |  |  | 	indentLevel int | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // NewEncoder returns a new encoder that writes to w. | 
					
						
							|  |  |  | // The Encoder should be closed after use to flush all data to w. | 
					
						
							|  |  |  | func NewEncoder(w io.Writer, opts ...EncodeOption) *Encoder { | 
					
						
							|  |  |  | 	return &Encoder{ | 
					
						
							| 
									
										
										
										
											2019-10-20 13:05:03 +09:00
										 |  |  | 		writer:             w, | 
					
						
							|  |  |  | 		opts:               opts, | 
					
						
							|  |  |  | 		indent:             DefaultIndentSpaces, | 
					
						
							|  |  |  | 		anchorPtrToNameMap: map[uintptr]string{}, | 
					
						
							|  |  |  | 		line:               1, | 
					
						
							|  |  |  | 		column:             1, | 
					
						
							|  |  |  | 		offset:             0, | 
					
						
							| 
									
										
										
										
											2019-10-19 18:28:36 +09:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Close closes the encoder by writing any remaining data. | 
					
						
							|  |  |  | // It does not write a stream terminating string "...". | 
					
						
							|  |  |  | func (e *Encoder) Close() error { | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Encode writes the YAML encoding of v to the stream. | 
					
						
							|  |  |  | // If multiple items are encoded to the stream, | 
					
						
							|  |  |  | // the second and subsequent document will be preceded with a "---" document separator, | 
					
						
							|  |  |  | // but the first will not. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // See the documentation for Marshal for details about the conversion of Go values to YAML. | 
					
						
							|  |  |  | func (e *Encoder) Encode(v interface{}) error { | 
					
						
							| 
									
										
										
										
											2020-11-11 19:27:58 +09:00
										 |  |  | 	return e.EncodeContext(context.Background(), v) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // EncodeContext writes the YAML encoding of v to the stream with context.Context. | 
					
						
							|  |  |  | func (e *Encoder) EncodeContext(ctx context.Context, v interface{}) error { | 
					
						
							|  |  |  | 	node, err := e.EncodeToNodeContext(ctx, v) | 
					
						
							| 
									
										
										
										
											2020-06-22 23:29:54 +09:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return errors.Wrapf(err, "failed to encode to node") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	var p printer.Printer | 
					
						
							|  |  |  | 	e.writer.Write(p.PrintNode(node)) | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // EncodeToNode convert v to ast.Node. | 
					
						
							|  |  |  | func (e *Encoder) EncodeToNode(v interface{}) (ast.Node, error) { | 
					
						
							| 
									
										
										
										
											2020-11-11 19:27:58 +09:00
										 |  |  | 	return e.EncodeToNodeContext(context.Background(), v) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // EncodeToNodeContext convert v to ast.Node with context.Context. | 
					
						
							|  |  |  | func (e *Encoder) EncodeToNodeContext(ctx context.Context, v interface{}) (ast.Node, error) { | 
					
						
							| 
									
										
										
										
											2019-10-31 13:29:43 +09:00
										 |  |  | 	for _, opt := range e.opts { | 
					
						
							|  |  |  | 		if err := opt(e); err != nil { | 
					
						
							| 
									
										
										
										
											2020-06-22 23:29:54 +09:00
										 |  |  | 			return nil, errors.Wrapf(err, "failed to run option for encoder") | 
					
						
							| 
									
										
										
										
											2019-10-31 13:29:43 +09:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-11-11 19:27:58 +09:00
										 |  |  | 	node, err := e.encodeValue(ctx, reflect.ValueOf(v), 1) | 
					
						
							| 
									
										
										
										
											2019-10-19 18:28:36 +09:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2020-06-22 23:29:54 +09:00
										 |  |  | 		return nil, errors.Wrapf(err, "failed to encode value") | 
					
						
							| 
									
										
										
										
											2019-10-19 18:28:36 +09:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-06-22 23:29:54 +09:00
										 |  |  | 	return node, nil | 
					
						
							| 
									
										
										
										
											2019-10-19 18:28:36 +09:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-25 02:38:32 +09:00
										 |  |  | func (e *Encoder) encodeDocument(doc []byte) (ast.Node, error) { | 
					
						
							| 
									
										
										
										
											2019-11-05 17:02:55 +09:00
										 |  |  | 	f, err := parser.ParseBytes(doc, 0) | 
					
						
							| 
									
										
										
										
											2019-10-25 02:38:32 +09:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, errors.Wrapf(err, "failed to parse yaml") | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-11-05 17:02:55 +09:00
										 |  |  | 	for _, docNode := range f.Docs { | 
					
						
							|  |  |  | 		if docNode.Body != nil { | 
					
						
							|  |  |  | 			return docNode.Body, nil | 
					
						
							| 
									
										
										
										
											2019-10-25 02:38:32 +09:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return nil, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-28 21:37:12 +09:00
										 |  |  | func (e *Encoder) isInvalidValue(v reflect.Value) bool { | 
					
						
							|  |  |  | 	if !v.IsValid() { | 
					
						
							|  |  |  | 		return true | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	kind := v.Type().Kind() | 
					
						
							|  |  |  | 	if kind == reflect.Ptr && v.IsNil() { | 
					
						
							|  |  |  | 		return true | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if kind == reflect.Interface && v.IsNil() { | 
					
						
							|  |  |  | 		return true | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return false | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-26 19:00:49 +09:00
										 |  |  | type jsonMarshaler interface { | 
					
						
							|  |  |  | 	MarshalJSON() ([]byte, error) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-11 19:27:58 +09:00
										 |  |  | func (e *Encoder) canEncodeByMarshaler(v reflect.Value) bool { | 
					
						
							|  |  |  | 	if !v.CanInterface() { | 
					
						
							|  |  |  | 		return false | 
					
						
							| 
									
										
										
										
											2019-10-28 21:37:12 +09:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-11-11 19:27:58 +09:00
										 |  |  | 	iface := v.Interface() | 
					
						
							|  |  |  | 	switch iface.(type) { | 
					
						
							|  |  |  | 	case BytesMarshalerContext: | 
					
						
							|  |  |  | 		return true | 
					
						
							|  |  |  | 	case BytesMarshaler: | 
					
						
							|  |  |  | 		return true | 
					
						
							|  |  |  | 	case InterfaceMarshalerContext: | 
					
						
							|  |  |  | 		return true | 
					
						
							|  |  |  | 	case InterfaceMarshaler: | 
					
						
							|  |  |  | 		return true | 
					
						
							|  |  |  | 	case time.Time: | 
					
						
							|  |  |  | 		return true | 
					
						
							|  |  |  | 	case encoding.TextMarshaler: | 
					
						
							|  |  |  | 		return true | 
					
						
							|  |  |  | 	case jsonMarshaler: | 
					
						
							|  |  |  | 		return e.useJSONMarshaler | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return false | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (e *Encoder) encodeByMarshaler(ctx context.Context, v reflect.Value, column int) (ast.Node, error) { | 
					
						
							|  |  |  | 	iface := v.Interface() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if marshaler, ok := iface.(BytesMarshalerContext); ok { | 
					
						
							|  |  |  | 		doc, err := marshaler.MarshalYAML(ctx) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return nil, errors.Wrapf(err, "failed to MarshalYAML") | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		node, err := e.encodeDocument(doc) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return nil, errors.Wrapf(err, "failed to encode document") | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return node, nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if marshaler, ok := iface.(BytesMarshaler); ok { | 
					
						
							|  |  |  | 		doc, err := marshaler.MarshalYAML() | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return nil, errors.Wrapf(err, "failed to MarshalYAML") | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		node, err := e.encodeDocument(doc) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return nil, errors.Wrapf(err, "failed to encode document") | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return node, nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if marshaler, ok := iface.(InterfaceMarshalerContext); ok { | 
					
						
							|  |  |  | 		marshalV, err := marshaler.MarshalYAML(ctx) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return nil, errors.Wrapf(err, "failed to MarshalYAML") | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return e.encodeValue(ctx, reflect.ValueOf(marshalV), column) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if marshaler, ok := iface.(InterfaceMarshaler); ok { | 
					
						
							|  |  |  | 		marshalV, err := marshaler.MarshalYAML() | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return nil, errors.Wrapf(err, "failed to MarshalYAML") | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return e.encodeValue(ctx, reflect.ValueOf(marshalV), column) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if t, ok := iface.(time.Time); ok { | 
					
						
							|  |  |  | 		return e.encodeTime(t, column), nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if marshaler, ok := iface.(encoding.TextMarshaler); ok { | 
					
						
							|  |  |  | 		doc, err := marshaler.MarshalText() | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return nil, errors.Wrapf(err, "failed to MarshalText") | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		node, err := e.encodeDocument(doc) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return nil, errors.Wrapf(err, "failed to encode document") | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return node, nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if e.useJSONMarshaler { | 
					
						
							|  |  |  | 		if marshaler, ok := iface.(jsonMarshaler); ok { | 
					
						
							|  |  |  | 			jsonBytes, err := marshaler.MarshalJSON() | 
					
						
							| 
									
										
										
										
											2019-10-28 21:37:12 +09:00
										 |  |  | 			if err != nil { | 
					
						
							| 
									
										
										
										
											2020-11-11 19:27:58 +09:00
										 |  |  | 				return nil, errors.Wrapf(err, "failed to MarshalJSON") | 
					
						
							| 
									
										
										
										
											2019-10-28 21:37:12 +09:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2020-11-11 19:27:58 +09:00
										 |  |  | 			doc, err := JSONToYAML(jsonBytes) | 
					
						
							| 
									
										
										
										
											2020-01-12 02:50:51 +09:00
										 |  |  | 			if err != nil { | 
					
						
							| 
									
										
										
										
											2020-11-11 19:27:58 +09:00
										 |  |  | 				return nil, errors.Wrapf(err, "failed to convert json to yaml") | 
					
						
							| 
									
										
										
										
											2020-01-12 02:50:51 +09:00
										 |  |  | 			} | 
					
						
							|  |  |  | 			node, err := e.encodeDocument(doc) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				return nil, errors.Wrapf(err, "failed to encode document") | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			return node, nil | 
					
						
							| 
									
										
										
										
											2019-10-24 17:16:29 +09:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-11-11 19:27:58 +09:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return nil, xerrors.Errorf("does not implemented Marshaler") | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (e *Encoder) encodeValue(ctx context.Context, v reflect.Value, column int) (ast.Node, error) { | 
					
						
							|  |  |  | 	if e.isInvalidValue(v) { | 
					
						
							|  |  |  | 		return e.encodeNil(), nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if e.canEncodeByMarshaler(v) { | 
					
						
							|  |  |  | 		node, err := e.encodeByMarshaler(ctx, v, column) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return nil, errors.Wrapf(err, "failed to encode by marshaler") | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return node, nil | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-10-20 19:05:56 +09:00
										 |  |  | 	switch v.Type().Kind() { | 
					
						
							|  |  |  | 	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: | 
					
						
							|  |  |  | 		return e.encodeInt(v.Int()), nil | 
					
						
							|  |  |  | 	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: | 
					
						
							|  |  |  | 		return e.encodeUint(v.Uint()), nil | 
					
						
							|  |  |  | 	case reflect.Float32, reflect.Float64: | 
					
						
							|  |  |  | 		return e.encodeFloat(v.Float()), nil | 
					
						
							| 
									
										
										
										
											2020-03-02 22:58:50 +09:00
										 |  |  | 	case reflect.Ptr: | 
					
						
							|  |  |  | 		anchorName := e.anchorPtrToNameMap[v.Pointer()] | 
					
						
							|  |  |  | 		if anchorName != "" { | 
					
						
							|  |  |  | 			aliasName := anchorName | 
					
						
							| 
									
										
										
										
											2020-06-21 16:22:04 +09:00
										 |  |  | 			alias := ast.Alias(token.New("*", "*", e.pos(column))) | 
					
						
							|  |  |  | 			alias.Value = ast.String(token.New(aliasName, aliasName, e.pos(column))) | 
					
						
							|  |  |  | 			return alias, nil | 
					
						
							| 
									
										
										
										
											2020-03-02 22:58:50 +09:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-11-11 19:27:58 +09:00
										 |  |  | 		return e.encodeValue(ctx, v.Elem(), column) | 
					
						
							| 
									
										
										
										
											2020-03-02 22:58:50 +09:00
										 |  |  | 	case reflect.Interface: | 
					
						
							| 
									
										
										
										
											2020-11-11 19:27:58 +09:00
										 |  |  | 		return e.encodeValue(ctx, v.Elem(), column) | 
					
						
							| 
									
										
										
										
											2019-10-19 18:28:36 +09:00
										 |  |  | 	case reflect.String: | 
					
						
							| 
									
										
										
										
											2019-10-20 19:05:56 +09:00
										 |  |  | 		return e.encodeString(v.String(), column), nil | 
					
						
							| 
									
										
										
										
											2019-10-19 18:28:36 +09:00
										 |  |  | 	case reflect.Bool: | 
					
						
							| 
									
										
										
										
											2019-10-20 19:05:56 +09:00
										 |  |  | 		return e.encodeBool(v.Bool()), nil | 
					
						
							| 
									
										
										
										
											2019-10-19 18:28:36 +09:00
										 |  |  | 	case reflect.Slice: | 
					
						
							| 
									
										
										
										
											2019-10-24 17:16:29 +09:00
										 |  |  | 		if mapSlice, ok := v.Interface().(MapSlice); ok { | 
					
						
							| 
									
										
										
										
											2020-11-11 19:27:58 +09:00
										 |  |  | 			return e.encodeMapSlice(ctx, mapSlice, column) | 
					
						
							| 
									
										
										
										
											2019-10-24 17:16:29 +09:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-11-11 19:27:58 +09:00
										 |  |  | 		return e.encodeSlice(ctx, v) | 
					
						
							| 
									
										
										
										
											2019-10-19 18:28:36 +09:00
										 |  |  | 	case reflect.Struct: | 
					
						
							| 
									
										
										
										
											2019-10-28 21:37:12 +09:00
										 |  |  | 		if v.CanInterface() { | 
					
						
							|  |  |  | 			if mapItem, ok := v.Interface().(MapItem); ok { | 
					
						
							| 
									
										
										
										
											2020-11-11 19:27:58 +09:00
										 |  |  | 				return e.encodeMapItem(ctx, mapItem, column) | 
					
						
							| 
									
										
										
										
											2019-10-28 21:37:12 +09:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2019-11-07 21:14:36 +09:00
										 |  |  | 			if t, ok := v.Interface().(time.Time); ok { | 
					
						
							|  |  |  | 				return e.encodeTime(t, column), nil | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2019-10-24 17:16:29 +09:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-11-11 19:27:58 +09:00
										 |  |  | 		return e.encodeStruct(ctx, v, column) | 
					
						
							| 
									
										
										
										
											2019-10-19 18:28:36 +09:00
										 |  |  | 	case reflect.Map: | 
					
						
							| 
									
										
										
										
											2020-11-11 19:27:58 +09:00
										 |  |  | 		return e.encodeMap(ctx, v, column), nil | 
					
						
							| 
									
										
										
										
											2019-10-19 18:28:36 +09:00
										 |  |  | 	default: | 
					
						
							| 
									
										
										
										
											2019-11-01 14:27:03 +09:00
										 |  |  | 		return nil, xerrors.Errorf("unknown value type %s", v.Type().String()) | 
					
						
							| 
									
										
										
										
											2019-10-19 18:28:36 +09:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	return nil, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (e *Encoder) pos(column int) *token.Position { | 
					
						
							|  |  |  | 	return &token.Position{ | 
					
						
							|  |  |  | 		Line:        e.line, | 
					
						
							|  |  |  | 		Column:      column, | 
					
						
							|  |  |  | 		Offset:      e.offset, | 
					
						
							|  |  |  | 		IndentNum:   e.indentNum, | 
					
						
							|  |  |  | 		IndentLevel: e.indentLevel, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (e *Encoder) encodeNil() ast.Node { | 
					
						
							|  |  |  | 	value := "null" | 
					
						
							|  |  |  | 	return ast.Null(token.New(value, value, e.pos(e.column))) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (e *Encoder) encodeInt(v int64) ast.Node { | 
					
						
							|  |  |  | 	value := fmt.Sprint(v) | 
					
						
							|  |  |  | 	return ast.Integer(token.New(value, value, e.pos(e.column))) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (e *Encoder) encodeUint(v uint64) ast.Node { | 
					
						
							|  |  |  | 	value := fmt.Sprint(v) | 
					
						
							|  |  |  | 	return ast.Integer(token.New(value, value, e.pos(e.column))) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (e *Encoder) encodeFloat(v float64) ast.Node { | 
					
						
							|  |  |  | 	if v == math.Inf(0) { | 
					
						
							|  |  |  | 		value := ".inf" | 
					
						
							|  |  |  | 		return ast.Infinity(token.New(value, value, e.pos(e.column))) | 
					
						
							|  |  |  | 	} else if v == math.Inf(-1) { | 
					
						
							|  |  |  | 		value := "-.inf" | 
					
						
							|  |  |  | 		return ast.Infinity(token.New(value, value, e.pos(e.column))) | 
					
						
							|  |  |  | 	} else if math.IsNaN(v) { | 
					
						
							|  |  |  | 		value := ".nan" | 
					
						
							|  |  |  | 		return ast.Nan(token.New(value, value, e.pos(e.column))) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	value := fmt.Sprintf("%f", v) | 
					
						
							|  |  |  | 	fvalue := strings.Split(value, ".") | 
					
						
							|  |  |  | 	if len(fvalue) > 1 { | 
					
						
							|  |  |  | 		precision := fvalue[1] | 
					
						
							|  |  |  | 		precisionNum := 1 | 
					
						
							|  |  |  | 		for i := len(precision) - 1; i >= 0; i-- { | 
					
						
							|  |  |  | 			if precision[i] != '0' { | 
					
						
							|  |  |  | 				precisionNum = i + 1 | 
					
						
							|  |  |  | 				break | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		value = strconv.FormatFloat(v, 'f', precisionNum, 64) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return ast.Float(token.New(value, value, e.pos(e.column))) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (e *Encoder) encodeString(v string, column int) ast.Node { | 
					
						
							| 
									
										
										
										
											2020-06-02 19:56:02 +09:00
										 |  |  | 	if e.isJSONStyle || token.IsNeedQuoted(v) { | 
					
						
							| 
									
										
										
										
											2019-10-19 18:28:36 +09:00
										 |  |  | 		v = strconv.Quote(v) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return ast.String(token.New(v, v, e.pos(column))) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (e *Encoder) encodeBool(v bool) ast.Node { | 
					
						
							|  |  |  | 	value := fmt.Sprint(v) | 
					
						
							|  |  |  | 	return ast.Bool(token.New(value, value, e.pos(e.column))) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-11 19:27:58 +09:00
										 |  |  | func (e *Encoder) encodeSlice(ctx context.Context, value reflect.Value) (ast.Node, error) { | 
					
						
							| 
									
										
										
										
											2020-03-09 21:59:00 +09:00
										 |  |  | 	sequence := ast.Sequence(token.New("-", "-", e.pos(e.column)), e.isFlowStyle) | 
					
						
							| 
									
										
										
										
											2019-10-19 18:28:36 +09:00
										 |  |  | 	for i := 0; i < value.Len(); i++ { | 
					
						
							| 
									
										
										
										
											2020-11-11 19:27:58 +09:00
										 |  |  | 		node, err := e.encodeValue(ctx, value.Index(i), e.column) | 
					
						
							| 
									
										
										
										
											2019-10-19 18:28:36 +09:00
										 |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2019-10-25 02:38:32 +09:00
										 |  |  | 			return nil, errors.Wrapf(err, "failed to encode value for slice") | 
					
						
							| 
									
										
										
										
											2019-10-19 18:28:36 +09:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		sequence.Values = append(sequence.Values, node) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-10-25 02:38:32 +09:00
										 |  |  | 	return sequence, nil | 
					
						
							| 
									
										
										
										
											2019-10-19 18:28:36 +09:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-11 19:27:58 +09:00
										 |  |  | func (e *Encoder) encodeMapItem(ctx context.Context, item MapItem, column int) (*ast.MappingValueNode, error) { | 
					
						
							| 
									
										
										
										
											2019-10-24 17:16:29 +09:00
										 |  |  | 	k := reflect.ValueOf(item.Key) | 
					
						
							|  |  |  | 	v := reflect.ValueOf(item.Value) | 
					
						
							| 
									
										
										
										
											2020-11-11 19:27:58 +09:00
										 |  |  | 	value, err := e.encodeValue(ctx, v, column) | 
					
						
							| 
									
										
										
										
											2019-10-24 17:16:29 +09:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, errors.Wrapf(err, "failed to encode MapItem") | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-10-31 13:07:00 +09:00
										 |  |  | 	if m, ok := value.(*ast.MappingNode); ok { | 
					
						
							| 
									
										
										
										
											2019-12-12 17:38:01 +09:00
										 |  |  | 		m.AddColumn(e.indent) | 
					
						
							| 
									
										
										
										
											2019-10-24 17:16:29 +09:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-06-21 16:22:04 +09:00
										 |  |  | 	return ast.MappingValue( | 
					
						
							|  |  |  | 		token.New("", "", e.pos(column)), | 
					
						
							|  |  |  | 		e.encodeString(k.Interface().(string), column), | 
					
						
							|  |  |  | 		value, | 
					
						
							|  |  |  | 	), nil | 
					
						
							| 
									
										
										
										
											2019-10-24 17:16:29 +09:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-11 19:27:58 +09:00
										 |  |  | func (e *Encoder) encodeMapSlice(ctx context.Context, value MapSlice, column int) (ast.Node, error) { | 
					
						
							| 
									
										
										
										
											2019-10-31 13:29:43 +09:00
										 |  |  | 	node := ast.Mapping(token.New("", "", e.pos(column)), e.isFlowStyle) | 
					
						
							| 
									
										
										
										
											2019-10-24 17:16:29 +09:00
										 |  |  | 	for _, item := range value { | 
					
						
							| 
									
										
										
										
											2020-11-11 19:27:58 +09:00
										 |  |  | 		value, err := e.encodeMapItem(ctx, item, column) | 
					
						
							| 
									
										
										
										
											2019-10-24 17:16:29 +09:00
										 |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return nil, errors.Wrapf(err, "failed to encode MapItem for MapSlice") | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		node.Values = append(node.Values, value) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return node, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-11 19:27:58 +09:00
										 |  |  | func (e *Encoder) encodeMap(ctx context.Context, value reflect.Value, column int) ast.Node { | 
					
						
							| 
									
										
										
										
											2020-03-09 21:59:00 +09:00
										 |  |  | 	node := ast.Mapping(token.New("", "", e.pos(column)), e.isFlowStyle) | 
					
						
							| 
									
										
										
										
											2019-10-19 18:28:36 +09:00
										 |  |  | 	keys := []string{} | 
					
						
							|  |  |  | 	for _, k := range value.MapKeys() { | 
					
						
							|  |  |  | 		keys = append(keys, k.Interface().(string)) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	sort.Strings(keys) | 
					
						
							|  |  |  | 	for _, key := range keys { | 
					
						
							|  |  |  | 		k := reflect.ValueOf(key) | 
					
						
							|  |  |  | 		v := value.MapIndex(k) | 
					
						
							| 
									
										
										
										
											2020-11-11 19:27:58 +09:00
										 |  |  | 		value, err := e.encodeValue(ctx, v, column) | 
					
						
							| 
									
										
										
										
											2019-10-19 18:28:36 +09:00
										 |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return nil | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-10-31 13:07:00 +09:00
										 |  |  | 		if m, ok := value.(*ast.MappingNode); ok { | 
					
						
							| 
									
										
										
										
											2019-12-12 17:38:01 +09:00
										 |  |  | 			m.AddColumn(e.indent) | 
					
						
							| 
									
										
										
										
											2019-10-19 18:28:36 +09:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-06-21 16:22:04 +09:00
										 |  |  | 		node.Values = append(node.Values, ast.MappingValue( | 
					
						
							|  |  |  | 			nil, | 
					
						
							|  |  |  | 			e.encodeString(k.Interface().(string), column), | 
					
						
							|  |  |  | 			value, | 
					
						
							|  |  |  | 		)) | 
					
						
							| 
									
										
										
										
											2019-10-19 18:28:36 +09:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	return node | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-20 20:50:15 +09:00
										 |  |  | // IsZeroer is used to check whether an object is zero to determine | 
					
						
							|  |  |  | // whether it should be omitted when marshaling with the omitempty flag. | 
					
						
							|  |  |  | // One notable implementation is time.Time. | 
					
						
							|  |  |  | type IsZeroer interface { | 
					
						
							|  |  |  | 	IsZero() bool | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (e *Encoder) isZeroValue(v reflect.Value) bool { | 
					
						
							|  |  |  | 	kind := v.Kind() | 
					
						
							|  |  |  | 	if z, ok := v.Interface().(IsZeroer); ok { | 
					
						
							|  |  |  | 		if (kind == reflect.Ptr || kind == reflect.Interface) && v.IsNil() { | 
					
						
							|  |  |  | 			return true | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return z.IsZero() | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	switch kind { | 
					
						
							|  |  |  | 	case reflect.String: | 
					
						
							|  |  |  | 		return len(v.String()) == 0 | 
					
						
							|  |  |  | 	case reflect.Interface, reflect.Ptr: | 
					
						
							|  |  |  | 		return v.IsNil() | 
					
						
							|  |  |  | 	case reflect.Slice: | 
					
						
							|  |  |  | 		return v.Len() == 0 | 
					
						
							|  |  |  | 	case reflect.Map: | 
					
						
							|  |  |  | 		return v.Len() == 0 | 
					
						
							|  |  |  | 	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: | 
					
						
							|  |  |  | 		return v.Int() == 0 | 
					
						
							|  |  |  | 	case reflect.Float32, reflect.Float64: | 
					
						
							|  |  |  | 		return v.Float() == 0 | 
					
						
							|  |  |  | 	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: | 
					
						
							|  |  |  | 		return v.Uint() == 0 | 
					
						
							|  |  |  | 	case reflect.Bool: | 
					
						
							|  |  |  | 		return !v.Bool() | 
					
						
							|  |  |  | 	case reflect.Struct: | 
					
						
							|  |  |  | 		vt := v.Type() | 
					
						
							|  |  |  | 		for i := v.NumField() - 1; i >= 0; i-- { | 
					
						
							|  |  |  | 			if vt.Field(i).PkgPath != "" { | 
					
						
							|  |  |  | 				continue // private field | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			if !e.isZeroValue(v.Field(i)) { | 
					
						
							|  |  |  | 				return false | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return true | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return false | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-07 21:14:36 +09:00
										 |  |  | func (e *Encoder) encodeTime(v time.Time, column int) ast.Node { | 
					
						
							|  |  |  | 	value := v.Format(time.RFC3339Nano) | 
					
						
							| 
									
										
										
										
											2020-06-02 19:56:02 +09:00
										 |  |  | 	if e.isJSONStyle { | 
					
						
							|  |  |  | 		value = strconv.Quote(value) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-11-07 21:14:36 +09:00
										 |  |  | 	return ast.String(token.New(value, value, e.pos(column))) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-09 13:33:10 +09:00
										 |  |  | func (e *Encoder) encodeAnchor(anchorName string, value ast.Node, fieldValue reflect.Value, column int) (ast.Node, error) { | 
					
						
							| 
									
										
										
										
											2020-06-21 16:22:04 +09:00
										 |  |  | 	anchorNode := ast.Anchor(token.New("&", "&", e.pos(column))) | 
					
						
							|  |  |  | 	anchorNode.Name = ast.String(token.New(anchorName, anchorName, e.pos(column))) | 
					
						
							|  |  |  | 	anchorNode.Value = value | 
					
						
							| 
									
										
										
										
											2020-01-09 13:33:10 +09:00
										 |  |  | 	if e.anchorCallback != nil { | 
					
						
							|  |  |  | 		if err := e.anchorCallback(anchorNode, fieldValue.Interface()); err != nil { | 
					
						
							|  |  |  | 			return nil, errors.Wrapf(err, "failed to marshal anchor") | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if snode, ok := anchorNode.Name.(*ast.StringNode); ok { | 
					
						
							|  |  |  | 			anchorName = snode.Value | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if fieldValue.Kind() == reflect.Ptr { | 
					
						
							|  |  |  | 		e.anchorPtrToNameMap[fieldValue.Pointer()] = anchorName | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return anchorNode, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-11 19:27:58 +09:00
										 |  |  | func (e *Encoder) encodeStruct(ctx context.Context, value reflect.Value, column int) (ast.Node, error) { | 
					
						
							| 
									
										
										
										
											2019-10-31 13:29:43 +09:00
										 |  |  | 	node := ast.Mapping(token.New("", "", e.pos(column)), e.isFlowStyle) | 
					
						
							| 
									
										
										
										
											2019-10-19 18:28:36 +09:00
										 |  |  | 	structType := value.Type() | 
					
						
							|  |  |  | 	structFieldMap, err := structFieldMap(structType) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2019-10-23 16:40:26 +09:00
										 |  |  | 		return nil, errors.Wrapf(err, "failed to get struct field map") | 
					
						
							| 
									
										
										
										
											2019-10-19 18:28:36 +09:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-03-02 22:58:50 +09:00
										 |  |  | 	hasInlineAnchorField := false | 
					
						
							|  |  |  | 	var inlineAnchorValue reflect.Value | 
					
						
							| 
									
										
										
										
											2019-10-19 18:28:36 +09:00
										 |  |  | 	for i := 0; i < value.NumField(); i++ { | 
					
						
							|  |  |  | 		field := structType.Field(i) | 
					
						
							|  |  |  | 		if isIgnoredStructField(field) { | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		fieldValue := value.FieldByName(field.Name) | 
					
						
							|  |  |  | 		structField := structFieldMap[field.Name] | 
					
						
							| 
									
										
										
										
											2019-10-20 20:50:15 +09:00
										 |  |  | 		if structField.IsOmitEmpty && e.isZeroValue(fieldValue) { | 
					
						
							|  |  |  | 			// omit encoding | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-11-11 19:27:58 +09:00
										 |  |  | 		value, err := e.encodeValue(ctx, fieldValue, column) | 
					
						
							| 
									
										
										
										
											2019-10-19 18:28:36 +09:00
										 |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2019-10-23 16:40:26 +09:00
										 |  |  | 			return nil, errors.Wrapf(err, "failed to encode value") | 
					
						
							| 
									
										
										
										
											2019-10-19 18:28:36 +09:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-10-31 13:07:00 +09:00
										 |  |  | 		if m, ok := value.(*ast.MappingNode); ok { | 
					
						
							| 
									
										
										
										
											2019-10-31 13:29:43 +09:00
										 |  |  | 			if !e.isFlowStyle && structField.IsFlow { | 
					
						
							| 
									
										
										
										
											2019-10-31 13:07:00 +09:00
										 |  |  | 				m.IsFlowStyle = true | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2019-12-12 17:38:01 +09:00
										 |  |  | 			value.AddColumn(e.indent) | 
					
						
							| 
									
										
										
										
											2019-10-31 13:07:00 +09:00
										 |  |  | 		} else if s, ok := value.(*ast.SequenceNode); ok { | 
					
						
							| 
									
										
										
										
											2019-10-31 13:29:43 +09:00
										 |  |  | 			if !e.isFlowStyle && structField.IsFlow { | 
					
						
							| 
									
										
										
										
											2019-10-31 13:07:00 +09:00
										 |  |  | 				s.IsFlowStyle = true | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2019-10-19 18:28:36 +09:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-10-20 21:47:06 +09:00
										 |  |  | 		key := e.encodeString(structField.RenderName, column) | 
					
						
							| 
									
										
										
										
											2019-10-28 21:37:12 +09:00
										 |  |  | 		switch { | 
					
						
							|  |  |  | 		case structField.AnchorName != "": | 
					
						
							| 
									
										
										
										
											2020-01-09 13:33:10 +09:00
										 |  |  | 			anchorNode, err := e.encodeAnchor(structField.AnchorName, value, fieldValue, column) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				return nil, errors.Wrapf(err, "failed to encode anchor") | 
					
						
							| 
									
										
										
										
											2019-10-20 13:31:29 +09:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2020-01-09 13:33:10 +09:00
										 |  |  | 			value = anchorNode | 
					
						
							| 
									
										
										
										
											2019-10-28 21:37:12 +09:00
										 |  |  | 		case structField.IsAutoAlias: | 
					
						
							| 
									
										
										
										
											2019-10-20 13:05:03 +09:00
										 |  |  | 			if fieldValue.Kind() != reflect.Ptr { | 
					
						
							| 
									
										
										
										
											2019-10-20 19:05:56 +09:00
										 |  |  | 				return nil, xerrors.Errorf( | 
					
						
							|  |  |  | 					"%s in struct is not pointer type. but required automatically alias detection", | 
					
						
							|  |  |  | 					structField.FieldName, | 
					
						
							|  |  |  | 				) | 
					
						
							| 
									
										
										
										
											2019-10-20 13:05:03 +09:00
										 |  |  | 			} | 
					
						
							|  |  |  | 			anchorName := e.anchorPtrToNameMap[fieldValue.Pointer()] | 
					
						
							|  |  |  | 			if anchorName == "" { | 
					
						
							| 
									
										
										
										
											2019-10-20 19:05:56 +09:00
										 |  |  | 				return nil, xerrors.Errorf( | 
					
						
							|  |  |  | 					"cannot find anchor name from pointer address for automatically alias detection", | 
					
						
							|  |  |  | 				) | 
					
						
							| 
									
										
										
										
											2019-10-20 13:05:03 +09:00
										 |  |  | 			} | 
					
						
							|  |  |  | 			aliasName := anchorName | 
					
						
							| 
									
										
										
										
											2020-06-21 16:22:04 +09:00
										 |  |  | 			alias := ast.Alias(token.New("*", "*", e.pos(column))) | 
					
						
							|  |  |  | 			alias.Value = ast.String(token.New(aliasName, aliasName, e.pos(column))) | 
					
						
							|  |  |  | 			value = alias | 
					
						
							| 
									
										
										
										
											2019-10-20 21:47:06 +09:00
										 |  |  | 			if structField.IsInline { | 
					
						
							|  |  |  | 				// if both used alias and inline, output `<<: *alias` | 
					
						
							|  |  |  | 				key = ast.MergeKey(token.New("<<", "<<", e.pos(column))) | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2019-10-28 21:37:12 +09:00
										 |  |  | 		case structField.AliasName != "": | 
					
						
							| 
									
										
										
										
											2019-10-19 22:01:36 +09:00
										 |  |  | 			aliasName := structField.AliasName | 
					
						
							| 
									
										
										
										
											2020-06-21 16:22:04 +09:00
										 |  |  | 			alias := ast.Alias(token.New("*", "*", e.pos(column))) | 
					
						
							|  |  |  | 			alias.Value = ast.String(token.New(aliasName, aliasName, e.pos(column))) | 
					
						
							|  |  |  | 			value = alias | 
					
						
							| 
									
										
										
										
											2019-10-20 21:47:06 +09:00
										 |  |  | 			if structField.IsInline { | 
					
						
							|  |  |  | 				// if both used alias and inline, output `<<: *alias` | 
					
						
							|  |  |  | 				key = ast.MergeKey(token.New("<<", "<<", e.pos(column))) | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2019-10-28 21:37:12 +09:00
										 |  |  | 		case structField.IsInline: | 
					
						
							| 
									
										
										
										
											2020-03-02 22:58:50 +09:00
										 |  |  | 			isAutoAnchor := structField.IsAutoAnchor | 
					
						
							|  |  |  | 			if !hasInlineAnchorField { | 
					
						
							|  |  |  | 				hasInlineAnchorField = isAutoAnchor | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			if isAutoAnchor { | 
					
						
							|  |  |  | 				inlineAnchorValue = fieldValue | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2019-10-28 21:37:12 +09:00
										 |  |  | 			mapNode, ok := value.(ast.MapNode) | 
					
						
							|  |  |  | 			if !ok { | 
					
						
							|  |  |  | 				return nil, xerrors.Errorf("inline value is must be map or struct type") | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			mapIter := mapNode.MapRange() | 
					
						
							|  |  |  | 			for mapIter.Next() { | 
					
						
							|  |  |  | 				key := mapIter.Key() | 
					
						
							|  |  |  | 				value := mapIter.Value() | 
					
						
							| 
									
										
										
										
											2019-10-28 22:25:00 +09:00
										 |  |  | 				keyName := key.GetToken().Value | 
					
						
							|  |  |  | 				if structFieldMap.isIncludedRenderName(keyName) { | 
					
						
							|  |  |  | 					// if declared same key name, skip encoding this field | 
					
						
							|  |  |  | 					continue | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2019-12-12 17:38:01 +09:00
										 |  |  | 				key.AddColumn(-e.indent) | 
					
						
							|  |  |  | 				value.AddColumn(-e.indent) | 
					
						
							| 
									
										
										
										
											2020-06-21 16:22:04 +09:00
										 |  |  | 				node.Values = append(node.Values, ast.MappingValue(nil, key, value)) | 
					
						
							| 
									
										
										
										
											2019-10-28 21:37:12 +09:00
										 |  |  | 			} | 
					
						
							|  |  |  | 			continue | 
					
						
							| 
									
										
										
										
											2020-03-02 22:58:50 +09:00
										 |  |  | 		case structField.IsAutoAnchor: | 
					
						
							|  |  |  | 			anchorNode, err := e.encodeAnchor(structField.RenderName, value, fieldValue, column) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				return nil, errors.Wrapf(err, "failed to encode anchor") | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			value = anchorNode | 
					
						
							| 
									
										
										
										
											2019-10-19 22:01:36 +09:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-06-21 16:22:04 +09:00
										 |  |  | 		node.Values = append(node.Values, ast.MappingValue(nil, key, value)) | 
					
						
							| 
									
										
										
										
											2019-10-19 18:28:36 +09:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-03-02 22:58:50 +09:00
										 |  |  | 	if hasInlineAnchorField { | 
					
						
							|  |  |  | 		node.AddColumn(e.indent) | 
					
						
							|  |  |  | 		anchorName := "anchor" | 
					
						
							| 
									
										
										
										
											2020-06-21 16:22:04 +09:00
										 |  |  | 		anchorNode := ast.Anchor(token.New("&", "&", e.pos(column))) | 
					
						
							|  |  |  | 		anchorNode.Name = ast.String(token.New(anchorName, anchorName, e.pos(column))) | 
					
						
							|  |  |  | 		anchorNode.Value = node | 
					
						
							| 
									
										
										
										
											2020-03-02 22:58:50 +09:00
										 |  |  | 		if e.anchorCallback != nil { | 
					
						
							|  |  |  | 			if err := e.anchorCallback(anchorNode, value.Addr().Interface()); err != nil { | 
					
						
							|  |  |  | 				return nil, errors.Wrapf(err, "failed to marshal anchor") | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			if snode, ok := anchorNode.Name.(*ast.StringNode); ok { | 
					
						
							|  |  |  | 				anchorName = snode.Value | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if inlineAnchorValue.Kind() == reflect.Ptr { | 
					
						
							|  |  |  | 			e.anchorPtrToNameMap[inlineAnchorValue.Pointer()] = anchorName | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return anchorNode, nil | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-10-20 19:05:56 +09:00
										 |  |  | 	return node, nil | 
					
						
							| 
									
										
										
										
											2019-10-19 18:28:36 +09:00
										 |  |  | } |