| 
									
										
										
										
											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" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 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 { | 
					
						
							| 
									
										
										
										
											2020-11-18 07:35:17 +02:00
										 |  |  | 	writer                     io.Writer | 
					
						
							|  |  |  | 	opts                       []EncodeOption | 
					
						
							| 
									
										
										
										
											2021-08-09 14:54:53 +00:00
										 |  |  | 	singleQuote                bool | 
					
						
							| 
									
										
										
										
											2020-11-18 07:35:17 +02:00
										 |  |  | 	isFlowStyle                bool | 
					
						
							|  |  |  | 	isJSONStyle                bool | 
					
						
							| 
									
										
										
										
											2020-11-18 07:36:24 +02:00
										 |  |  | 	useJSONMarshaler           bool | 
					
						
							| 
									
										
										
										
											2025-02-16 20:52:25 +09:00
										 |  |  | 	enableSmartAnchor          bool | 
					
						
							|  |  |  | 	aliasRefToName             map[uintptr]string | 
					
						
							|  |  |  | 	anchorRefToName            map[uintptr]string | 
					
						
							|  |  |  | 	anchorNameMap              map[string]struct{} | 
					
						
							| 
									
										
										
										
											2020-11-18 07:35:17 +02:00
										 |  |  | 	anchorCallback             func(*ast.AnchorNode, interface{}) error | 
					
						
							| 
									
										
										
										
											2023-04-02 10:50:03 +09:00
										 |  |  | 	customMarshalerMap         map[reflect.Type]func(interface{}) ([]byte, error) | 
					
						
							| 
									
										
										
										
											2025-04-11 12:13:15 +02:00
										 |  |  | 	omitEmpty                  bool | 
					
						
							| 
									
										
										
										
											2025-02-25 14:44:58 +01:00
										 |  |  | 	autoInt                    bool | 
					
						
							| 
									
										
										
										
											2020-11-18 07:35:17 +02:00
										 |  |  | 	useLiteralStyleIfMultiline bool | 
					
						
							| 
									
										
										
										
											2023-03-01 16:59:07 +09:00
										 |  |  | 	commentMap                 map[*Path][]*Comment | 
					
						
							| 
									
										
										
										
											2022-10-25 00:51:54 +09:00
										 |  |  | 	written                    bool | 
					
						
							| 
									
										
										
										
											2019-10-19 18:28:36 +09:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-01-31 02:27:06 +01:00
										 |  |  | 	line           int | 
					
						
							|  |  |  | 	column         int | 
					
						
							|  |  |  | 	offset         int | 
					
						
							|  |  |  | 	indentNum      int | 
					
						
							|  |  |  | 	indentLevel    int | 
					
						
							|  |  |  | 	indentSequence bool | 
					
						
							| 
									
										
										
										
											2019-10-19 18:28:36 +09:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // 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, | 
					
						
							| 
									
										
										
										
											2023-04-02 10:50:03 +09:00
										 |  |  | 		customMarshalerMap: map[reflect.Type]func(interface{}) ([]byte, error){}, | 
					
						
							| 
									
										
										
										
											2019-10-20 13:05:03 +09:00
										 |  |  | 		line:               1, | 
					
						
							|  |  |  | 		column:             1, | 
					
						
							|  |  |  | 		offset:             0, | 
					
						
							| 
									
										
										
										
											2025-01-31 02:27:06 +01:00
										 |  |  | 		indentNum:          DefaultIndentSpaces, | 
					
						
							| 
									
										
										
										
											2025-02-16 20:52:25 +09:00
										 |  |  | 		anchorRefToName:    make(map[uintptr]string), | 
					
						
							|  |  |  | 		anchorNameMap:      make(map[string]struct{}), | 
					
						
							|  |  |  | 		aliasRefToName:     make(map[uintptr]string), | 
					
						
							| 
									
										
										
										
											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 { | 
					
						
							| 
									
										
										
										
											2024-10-28 21:24:15 +09:00
										 |  |  | 		return err | 
					
						
							| 
									
										
										
										
											2020-06-22 23:29:54 +09:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-07-20 15:03:36 +09:00
										 |  |  | 	if err := e.setCommentByCommentMap(node); err != nil { | 
					
						
							| 
									
										
										
										
											2024-10-28 21:24:15 +09:00
										 |  |  | 		return err | 
					
						
							| 
									
										
										
										
											2021-07-20 14:26:02 +09:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2022-10-25 00:51:54 +09:00
										 |  |  | 	if !e.written { | 
					
						
							|  |  |  | 		e.written = true | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		// write document separator | 
					
						
							| 
									
										
										
										
											2024-10-28 11:31:15 +09:00
										 |  |  | 		_, _ = e.writer.Write([]byte("---\n")) | 
					
						
							| 
									
										
										
										
											2022-10-25 00:51:54 +09:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-06-22 23:29:54 +09:00
										 |  |  | 	var p printer.Printer | 
					
						
							| 
									
										
										
										
											2024-10-28 11:31:15 +09:00
										 |  |  | 	_, _ = e.writer.Write(p.PrintNode(node)) | 
					
						
							| 
									
										
										
										
											2020-06-22 23:29:54 +09:00
										 |  |  | 	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 { | 
					
						
							| 
									
										
										
										
											2024-10-28 21:24:15 +09:00
										 |  |  | 			return nil, err | 
					
						
							| 
									
										
										
										
											2019-10-31 13:29:43 +09:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2025-02-16 20:52:25 +09:00
										 |  |  | 	if e.enableSmartAnchor { | 
					
						
							|  |  |  | 		// during the first encoding, store all mappings between alias addresses and their names. | 
					
						
							|  |  |  | 		if _, err := e.encodeValue(ctx, reflect.ValueOf(v), 1); err != nil { | 
					
						
							|  |  |  | 			return nil, err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		e.clearSmartAnchorRef() | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											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 { | 
					
						
							| 
									
										
										
										
											2024-10-28 21:24:15 +09:00
										 |  |  | 		return nil, err | 
					
						
							| 
									
										
										
										
											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
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-20 14:26:02 +09:00
										 |  |  | func (e *Encoder) setCommentByCommentMap(node ast.Node) error { | 
					
						
							| 
									
										
										
										
											2021-07-20 15:03:36 +09:00
										 |  |  | 	if e.commentMap == nil { | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-03-01 16:59:07 +09:00
										 |  |  | 	for path, comments := range e.commentMap { | 
					
						
							| 
									
										
										
										
											2021-07-20 14:26:02 +09:00
										 |  |  | 		n, err := path.FilterNode(node) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2024-10-28 21:24:15 +09:00
										 |  |  | 			return err | 
					
						
							| 
									
										
										
										
											2021-07-20 14:26:02 +09:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2023-03-01 16:59:07 +09:00
										 |  |  | 		if n == nil { | 
					
						
							|  |  |  | 			continue | 
					
						
							| 
									
										
										
										
											2021-07-20 14:26:02 +09:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2023-03-01 16:59:07 +09:00
										 |  |  | 		for _, comment := range comments { | 
					
						
							|  |  |  | 			commentTokens := []*token.Token{} | 
					
						
							|  |  |  | 			for _, text := range comment.Texts { | 
					
						
							|  |  |  | 				commentTokens = append(commentTokens, token.New(text, text, nil)) | 
					
						
							| 
									
										
										
										
											2021-07-20 14:26:02 +09:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2023-03-01 16:59:07 +09:00
										 |  |  | 			commentGroup := ast.CommentGroup(commentTokens) | 
					
						
							|  |  |  | 			switch comment.Position { | 
					
						
							|  |  |  | 			case CommentHeadPosition: | 
					
						
							|  |  |  | 				if err := e.setHeadComment(node, n, commentGroup); err != nil { | 
					
						
							| 
									
										
										
										
											2024-10-28 21:24:15 +09:00
										 |  |  | 					return err | 
					
						
							| 
									
										
										
										
											2023-03-01 16:59:07 +09:00
										 |  |  | 				} | 
					
						
							|  |  |  | 			case CommentLinePosition: | 
					
						
							|  |  |  | 				if err := e.setLineComment(node, n, commentGroup); err != nil { | 
					
						
							| 
									
										
										
										
											2024-10-28 21:24:15 +09:00
										 |  |  | 					return err | 
					
						
							| 
									
										
										
										
											2021-07-20 14:26:02 +09:00
										 |  |  | 				} | 
					
						
							| 
									
										
										
										
											2023-03-01 16:59:07 +09:00
										 |  |  | 			case CommentFootPosition: | 
					
						
							|  |  |  | 				if err := e.setFootComment(node, n, commentGroup); err != nil { | 
					
						
							| 
									
										
										
										
											2024-10-28 21:24:15 +09:00
										 |  |  | 					return err | 
					
						
							| 
									
										
										
										
											2021-07-20 14:26:02 +09:00
										 |  |  | 				} | 
					
						
							|  |  |  | 			default: | 
					
						
							| 
									
										
										
										
											2023-03-01 16:59:07 +09:00
										 |  |  | 				return ErrUnknownCommentPositionType | 
					
						
							| 
									
										
										
										
											2021-07-20 14:26:02 +09:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-01 16:59:07 +09:00
										 |  |  | func (e *Encoder) setHeadComment(node ast.Node, filtered ast.Node, comment *ast.CommentGroupNode) error { | 
					
						
							|  |  |  | 	parent := ast.Parent(node, filtered) | 
					
						
							|  |  |  | 	if parent == nil { | 
					
						
							|  |  |  | 		return ErrUnsupportedHeadPositionType(node) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	switch p := parent.(type) { | 
					
						
							|  |  |  | 	case *ast.MappingValueNode: | 
					
						
							|  |  |  | 		if err := p.SetComment(comment); err != nil { | 
					
						
							| 
									
										
										
										
											2024-10-28 21:24:15 +09:00
										 |  |  | 			return err | 
					
						
							| 
									
										
										
										
											2023-03-01 16:59:07 +09:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	case *ast.MappingNode: | 
					
						
							|  |  |  | 		if err := p.SetComment(comment); err != nil { | 
					
						
							| 
									
										
										
										
											2024-10-28 21:24:15 +09:00
										 |  |  | 			return err | 
					
						
							| 
									
										
										
										
											2023-03-01 16:59:07 +09:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	case *ast.SequenceNode: | 
					
						
							|  |  |  | 		if len(p.ValueHeadComments) == 0 { | 
					
						
							|  |  |  | 			p.ValueHeadComments = make([]*ast.CommentGroupNode, len(p.Values)) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		var foundIdx int | 
					
						
							|  |  |  | 		for idx, v := range p.Values { | 
					
						
							|  |  |  | 			if v == filtered { | 
					
						
							|  |  |  | 				foundIdx = idx | 
					
						
							|  |  |  | 				break | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		p.ValueHeadComments[foundIdx] = comment | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		return ErrUnsupportedHeadPositionType(node) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (e *Encoder) setLineComment(node ast.Node, filtered ast.Node, comment *ast.CommentGroupNode) error { | 
					
						
							|  |  |  | 	switch filtered.(type) { | 
					
						
							|  |  |  | 	case *ast.MappingValueNode, *ast.SequenceNode: | 
					
						
							|  |  |  | 		// Line comment cannot be set for mapping value node. | 
					
						
							|  |  |  | 		// It should probably be set for the parent map node | 
					
						
							|  |  |  | 		if err := e.setLineCommentToParentMapNode(node, filtered, comment); err != nil { | 
					
						
							| 
									
										
										
										
											2024-10-28 21:24:15 +09:00
										 |  |  | 			return err | 
					
						
							| 
									
										
										
										
											2023-03-01 16:59:07 +09:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		if err := filtered.SetComment(comment); err != nil { | 
					
						
							| 
									
										
										
										
											2024-10-28 21:24:15 +09:00
										 |  |  | 			return err | 
					
						
							| 
									
										
										
										
											2023-03-01 16:59:07 +09:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (e *Encoder) setLineCommentToParentMapNode(node ast.Node, filtered ast.Node, comment *ast.CommentGroupNode) error { | 
					
						
							|  |  |  | 	parent := ast.Parent(node, filtered) | 
					
						
							|  |  |  | 	if parent == nil { | 
					
						
							|  |  |  | 		return ErrUnsupportedLinePositionType(node) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	switch p := parent.(type) { | 
					
						
							|  |  |  | 	case *ast.MappingValueNode: | 
					
						
							|  |  |  | 		if err := p.Key.SetComment(comment); err != nil { | 
					
						
							| 
									
										
										
										
											2024-10-28 21:24:15 +09:00
										 |  |  | 			return err | 
					
						
							| 
									
										
										
										
											2023-03-01 16:59:07 +09:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	case *ast.MappingNode: | 
					
						
							|  |  |  | 		if err := p.SetComment(comment); err != nil { | 
					
						
							| 
									
										
										
										
											2024-10-28 21:24:15 +09:00
										 |  |  | 			return err | 
					
						
							| 
									
										
										
										
											2023-03-01 16:59:07 +09:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		return ErrUnsupportedLinePositionType(parent) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (e *Encoder) setFootComment(node ast.Node, filtered ast.Node, comment *ast.CommentGroupNode) error { | 
					
						
							|  |  |  | 	parent := ast.Parent(node, filtered) | 
					
						
							|  |  |  | 	if parent == nil { | 
					
						
							|  |  |  | 		return ErrUnsupportedFootPositionType(node) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	switch n := parent.(type) { | 
					
						
							|  |  |  | 	case *ast.MappingValueNode: | 
					
						
							|  |  |  | 		n.FootComment = comment | 
					
						
							|  |  |  | 	case *ast.MappingNode: | 
					
						
							|  |  |  | 		n.FootComment = comment | 
					
						
							|  |  |  | 	case *ast.SequenceNode: | 
					
						
							|  |  |  | 		n.FootComment = comment | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		return ErrUnsupportedFootPositionType(n) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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 { | 
					
						
							| 
									
										
										
										
											2024-10-28 21:24:15 +09:00
										 |  |  | 		return nil, err | 
					
						
							| 
									
										
										
										
											2019-10-25 02:38:32 +09:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											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) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-02 10:50:03 +09:00
										 |  |  | func (e *Encoder) existsTypeInCustomMarshalerMap(t reflect.Type) bool { | 
					
						
							|  |  |  | 	if _, exists := e.customMarshalerMap[t]; exists { | 
					
						
							|  |  |  | 		return true | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	globalCustomMarshalerMu.Lock() | 
					
						
							|  |  |  | 	defer globalCustomMarshalerMu.Unlock() | 
					
						
							|  |  |  | 	if _, exists := globalCustomMarshalerMap[t]; exists { | 
					
						
							|  |  |  | 		return true | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return false | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (e *Encoder) marshalerFromCustomMarshalerMap(t reflect.Type) (func(interface{}) ([]byte, error), bool) { | 
					
						
							|  |  |  | 	if marshaler, exists := e.customMarshalerMap[t]; exists { | 
					
						
							|  |  |  | 		return marshaler, exists | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	globalCustomMarshalerMu.Lock() | 
					
						
							|  |  |  | 	defer globalCustomMarshalerMu.Unlock() | 
					
						
							|  |  |  | 	if marshaler, exists := globalCustomMarshalerMap[t]; exists { | 
					
						
							|  |  |  | 		return marshaler, exists | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return nil, false | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-04-02 10:50:03 +09:00
										 |  |  | 	if e.existsTypeInCustomMarshalerMap(v.Type()) { | 
					
						
							|  |  |  | 		return true | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											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 | 
					
						
							| 
									
										
										
										
											2025-05-05 16:27:26 +02:00
										 |  |  | 	case time.Time, *time.Time: | 
					
						
							| 
									
										
										
										
											2020-11-11 19:27:58 +09:00
										 |  |  | 		return true | 
					
						
							| 
									
										
										
										
											2021-08-11 15:02:20 -04:00
										 |  |  | 	case time.Duration: | 
					
						
							|  |  |  | 		return true | 
					
						
							| 
									
										
										
										
											2020-11-11 19:27:58 +09:00
										 |  |  | 	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() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-02 10:50:03 +09:00
										 |  |  | 	if marshaler, exists := e.marshalerFromCustomMarshalerMap(v.Type()); exists { | 
					
						
							|  |  |  | 		doc, err := marshaler(iface) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2024-10-28 21:24:15 +09:00
										 |  |  | 			return nil, err | 
					
						
							| 
									
										
										
										
											2023-04-02 10:50:03 +09:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		node, err := e.encodeDocument(doc) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2024-10-28 21:24:15 +09:00
										 |  |  | 			return nil, err | 
					
						
							| 
									
										
										
										
											2023-04-02 10:50:03 +09:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		return node, nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-11 19:27:58 +09:00
										 |  |  | 	if marshaler, ok := iface.(BytesMarshalerContext); ok { | 
					
						
							|  |  |  | 		doc, err := marshaler.MarshalYAML(ctx) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2024-10-28 21:24:15 +09:00
										 |  |  | 			return nil, err | 
					
						
							| 
									
										
										
										
											2020-11-11 19:27:58 +09:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		node, err := e.encodeDocument(doc) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2024-10-28 21:24:15 +09:00
										 |  |  | 			return nil, err | 
					
						
							| 
									
										
										
										
											2020-11-11 19:27:58 +09:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		return node, nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if marshaler, ok := iface.(BytesMarshaler); ok { | 
					
						
							|  |  |  | 		doc, err := marshaler.MarshalYAML() | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2024-10-28 21:24:15 +09:00
										 |  |  | 			return nil, err | 
					
						
							| 
									
										
										
										
											2020-11-11 19:27:58 +09:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		node, err := e.encodeDocument(doc) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2024-10-28 21:24:15 +09:00
										 |  |  | 			return nil, err | 
					
						
							| 
									
										
										
										
											2020-11-11 19:27:58 +09:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		return node, nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if marshaler, ok := iface.(InterfaceMarshalerContext); ok { | 
					
						
							|  |  |  | 		marshalV, err := marshaler.MarshalYAML(ctx) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2024-10-28 21:24:15 +09:00
										 |  |  | 			return nil, err | 
					
						
							| 
									
										
										
										
											2020-11-11 19:27:58 +09:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		return e.encodeValue(ctx, reflect.ValueOf(marshalV), column) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if marshaler, ok := iface.(InterfaceMarshaler); ok { | 
					
						
							|  |  |  | 		marshalV, err := marshaler.MarshalYAML() | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2024-10-28 21:24:15 +09:00
										 |  |  | 			return nil, err | 
					
						
							| 
									
										
										
										
											2020-11-11 19:27:58 +09:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		return e.encodeValue(ctx, reflect.ValueOf(marshalV), column) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if t, ok := iface.(time.Time); ok { | 
					
						
							|  |  |  | 		return e.encodeTime(t, column), nil | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2025-05-05 16:27:26 +02:00
										 |  |  | 	// Handle *time.Time explicitly since it implements TextMarshaler and shouldn't be treated as plain text | 
					
						
							|  |  |  | 	if t, ok := iface.(*time.Time); ok && t != nil { | 
					
						
							|  |  |  | 		return e.encodeTime(*t, column), nil | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-11-11 19:27:58 +09:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-11 15:02:20 -04:00
										 |  |  | 	if t, ok := iface.(time.Duration); ok { | 
					
						
							|  |  |  | 		return e.encodeDuration(t, column), nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-11 19:27:58 +09:00
										 |  |  | 	if marshaler, ok := iface.(encoding.TextMarshaler); ok { | 
					
						
							| 
									
										
										
										
											2025-05-05 16:27:26 +02:00
										 |  |  | 		text, err := marshaler.MarshalText() | 
					
						
							| 
									
										
										
										
											2020-11-11 19:27:58 +09:00
										 |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2024-10-28 21:24:15 +09:00
										 |  |  | 			return nil, err | 
					
						
							| 
									
										
										
										
											2020-11-11 19:27:58 +09:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2025-05-05 16:27:26 +02:00
										 |  |  | 		node := e.encodeString(string(text), column) | 
					
						
							| 
									
										
										
										
											2020-11-11 19:27:58 +09:00
										 |  |  | 		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 { | 
					
						
							| 
									
										
										
										
											2024-10-28 21:24:15 +09:00
										 |  |  | 				return nil, err | 
					
						
							| 
									
										
										
										
											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 { | 
					
						
							| 
									
										
										
										
											2024-10-28 21:24:15 +09:00
										 |  |  | 				return nil, err | 
					
						
							| 
									
										
										
										
											2020-01-12 02:50:51 +09:00
										 |  |  | 			} | 
					
						
							|  |  |  | 			node, err := e.encodeDocument(doc) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							| 
									
										
										
										
											2024-10-28 21:24:15 +09:00
										 |  |  | 				return nil, err | 
					
						
							| 
									
										
										
										
											2020-01-12 02:50:51 +09:00
										 |  |  | 			} | 
					
						
							|  |  |  | 			return node, nil | 
					
						
							| 
									
										
										
										
											2019-10-24 17:16:29 +09:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-11-11 19:27:58 +09:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-28 21:24:15 +09:00
										 |  |  | 	return nil, errors.New("does not implemented Marshaler") | 
					
						
							| 
									
										
										
										
											2020-11-11 19:27:58 +09:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 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 { | 
					
						
							| 
									
										
										
										
											2024-10-28 21:24:15 +09:00
										 |  |  | 			return nil, err | 
					
						
							| 
									
										
										
										
											2020-11-11 19:27:58 +09:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		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 | 
					
						
							| 
									
										
										
										
											2021-03-01 16:17:23 +09:00
										 |  |  | 	case reflect.Float32: | 
					
						
							|  |  |  | 		return e.encodeFloat(v.Float(), 32), nil | 
					
						
							|  |  |  | 	case reflect.Float64: | 
					
						
							|  |  |  | 		return e.encodeFloat(v.Float(), 64), nil | 
					
						
							| 
									
										
										
										
											2020-03-02 22:58:50 +09:00
										 |  |  | 	case reflect.Ptr: | 
					
						
							| 
									
										
										
										
											2025-02-16 20:52:25 +09:00
										 |  |  | 		if value := e.encodePtrAnchor(v, column); value != nil { | 
					
						
							|  |  |  | 			return value, 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
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2025-02-16 20:52:25 +09:00
										 |  |  | 		if value := e.encodePtrAnchor(v, column); value != nil { | 
					
						
							|  |  |  | 			return value, nil | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-11-11 19:27:58 +09:00
										 |  |  | 		return e.encodeSlice(ctx, v) | 
					
						
							| 
									
										
										
										
											2020-11-12 19:17:56 +09:00
										 |  |  | 	case reflect.Array: | 
					
						
							|  |  |  | 		return e.encodeArray(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: | 
					
						
							| 
									
										
										
										
											2025-02-16 20:52:25 +09:00
										 |  |  | 		if value := e.encodePtrAnchor(v, column); value != nil { | 
					
						
							|  |  |  | 			return value, nil | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2025-03-06 17:45:33 -08:00
										 |  |  | 		return e.encodeMap(ctx, v, column) | 
					
						
							| 
									
										
										
										
											2019-10-19 18:28:36 +09:00
										 |  |  | 	default: | 
					
						
							| 
									
										
										
										
											2024-10-28 21:24:15 +09:00
										 |  |  | 		return nil, fmt.Errorf("unknown value type %s", v.Type().String()) | 
					
						
							| 
									
										
										
										
											2019-10-19 18:28:36 +09:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-02-16 20:52:25 +09:00
										 |  |  | func (e *Encoder) encodePtrAnchor(v reflect.Value, column int) ast.Node { | 
					
						
							|  |  |  | 	anchorName, exists := e.getAnchor(v.Pointer()) | 
					
						
							|  |  |  | 	if !exists { | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	aliasName := anchorName | 
					
						
							|  |  |  | 	alias := ast.Alias(token.New("*", "*", e.pos(column))) | 
					
						
							|  |  |  | 	alias.Value = ast.String(token.New(aliasName, aliasName, e.pos(column))) | 
					
						
							|  |  |  | 	e.setSmartAlias(aliasName, v.Pointer()) | 
					
						
							|  |  |  | 	return alias | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-19 18:28:36 +09:00
										 |  |  | 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, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-18 13:26:00 +09:00
										 |  |  | func (e *Encoder) encodeNil() *ast.NullNode { | 
					
						
							| 
									
										
										
										
											2019-10-19 18:28:36 +09:00
										 |  |  | 	value := "null" | 
					
						
							|  |  |  | 	return ast.Null(token.New(value, value, e.pos(e.column))) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-18 13:26:00 +09:00
										 |  |  | func (e *Encoder) encodeInt(v int64) *ast.IntegerNode { | 
					
						
							| 
									
										
										
										
											2024-12-12 17:31:35 +02:00
										 |  |  | 	value := strconv.FormatInt(v, 10) | 
					
						
							| 
									
										
										
										
											2019-10-19 18:28:36 +09:00
										 |  |  | 	return ast.Integer(token.New(value, value, e.pos(e.column))) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-18 13:26:00 +09:00
										 |  |  | func (e *Encoder) encodeUint(v uint64) *ast.IntegerNode { | 
					
						
							| 
									
										
										
										
											2024-12-12 17:31:35 +02:00
										 |  |  | 	value := strconv.FormatUint(v, 10) | 
					
						
							| 
									
										
										
										
											2019-10-19 18:28:36 +09:00
										 |  |  | 	return ast.Integer(token.New(value, value, e.pos(e.column))) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-01 16:17:23 +09:00
										 |  |  | func (e *Encoder) encodeFloat(v float64, bitSize int) ast.Node { | 
					
						
							| 
									
										
										
										
											2019-10-19 18:28:36 +09:00
										 |  |  | 	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))) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-03-01 16:17:23 +09:00
										 |  |  | 	value := strconv.FormatFloat(v, 'g', -1, bitSize) | 
					
						
							| 
									
										
										
										
											2021-08-16 16:30:13 -04:00
										 |  |  | 	if !strings.Contains(value, ".") && !strings.Contains(value, "e") { | 
					
						
							| 
									
										
										
										
											2025-02-25 14:44:58 +01:00
										 |  |  | 		if e.autoInt { | 
					
						
							|  |  |  | 			return ast.Integer(token.New(value, value, e.pos(e.column))) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2021-03-01 16:17:23 +09:00
										 |  |  | 		// append x.0 suffix to keep float value context | 
					
						
							|  |  |  | 		value = fmt.Sprintf("%s.0", value) | 
					
						
							| 
									
										
										
										
											2019-10-19 18:28:36 +09:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	return ast.Float(token.New(value, value, e.pos(e.column))) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-19 10:15:56 +02:00
										 |  |  | func (e *Encoder) isNeedQuoted(v string) bool { | 
					
						
							| 
									
										
										
										
											2020-11-18 07:35:17 +02:00
										 |  |  | 	if e.isJSONStyle { | 
					
						
							| 
									
										
										
										
											2020-11-19 10:15:56 +02:00
										 |  |  | 		return true | 
					
						
							| 
									
										
										
										
											2020-11-18 07:35:17 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-11-19 10:15:56 +02:00
										 |  |  | 	if e.useLiteralStyleIfMultiline && strings.ContainsAny(v, "\n\r") { | 
					
						
							|  |  |  | 		return false | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-11-22 23:50:18 -05:00
										 |  |  | 	if e.isFlowStyle && strings.ContainsAny(v, `]},'"`) { | 
					
						
							|  |  |  | 		return true | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2024-12-22 20:28:28 +09:00
										 |  |  | 	if e.isFlowStyle { | 
					
						
							|  |  |  | 		for i := 0; i < len(v); i++ { | 
					
						
							|  |  |  | 			if v[i] != ':' { | 
					
						
							|  |  |  | 				continue | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			if i+1 < len(v) && v[i+1] == '/' { | 
					
						
							|  |  |  | 				continue | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			return true | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-11-19 10:15:56 +02:00
										 |  |  | 	if token.IsNeedQuoted(v) { | 
					
						
							|  |  |  | 		return true | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return false | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2020-11-18 07:35:17 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-18 13:26:00 +09:00
										 |  |  | func (e *Encoder) encodeString(v string, column int) *ast.StringNode { | 
					
						
							| 
									
										
										
										
											2020-11-19 10:15:56 +02:00
										 |  |  | 	if e.isNeedQuoted(v) { | 
					
						
							| 
									
										
										
										
											2021-08-09 14:54:53 +00:00
										 |  |  | 		if e.singleQuote { | 
					
						
							|  |  |  | 			v = quoteWith(v, '\'') | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			v = strconv.Quote(v) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-10-19 18:28:36 +09:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	return ast.String(token.New(v, v, e.pos(column))) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-18 13:26:00 +09:00
										 |  |  | func (e *Encoder) encodeBool(v bool) *ast.BoolNode { | 
					
						
							| 
									
										
										
										
											2024-12-12 17:31:35 +02:00
										 |  |  | 	value := strconv.FormatBool(v) | 
					
						
							| 
									
										
										
										
											2019-10-19 18:28:36 +09:00
										 |  |  | 	return ast.Bool(token.New(value, value, e.pos(e.column))) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-18 13:26:00 +09:00
										 |  |  | func (e *Encoder) encodeSlice(ctx context.Context, value reflect.Value) (*ast.SequenceNode, error) { | 
					
						
							| 
									
										
										
										
											2021-06-29 16:35:54 +02:00
										 |  |  | 	if e.indentSequence { | 
					
						
							| 
									
										
										
										
											2025-01-31 02:27:06 +01:00
										 |  |  | 		e.column += e.indentNum | 
					
						
							| 
									
										
										
										
											2025-05-05 16:45:47 +02:00
										 |  |  | 		defer func() { e.column -= e.indentNum }() | 
					
						
							| 
									
										
										
										
											2021-06-29 16:35:54 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-08-09 14:05:40 +00:00
										 |  |  | 	column := e.column | 
					
						
							| 
									
										
										
										
											2021-06-29 16:35:54 +02:00
										 |  |  | 	sequence := ast.Sequence(token.New("-", "-", e.pos(column)), e.isFlowStyle) | 
					
						
							| 
									
										
										
										
											2019-10-19 18:28:36 +09:00
										 |  |  | 	for i := 0; i < value.Len(); i++ { | 
					
						
							| 
									
										
										
										
											2021-06-29 16:35:54 +02:00
										 |  |  | 		node, err := e.encodeValue(ctx, value.Index(i), column) | 
					
						
							| 
									
										
										
										
											2019-10-19 18:28:36 +09:00
										 |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2024-10-28 21:24:15 +09:00
										 |  |  | 			return nil, err | 
					
						
							| 
									
										
										
										
											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
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-18 13:26:00 +09:00
										 |  |  | func (e *Encoder) encodeArray(ctx context.Context, value reflect.Value) (*ast.SequenceNode, error) { | 
					
						
							| 
									
										
										
										
											2021-06-29 16:35:54 +02:00
										 |  |  | 	if e.indentSequence { | 
					
						
							| 
									
										
										
										
											2025-01-31 02:27:06 +01:00
										 |  |  | 		e.column += e.indentNum | 
					
						
							| 
									
										
										
										
											2025-05-05 16:45:47 +02:00
										 |  |  | 		defer func() { e.column -= e.indentNum }() | 
					
						
							| 
									
										
										
										
											2021-06-29 16:35:54 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-08-09 14:05:40 +00:00
										 |  |  | 	column := e.column | 
					
						
							| 
									
										
										
										
											2021-06-29 16:35:54 +02:00
										 |  |  | 	sequence := ast.Sequence(token.New("-", "-", e.pos(column)), e.isFlowStyle) | 
					
						
							| 
									
										
										
										
											2020-11-12 19:17:56 +09:00
										 |  |  | 	for i := 0; i < value.Len(); i++ { | 
					
						
							| 
									
										
										
										
											2021-06-29 16:35:54 +02:00
										 |  |  | 		node, err := e.encodeValue(ctx, value.Index(i), column) | 
					
						
							| 
									
										
										
										
											2020-11-12 19:17:56 +09:00
										 |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2024-10-28 21:24:15 +09:00
										 |  |  | 			return nil, err | 
					
						
							| 
									
										
										
										
											2020-11-12 19:17:56 +09:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		sequence.Values = append(sequence.Values, node) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return sequence, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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 { | 
					
						
							| 
									
										
										
										
											2024-10-28 21:24:15 +09:00
										 |  |  | 		return nil, err | 
					
						
							| 
									
										
										
										
											2019-10-24 17:16:29 +09:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2022-12-19 14:26:21 +09:00
										 |  |  | 	if e.isMapNode(value) { | 
					
						
							| 
									
										
										
										
											2025-01-31 02:27:06 +01:00
										 |  |  | 		value.AddColumn(e.indentNum) | 
					
						
							| 
									
										
										
										
											2019-10-24 17:16:29 +09:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2025-02-04 06:25:25 +01:00
										 |  |  | 	if e.isTagAndMapNode(value) { | 
					
						
							|  |  |  | 		value.AddColumn(e.indentNum) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											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
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-18 13:26:00 +09:00
										 |  |  | func (e *Encoder) encodeMapSlice(ctx context.Context, value MapSlice, column int) (*ast.MappingNode, 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 { | 
					
						
							| 
									
										
										
										
											2025-05-05 16:45:47 +02:00
										 |  |  | 		encoded, err := e.encodeMapItem(ctx, item, column) | 
					
						
							| 
									
										
										
										
											2019-10-24 17:16:29 +09:00
										 |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2024-10-28 21:24:15 +09:00
										 |  |  | 			return nil, err | 
					
						
							| 
									
										
										
										
											2019-10-24 17:16:29 +09:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2025-05-05 16:45:47 +02:00
										 |  |  | 		node.Values = append(node.Values, encoded) | 
					
						
							| 
									
										
										
										
											2019-10-24 17:16:29 +09:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	return node, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-19 14:26:21 +09:00
										 |  |  | func (e *Encoder) isMapNode(node ast.Node) bool { | 
					
						
							|  |  |  | 	_, ok := node.(ast.MapNode) | 
					
						
							|  |  |  | 	return ok | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-02-04 06:25:25 +01:00
										 |  |  | func (e *Encoder) isTagAndMapNode(node ast.Node) bool { | 
					
						
							|  |  |  | 	tn, ok := node.(*ast.TagNode) | 
					
						
							|  |  |  | 	return ok && e.isMapNode(tn.Value) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-06 17:45:33 -08:00
										 |  |  | func (e *Encoder) encodeMap(ctx context.Context, value reflect.Value, column int) (ast.Node, error) { | 
					
						
							| 
									
										
										
										
											2020-03-09 21:59:00 +09:00
										 |  |  | 	node := ast.Mapping(token.New("", "", e.pos(column)), e.isFlowStyle) | 
					
						
							| 
									
										
										
										
											2021-06-26 20:38:11 +09:00
										 |  |  | 	keys := make([]interface{}, len(value.MapKeys())) | 
					
						
							|  |  |  | 	for i, k := range value.MapKeys() { | 
					
						
							|  |  |  | 		keys[i] = k.Interface() | 
					
						
							| 
									
										
										
										
											2019-10-19 18:28:36 +09:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-06-26 20:38:11 +09:00
										 |  |  | 	sort.Slice(keys, func(i, j int) bool { | 
					
						
							| 
									
										
										
										
											2021-06-30 14:32:52 +09:00
										 |  |  | 		return fmt.Sprint(keys[i]) < fmt.Sprint(keys[j]) | 
					
						
							| 
									
										
										
										
											2021-06-26 20:38:11 +09:00
										 |  |  | 	}) | 
					
						
							| 
									
										
										
										
											2019-10-19 18:28:36 +09:00
										 |  |  | 	for _, key := range keys { | 
					
						
							|  |  |  | 		k := reflect.ValueOf(key) | 
					
						
							|  |  |  | 		v := value.MapIndex(k) | 
					
						
							| 
									
										
										
										
											2025-05-05 16:45:47 +02:00
										 |  |  | 		encoded, err := e.encodeValue(ctx, v, column) | 
					
						
							| 
									
										
										
										
											2019-10-19 18:28:36 +09:00
										 |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2025-03-06 17:45:33 -08:00
										 |  |  | 			return nil, err | 
					
						
							| 
									
										
										
										
											2019-10-19 18:28:36 +09:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2025-05-05 16:45:47 +02:00
										 |  |  | 		if e.isMapNode(encoded) { | 
					
						
							|  |  |  | 			encoded.AddColumn(e.indentNum) | 
					
						
							| 
									
										
										
										
											2019-10-19 18:28:36 +09:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2025-05-05 16:45:47 +02:00
										 |  |  | 		if e.isTagAndMapNode(encoded) { | 
					
						
							|  |  |  | 			encoded.AddColumn(e.indentNum) | 
					
						
							| 
									
										
										
										
											2025-02-04 06:25:25 +01:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2025-02-16 20:52:25 +09:00
										 |  |  | 		keyText := fmt.Sprint(key) | 
					
						
							|  |  |  | 		vRef := e.toPointer(v) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// during the second encoding, an anchor is assigned if it is found to be used by an alias. | 
					
						
							|  |  |  | 		if aliasName, exists := e.getSmartAlias(vRef); exists { | 
					
						
							|  |  |  | 			anchorName := aliasName | 
					
						
							|  |  |  | 			anchorNode := ast.Anchor(token.New("&", "&", e.pos(column))) | 
					
						
							|  |  |  | 			anchorNode.Name = ast.String(token.New(anchorName, anchorName, e.pos(column))) | 
					
						
							| 
									
										
										
										
											2025-05-05 16:45:47 +02:00
										 |  |  | 			anchorNode.Value = encoded | 
					
						
							|  |  |  | 			encoded = anchorNode | 
					
						
							| 
									
										
										
										
											2025-02-16 20:52:25 +09:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-06-21 16:22:04 +09:00
										 |  |  | 		node.Values = append(node.Values, ast.MappingValue( | 
					
						
							|  |  |  | 			nil, | 
					
						
							| 
									
										
										
										
											2025-02-16 20:52:25 +09:00
										 |  |  | 			e.encodeString(keyText, column), | 
					
						
							| 
									
										
										
										
											2025-05-05 16:45:47 +02:00
										 |  |  | 			encoded, | 
					
						
							| 
									
										
										
										
											2020-06-21 16:22:04 +09:00
										 |  |  | 		)) | 
					
						
							| 
									
										
										
										
											2025-02-16 20:52:25 +09:00
										 |  |  | 		e.setSmartAnchor(vRef, keyText) | 
					
						
							| 
									
										
										
										
											2019-10-19 18:28:36 +09:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2025-03-06 17:45:33 -08:00
										 |  |  | 	return node, nil | 
					
						
							| 
									
										
										
										
											2019-10-19 18:28:36 +09:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-18 13:26:00 +09:00
										 |  |  | func (e *Encoder) encodeTime(v time.Time, column int) *ast.StringNode { | 
					
						
							| 
									
										
										
										
											2019-11-07 21:14:36 +09:00
										 |  |  | 	value := v.Format(time.RFC3339Nano) | 
					
						
							| 
									
										
										
										
											2020-06-02 19:56:02 +09:00
										 |  |  | 	if e.isJSONStyle { | 
					
						
							|  |  |  | 		value = strconv.Quote(value) | 
					
						
							| 
									
										
										
										
											2021-08-11 15:02:20 -04:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	return ast.String(token.New(value, value, e.pos(column))) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-18 13:26:00 +09:00
										 |  |  | func (e *Encoder) encodeDuration(v time.Duration, column int) *ast.StringNode { | 
					
						
							| 
									
										
										
										
											2021-08-11 15:02:20 -04:00
										 |  |  | 	value := v.String() | 
					
						
							|  |  |  | 	if e.isJSONStyle { | 
					
						
							|  |  |  | 		value = strconv.Quote(value) | 
					
						
							| 
									
										
										
										
											2020-06-02 19:56:02 +09:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-11-07 21:14:36 +09:00
										 |  |  | 	return ast.String(token.New(value, value, e.pos(column))) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-18 13:26:00 +09:00
										 |  |  | func (e *Encoder) encodeAnchor(anchorName string, value ast.Node, fieldValue reflect.Value, column int) (*ast.AnchorNode, 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 { | 
					
						
							| 
									
										
										
										
											2024-10-28 21:24:15 +09:00
										 |  |  | 			return nil, err | 
					
						
							| 
									
										
										
										
											2020-01-09 13:33:10 +09:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		if snode, ok := anchorNode.Name.(*ast.StringNode); ok { | 
					
						
							|  |  |  | 			anchorName = snode.Value | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if fieldValue.Kind() == reflect.Ptr { | 
					
						
							| 
									
										
										
										
											2025-02-16 20:52:25 +09:00
										 |  |  | 		e.setAnchor(fieldValue.Pointer(), anchorName) | 
					
						
							| 
									
										
										
										
											2020-01-09 13:33:10 +09:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	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() | 
					
						
							| 
									
										
										
										
											2025-05-05 16:45:47 +02:00
										 |  |  | 	fieldMap, err := structFieldMap(structType) | 
					
						
							| 
									
										
										
										
											2019-10-19 18:28:36 +09:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2024-10-28 21:24:15 +09:00
										 |  |  | 		return nil, err | 
					
						
							| 
									
										
										
										
											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) | 
					
						
							| 
									
										
										
										
											2025-05-05 16:45:47 +02:00
										 |  |  | 		sf := fieldMap[field.Name] | 
					
						
							|  |  |  | 		if (e.omitEmpty || sf.IsOmitEmpty) && e.isZeroValue(fieldValue) { | 
					
						
							| 
									
										
										
										
											2019-10-20 20:50:15 +09:00
										 |  |  | 			// omit encoding | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2021-11-22 23:50:18 -05:00
										 |  |  | 		ve := e | 
					
						
							| 
									
										
										
										
											2025-05-05 16:45:47 +02:00
										 |  |  | 		if !e.isFlowStyle && sf.IsFlow { | 
					
						
							| 
									
										
										
										
											2021-11-22 23:50:18 -05:00
										 |  |  | 			ve = &Encoder{} | 
					
						
							|  |  |  | 			*ve = *e | 
					
						
							|  |  |  | 			ve.isFlowStyle = true | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2025-05-05 16:45:47 +02:00
										 |  |  | 		encoded, err := ve.encodeValue(ctx, fieldValue, column) | 
					
						
							| 
									
										
										
										
											2019-10-19 18:28:36 +09:00
										 |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2024-10-28 21:24:15 +09:00
										 |  |  | 			return nil, err | 
					
						
							| 
									
										
										
										
											2019-10-19 18:28:36 +09:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2025-05-05 16:45:47 +02:00
										 |  |  | 		if e.isMapNode(encoded) { | 
					
						
							|  |  |  | 			encoded.AddColumn(e.indentNum) | 
					
						
							| 
									
										
										
										
											2019-10-19 18:28:36 +09:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2025-05-05 16:45:47 +02:00
										 |  |  | 		var key ast.MapKeyNode = e.encodeString(sf.RenderName, column) | 
					
						
							| 
									
										
										
										
											2019-10-28 21:37:12 +09:00
										 |  |  | 		switch { | 
					
						
							| 
									
										
										
										
											2025-05-05 16:45:47 +02:00
										 |  |  | 		case encoded.Type() == ast.AliasType: | 
					
						
							|  |  |  | 			if aliasName := sf.AliasName; aliasName != "" { | 
					
						
							|  |  |  | 				alias, ok := encoded.(*ast.AliasNode) | 
					
						
							| 
									
										
										
										
											2024-12-23 10:25:20 +09:00
										 |  |  | 				if !ok { | 
					
						
							| 
									
										
										
										
											2025-05-05 16:45:47 +02:00
										 |  |  | 					return nil, errors.ErrUnexpectedNodeType(encoded.Type(), ast.AliasType, encoded.GetToken()) | 
					
						
							| 
									
										
										
										
											2024-12-23 10:25:20 +09:00
										 |  |  | 				} | 
					
						
							|  |  |  | 				got := alias.Value.String() | 
					
						
							|  |  |  | 				if aliasName != got { | 
					
						
							|  |  |  | 					return nil, fmt.Errorf("expected alias name is %q but got %q", aliasName, got) | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2019-10-20 13:05:03 +09:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2025-05-05 16:45:47 +02:00
										 |  |  | 			if sf.IsInline { | 
					
						
							| 
									
										
										
										
											2019-10-20 21:47:06 +09:00
										 |  |  | 				// if both used alias and inline, output `<<: *alias` | 
					
						
							|  |  |  | 				key = ast.MergeKey(token.New("<<", "<<", e.pos(column))) | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2025-05-05 16:45:47 +02:00
										 |  |  | 		case sf.AnchorName != "": | 
					
						
							|  |  |  | 			anchorNode, err := e.encodeAnchor(sf.AnchorName, encoded, fieldValue, column) | 
					
						
							| 
									
										
										
										
											2024-12-23 10:25:20 +09:00
										 |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				return nil, err | 
					
						
							| 
									
										
										
										
											2019-10-20 21:47:06 +09:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2025-05-05 16:45:47 +02:00
										 |  |  | 			encoded = anchorNode | 
					
						
							|  |  |  | 		case sf.IsInline: | 
					
						
							|  |  |  | 			isAutoAnchor := sf.IsAutoAnchor | 
					
						
							| 
									
										
										
										
											2020-03-02 22:58:50 +09:00
										 |  |  | 			if !hasInlineAnchorField { | 
					
						
							|  |  |  | 				hasInlineAnchorField = isAutoAnchor | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			if isAutoAnchor { | 
					
						
							|  |  |  | 				inlineAnchorValue = fieldValue | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2025-05-05 16:45:47 +02:00
										 |  |  | 			mapNode, ok := encoded.(ast.MapNode) | 
					
						
							| 
									
										
										
										
											2019-10-28 21:37:12 +09:00
										 |  |  | 			if !ok { | 
					
						
							| 
									
										
										
										
											2023-09-13 17:17:47 +09:00
										 |  |  | 				// if an inline field is null, skip encoding it | 
					
						
							| 
									
										
										
										
											2025-05-05 16:45:47 +02:00
										 |  |  | 				if _, ok := encoded.(*ast.NullNode); ok { | 
					
						
							| 
									
										
										
										
											2023-09-13 17:17:47 +09:00
										 |  |  | 					continue | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2024-10-28 21:24:15 +09:00
										 |  |  | 				return nil, errors.New("inline value is must be map or struct type") | 
					
						
							| 
									
										
										
										
											2019-10-28 21:37:12 +09:00
										 |  |  | 			} | 
					
						
							|  |  |  | 			mapIter := mapNode.MapRange() | 
					
						
							|  |  |  | 			for mapIter.Next() { | 
					
						
							| 
									
										
										
										
											2025-05-05 16:45:47 +02:00
										 |  |  | 				mapKey := mapIter.Key() | 
					
						
							|  |  |  | 				mapValue := mapIter.Value() | 
					
						
							|  |  |  | 				keyName := mapKey.GetToken().Value | 
					
						
							|  |  |  | 				if fieldMap.isIncludedRenderName(keyName) { | 
					
						
							|  |  |  | 					// if declared the same key name, skip encoding this field | 
					
						
							| 
									
										
										
										
											2019-10-28 22:25:00 +09:00
										 |  |  | 					continue | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2025-05-05 16:45:47 +02:00
										 |  |  | 				mapKey.AddColumn(-e.indentNum) | 
					
						
							|  |  |  | 				mapValue.AddColumn(-e.indentNum) | 
					
						
							|  |  |  | 				node.Values = append(node.Values, ast.MappingValue(nil, mapKey, mapValue)) | 
					
						
							| 
									
										
										
										
											2019-10-28 21:37:12 +09:00
										 |  |  | 			} | 
					
						
							|  |  |  | 			continue | 
					
						
							| 
									
										
										
										
											2025-05-05 16:45:47 +02:00
										 |  |  | 		case sf.IsAutoAnchor: | 
					
						
							|  |  |  | 			anchorNode, err := e.encodeAnchor(sf.RenderName, encoded, fieldValue, column) | 
					
						
							| 
									
										
										
										
											2020-03-02 22:58:50 +09:00
										 |  |  | 			if err != nil { | 
					
						
							| 
									
										
										
										
											2024-10-28 21:24:15 +09:00
										 |  |  | 				return nil, err | 
					
						
							| 
									
										
										
										
											2020-03-02 22:58:50 +09:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2025-05-05 16:45:47 +02:00
										 |  |  | 			encoded = anchorNode | 
					
						
							| 
									
										
										
										
											2019-10-19 22:01:36 +09:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2025-05-05 16:45:47 +02:00
										 |  |  | 		node.Values = append(node.Values, ast.MappingValue(nil, key, encoded)) | 
					
						
							| 
									
										
										
										
											2019-10-19 18:28:36 +09:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-03-02 22:58:50 +09:00
										 |  |  | 	if hasInlineAnchorField { | 
					
						
							| 
									
										
										
										
											2025-01-31 02:27:06 +01:00
										 |  |  | 		node.AddColumn(e.indentNum) | 
					
						
							| 
									
										
										
										
											2020-03-02 22:58:50 +09:00
										 |  |  | 		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 { | 
					
						
							| 
									
										
										
										
											2024-10-28 21:24:15 +09:00
										 |  |  | 				return nil, err | 
					
						
							| 
									
										
										
										
											2020-03-02 22:58:50 +09:00
										 |  |  | 			} | 
					
						
							|  |  |  | 			if snode, ok := anchorNode.Name.(*ast.StringNode); ok { | 
					
						
							|  |  |  | 				anchorName = snode.Value | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if inlineAnchorValue.Kind() == reflect.Ptr { | 
					
						
							| 
									
										
										
										
											2025-02-16 20:52:25 +09:00
										 |  |  | 			e.setAnchor(inlineAnchorValue.Pointer(), anchorName) | 
					
						
							| 
									
										
										
										
											2020-03-02 22:58:50 +09:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		return anchorNode, nil | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-10-20 19:05:56 +09:00
										 |  |  | 	return node, nil | 
					
						
							| 
									
										
										
										
											2019-10-19 18:28:36 +09:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2025-02-16 20:52:25 +09:00
										 |  |  | 
 | 
					
						
							|  |  |  | func (e *Encoder) toPointer(v reflect.Value) uintptr { | 
					
						
							|  |  |  | 	if e.isInvalidValue(v) { | 
					
						
							|  |  |  | 		return 0 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	switch v.Type().Kind() { | 
					
						
							|  |  |  | 	case reflect.Ptr: | 
					
						
							|  |  |  | 		return v.Pointer() | 
					
						
							|  |  |  | 	case reflect.Interface: | 
					
						
							|  |  |  | 		return e.toPointer(v.Elem()) | 
					
						
							|  |  |  | 	case reflect.Slice: | 
					
						
							|  |  |  | 		return v.Pointer() | 
					
						
							|  |  |  | 	case reflect.Map: | 
					
						
							|  |  |  | 		return v.Pointer() | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return 0 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (e *Encoder) clearSmartAnchorRef() { | 
					
						
							|  |  |  | 	if !e.enableSmartAnchor { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	e.anchorRefToName = make(map[uintptr]string) | 
					
						
							|  |  |  | 	e.anchorNameMap = make(map[string]struct{}) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (e *Encoder) setSmartAnchor(ptr uintptr, name string) { | 
					
						
							|  |  |  | 	if !e.enableSmartAnchor { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	e.setAnchor(ptr, e.generateAnchorName(name)) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (e *Encoder) setAnchor(ptr uintptr, name string) { | 
					
						
							|  |  |  | 	if ptr == 0 { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if name == "" { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	e.anchorRefToName[ptr] = name | 
					
						
							|  |  |  | 	e.anchorNameMap[name] = struct{}{} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (e *Encoder) generateAnchorName(base string) string { | 
					
						
							|  |  |  | 	if _, exists := e.anchorNameMap[base]; !exists { | 
					
						
							|  |  |  | 		return base | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	for i := 1; i < 100; i++ { | 
					
						
							|  |  |  | 		name := base + strconv.Itoa(i) | 
					
						
							|  |  |  | 		if _, exists := e.anchorNameMap[name]; exists { | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return name | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return "" | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (e *Encoder) getAnchor(ref uintptr) (string, bool) { | 
					
						
							|  |  |  | 	anchorName, exists := e.anchorRefToName[ref] | 
					
						
							|  |  |  | 	return anchorName, exists | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (e *Encoder) setSmartAlias(name string, ref uintptr) { | 
					
						
							|  |  |  | 	if !e.enableSmartAnchor { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	e.aliasRefToName[ref] = name | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (e *Encoder) getSmartAlias(ref uintptr) (string, bool) { | 
					
						
							|  |  |  | 	if !e.enableSmartAnchor { | 
					
						
							|  |  |  | 		return "", false | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	aliasName, exists := e.aliasRefToName[ref] | 
					
						
							|  |  |  | 	return aliasName, exists | 
					
						
							|  |  |  | } |