github.com/bingoohuang/gg@v0.0.0-20240325092523-45da7dee9335/pkg/yaml/encode.go (about)

     1  package yaml
     2  
     3  import (
     4  	"context"
     5  	"encoding"
     6  	"fmt"
     7  	"io"
     8  	"math"
     9  	"reflect"
    10  	"sort"
    11  	"strconv"
    12  	"strings"
    13  	"time"
    14  
    15  	"github.com/bingoohuang/gg/pkg/yaml/ast"
    16  	"github.com/bingoohuang/gg/pkg/yaml/internal/errors"
    17  	"github.com/bingoohuang/gg/pkg/yaml/parser"
    18  	"github.com/bingoohuang/gg/pkg/yaml/printer"
    19  	"github.com/bingoohuang/gg/pkg/yaml/token"
    20  	"golang.org/x/xerrors"
    21  )
    22  
    23  const (
    24  	// DefaultIndentSpaces default number of space for indent
    25  	DefaultIndentSpaces = 2
    26  )
    27  
    28  type KeyNamingStrategy int
    29  
    30  const (
    31  	KeyNamingDefault KeyNamingStrategy = iota
    32  	KeyNamingRaw
    33  	KeyNamingLowerCamel
    34  )
    35  
    36  // Encoder writes YAML values to an output stream.
    37  type Encoder struct {
    38  	writer                     io.Writer
    39  	opts                       []EncodeOption
    40  	indent                     int
    41  	indentSequence             bool
    42  	isFlowStyle                bool
    43  	isJSONStyle                bool
    44  	useJSONMarshaler           bool
    45  	anchorCallback             func(*ast.AnchorNode, interface{}) error
    46  	anchorPtrToNameMap         map[uintptr]string
    47  	useLiteralStyleIfMultiline bool
    48  	commentMap                 map[*Path]*Comment
    49  
    50  	line        int
    51  	column      int
    52  	offset      int
    53  	indentNum   int
    54  	indentLevel int
    55  
    56  	KeyNaming KeyNamingStrategy
    57  }
    58  
    59  // NewEncoder returns a new encoder that writes to w.
    60  // The Encoder should be closed after use to flush all data to w.
    61  func NewEncoder(w io.Writer, opts ...EncodeOption) *Encoder {
    62  	return &Encoder{
    63  		writer:             w,
    64  		opts:               opts,
    65  		indent:             DefaultIndentSpaces,
    66  		anchorPtrToNameMap: map[uintptr]string{},
    67  		line:               1,
    68  		column:             1,
    69  		offset:             0,
    70  	}
    71  }
    72  
    73  // Close closes the encoder by writing any remaining data.
    74  // It does not write a stream terminating string "...".
    75  func (e *Encoder) Close() error {
    76  	return nil
    77  }
    78  
    79  // Encode writes the YAML encoding of v to the stream.
    80  // If multiple items are encoded to the stream,
    81  // the second and subsequent document will be preceded with a "---" document separator,
    82  // but the first will not.
    83  //
    84  // See the documentation for Marshal for details about the conversion of Go values to YAML.
    85  func (e *Encoder) Encode(v interface{}) error {
    86  	return e.EncodeContext(context.Background(), v)
    87  }
    88  
    89  // EncodeContext writes the YAML encoding of v to the stream with context.Context.
    90  func (e *Encoder) EncodeContext(ctx context.Context, v interface{}) error {
    91  	node, err := e.EncodeToNodeContext(ctx, v)
    92  	if err != nil {
    93  		return errors.Wrapf(err, "failed to encode to node")
    94  	}
    95  	if err := e.setCommentByCommentMap(node); err != nil {
    96  		return errors.Wrapf(err, "failed to set comment by comment map")
    97  	}
    98  	var p printer.Printer
    99  	e.writer.Write(p.PrintNode(node))
   100  	return nil
   101  }
   102  
   103  // EncodeToNode convert v to ast.Node.
   104  func (e *Encoder) EncodeToNode(v interface{}) (ast.Node, error) {
   105  	return e.EncodeToNodeContext(context.Background(), v)
   106  }
   107  
   108  // EncodeToNodeContext convert v to ast.Node with context.Context.
   109  func (e *Encoder) EncodeToNodeContext(ctx context.Context, v interface{}) (ast.Node, error) {
   110  	for _, opt := range e.opts {
   111  		if err := opt(e); err != nil {
   112  			return nil, errors.Wrapf(err, "failed to run option for encoder")
   113  		}
   114  	}
   115  	node, err := e.encodeValue(ctx, reflect.ValueOf(v), 1)
   116  	if err != nil {
   117  		return nil, errors.Wrapf(err, "failed to encode value")
   118  	}
   119  	return node, nil
   120  }
   121  
   122  func (e *Encoder) setCommentByCommentMap(node ast.Node) error {
   123  	if e.commentMap == nil {
   124  		return nil
   125  	}
   126  	for path, comment := range e.commentMap {
   127  		n, err := path.FilterNode(node)
   128  		if err != nil {
   129  			return errors.Wrapf(err, "failed to filter node")
   130  		}
   131  		comments := []*token.Token{}
   132  		for _, text := range comment.Texts {
   133  			comments = append(comments, token.New(text, text, nil))
   134  		}
   135  		commentGroup := ast.CommentGroup(comments)
   136  		switch comment.Position {
   137  		case CommentLinePosition:
   138  			if err := n.SetComment(commentGroup); err != nil {
   139  				return errors.Wrapf(err, "failed to set comment")
   140  			}
   141  		case CommentHeadPosition:
   142  			parent := ast.Parent(node, n)
   143  			if parent == nil {
   144  				return ErrUnsupportedHeadPositionType(node)
   145  			}
   146  			switch node := parent.(type) {
   147  			case *ast.MappingValueNode:
   148  				if err := node.SetComment(commentGroup); err != nil {
   149  					return errors.Wrapf(err, "failed to set comment")
   150  				}
   151  			case *ast.MappingNode:
   152  				if err := node.SetComment(commentGroup); err != nil {
   153  					return errors.Wrapf(err, "failed to set comment")
   154  				}
   155  			default:
   156  				return ErrUnsupportedHeadPositionType(node)
   157  			}
   158  		default:
   159  			return ErrUnknownCommentPositionType
   160  		}
   161  	}
   162  	return nil
   163  }
   164  
   165  func (e *Encoder) encodeDocument(doc []byte) (ast.Node, error) {
   166  	f, err := parser.ParseBytes(doc, 0)
   167  	if err != nil {
   168  		return nil, errors.Wrapf(err, "failed to parse yaml")
   169  	}
   170  	for _, docNode := range f.Docs {
   171  		if docNode.Body != nil {
   172  			return docNode.Body, nil
   173  		}
   174  	}
   175  	return nil, nil
   176  }
   177  
   178  func (e *Encoder) isInvalidValue(v reflect.Value) bool {
   179  	if !v.IsValid() {
   180  		return true
   181  	}
   182  	kind := v.Type().Kind()
   183  	if kind == reflect.Ptr && v.IsNil() {
   184  		return true
   185  	}
   186  	if kind == reflect.Interface && v.IsNil() {
   187  		return true
   188  	}
   189  	return false
   190  }
   191  
   192  type jsonMarshaler interface {
   193  	MarshalJSON() ([]byte, error)
   194  }
   195  
   196  func (e *Encoder) canEncodeByMarshaler(v reflect.Value) bool {
   197  	if !v.CanInterface() {
   198  		return false
   199  	}
   200  	iface := v.Interface()
   201  	switch iface.(type) {
   202  	case BytesMarshalerContext:
   203  		return true
   204  	case BytesMarshaler:
   205  		return true
   206  	case InterfaceMarshalerContext:
   207  		return true
   208  	case InterfaceMarshaler:
   209  		return true
   210  	case time.Time:
   211  		return true
   212  	case time.Duration:
   213  		return true
   214  	case encoding.TextMarshaler:
   215  		return true
   216  	case jsonMarshaler:
   217  		return e.useJSONMarshaler
   218  	}
   219  	return false
   220  }
   221  
   222  func (e *Encoder) encodeByMarshaler(ctx context.Context, v reflect.Value, column int) (ast.Node, error) {
   223  	iface := v.Interface()
   224  
   225  	if marshaler, ok := iface.(BytesMarshalerContext); ok {
   226  		doc, err := marshaler.MarshalYAML(ctx)
   227  		if err != nil {
   228  			return nil, errors.Wrapf(err, "failed to MarshalYAML")
   229  		}
   230  		node, err := e.encodeDocument(doc)
   231  		if err != nil {
   232  			return nil, errors.Wrapf(err, "failed to encode document")
   233  		}
   234  		return node, nil
   235  	}
   236  
   237  	if marshaler, ok := iface.(BytesMarshaler); ok {
   238  		doc, err := marshaler.MarshalYAML()
   239  		if err != nil {
   240  			return nil, errors.Wrapf(err, "failed to MarshalYAML")
   241  		}
   242  		node, err := e.encodeDocument(doc)
   243  		if err != nil {
   244  			return nil, errors.Wrapf(err, "failed to encode document")
   245  		}
   246  		return node, nil
   247  	}
   248  
   249  	if marshaler, ok := iface.(InterfaceMarshalerContext); ok {
   250  		marshalV, err := marshaler.MarshalYAML(ctx)
   251  		if err != nil {
   252  			return nil, errors.Wrapf(err, "failed to MarshalYAML")
   253  		}
   254  		return e.encodeValue(ctx, reflect.ValueOf(marshalV), column)
   255  	}
   256  
   257  	if marshaler, ok := iface.(InterfaceMarshaler); ok {
   258  		marshalV, err := marshaler.MarshalYAML()
   259  		if err != nil {
   260  			return nil, errors.Wrapf(err, "failed to MarshalYAML")
   261  		}
   262  		return e.encodeValue(ctx, reflect.ValueOf(marshalV), column)
   263  	}
   264  
   265  	if t, ok := iface.(time.Time); ok {
   266  		return e.encodeTime(t, column), nil
   267  	}
   268  	if t, ok := iface.(time.Duration); ok {
   269  		return e.encodeDuration(t, column), nil
   270  	}
   271  	if marshaler, ok := iface.(encoding.TextMarshaler); ok {
   272  		doc, err := marshaler.MarshalText()
   273  		if err != nil {
   274  			return nil, errors.Wrapf(err, "failed to MarshalText")
   275  		}
   276  		node, err := e.encodeDocument(doc)
   277  		if err != nil {
   278  			return nil, errors.Wrapf(err, "failed to encode document")
   279  		}
   280  		return node, nil
   281  	}
   282  
   283  	if e.useJSONMarshaler {
   284  		if marshaler, ok := iface.(jsonMarshaler); ok {
   285  			jsonBytes, err := marshaler.MarshalJSON()
   286  			if err != nil {
   287  				return nil, errors.Wrapf(err, "failed to MarshalJSON")
   288  			}
   289  			doc, err := JSONToYAML(jsonBytes)
   290  			if err != nil {
   291  				return nil, errors.Wrapf(err, "failed to convert json to yaml")
   292  			}
   293  			node, err := e.encodeDocument(doc)
   294  			if err != nil {
   295  				return nil, errors.Wrapf(err, "failed to encode document")
   296  			}
   297  			return node, nil
   298  		}
   299  	}
   300  
   301  	return nil, xerrors.Errorf("does not implemented Marshaler")
   302  }
   303  
   304  func (e *Encoder) encodeValue(ctx context.Context, v reflect.Value, column int) (ast.Node, error) {
   305  	if e.isInvalidValue(v) {
   306  		return e.encodeNil(), nil
   307  	}
   308  	if e.canEncodeByMarshaler(v) {
   309  		node, err := e.encodeByMarshaler(ctx, v, column)
   310  		if err != nil {
   311  			return nil, errors.Wrapf(err, "failed to encode by marshaler")
   312  		}
   313  		return node, nil
   314  	}
   315  	switch v.Type().Kind() {
   316  	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
   317  		return e.encodeInt(v.Int()), nil
   318  	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
   319  		return e.encodeUint(v.Uint()), nil
   320  	case reflect.Float32:
   321  		return e.encodeFloat(v.Float(), 32), nil
   322  	case reflect.Float64:
   323  		return e.encodeFloat(v.Float(), 64), nil
   324  	case reflect.Ptr:
   325  		anchorName := e.anchorPtrToNameMap[v.Pointer()]
   326  		if anchorName != "" {
   327  			aliasName := anchorName
   328  			alias := ast.Alias(token.New("*", "*", e.pos(column)))
   329  			alias.Value = ast.String(token.New(aliasName, aliasName, e.pos(column)))
   330  			return alias, nil
   331  		}
   332  		return e.encodeValue(ctx, v.Elem(), column)
   333  	case reflect.Interface:
   334  		return e.encodeValue(ctx, v.Elem(), column)
   335  	case reflect.String:
   336  		return e.encodeString(v.String(), column, nil), nil
   337  	case reflect.Bool:
   338  		return e.encodeBool(v.Bool()), nil
   339  	case reflect.Slice:
   340  		if mapSlice, ok := v.Interface().(MapSlice); ok {
   341  			return e.encodeMapSlice(ctx, mapSlice, column)
   342  		}
   343  		return e.encodeSlice(ctx, v)
   344  	case reflect.Array:
   345  		return e.encodeArray(ctx, v)
   346  	case reflect.Struct:
   347  		if v.CanInterface() {
   348  			if mapItem, ok := v.Interface().(MapItem); ok {
   349  				return e.encodeMapItem(ctx, mapItem, column)
   350  			}
   351  			if t, ok := v.Interface().(time.Time); ok {
   352  				return e.encodeTime(t, column), nil
   353  			}
   354  		}
   355  		return e.encodeStruct(ctx, v, column)
   356  	case reflect.Map:
   357  		return e.encodeMap(ctx, v, column), nil
   358  	default:
   359  		return nil, xerrors.Errorf("unknown value type %s", v.Type().String())
   360  	}
   361  	return nil, nil
   362  }
   363  
   364  func (e *Encoder) pos(column int) *token.Position {
   365  	return &token.Position{
   366  		Line:        e.line,
   367  		Column:      column,
   368  		Offset:      e.offset,
   369  		IndentNum:   e.indentNum,
   370  		IndentLevel: e.indentLevel,
   371  	}
   372  }
   373  
   374  func (e *Encoder) encodeNil() ast.Node {
   375  	value := "null"
   376  	return ast.Null(token.New(value, value, e.pos(e.column)))
   377  }
   378  
   379  func (e *Encoder) encodeInt(v int64) ast.Node {
   380  	value := fmt.Sprint(v)
   381  	return ast.Integer(token.New(value, value, e.pos(e.column)))
   382  }
   383  
   384  func (e *Encoder) encodeUint(v uint64) ast.Node {
   385  	value := fmt.Sprint(v)
   386  	return ast.Integer(token.New(value, value, e.pos(e.column)))
   387  }
   388  
   389  func (e *Encoder) encodeFloat(v float64, bitSize int) ast.Node {
   390  	if v == math.Inf(0) {
   391  		value := ".inf"
   392  		return ast.Infinity(token.New(value, value, e.pos(e.column)))
   393  	} else if v == math.Inf(-1) {
   394  		value := "-.inf"
   395  		return ast.Infinity(token.New(value, value, e.pos(e.column)))
   396  	} else if math.IsNaN(v) {
   397  		value := ".nan"
   398  		return ast.Nan(token.New(value, value, e.pos(e.column)))
   399  	}
   400  	value := strconv.FormatFloat(v, 'g', -1, bitSize)
   401  	if !strings.Contains(value, ".") && !strings.Contains(value, "e") {
   402  		// append x.0 suffix to keep float value context
   403  		value = fmt.Sprintf("%s.0", value)
   404  	}
   405  	return ast.Float(token.New(value, value, e.pos(e.column)))
   406  }
   407  
   408  func (e *Encoder) isNeedQuoted(v string) bool {
   409  	if e.isJSONStyle {
   410  		return true
   411  	}
   412  	if e.useLiteralStyleIfMultiline && strings.ContainsAny(v, "\n\r") {
   413  		return false
   414  	}
   415  	if token.IsNeedQuoted(v) {
   416  		return true
   417  	}
   418  	return false
   419  }
   420  
   421  func (e *Encoder) encodeString(v string, column int, s *structFieldNameOption) ast.Node {
   422  	v = e.keyNameChange(v, s)
   423  	if e.isNeedQuoted(v) {
   424  		v = strconv.Quote(v)
   425  	}
   426  	return ast.String(token.New(v, v, e.pos(column)))
   427  }
   428  
   429  func (e *Encoder) encodeBool(v bool) ast.Node {
   430  	value := fmt.Sprint(v)
   431  	return ast.Bool(token.New(value, value, e.pos(e.column)))
   432  }
   433  
   434  func (e *Encoder) encodeSlice(ctx context.Context, value reflect.Value) (ast.Node, error) {
   435  	if e.indentSequence {
   436  		e.column += e.indent
   437  	}
   438  	column := e.column
   439  	sequence := ast.Sequence(token.New("-", "-", e.pos(column)), e.isFlowStyle)
   440  	for i := 0; i < value.Len(); i++ {
   441  		node, err := e.encodeValue(ctx, value.Index(i), column)
   442  		if err != nil {
   443  			return nil, errors.Wrapf(err, "failed to encode value for slice")
   444  		}
   445  		sequence.Values = append(sequence.Values, node)
   446  	}
   447  	if e.indentSequence {
   448  		e.column -= e.indent
   449  	}
   450  	return sequence, nil
   451  }
   452  
   453  func (e *Encoder) encodeArray(ctx context.Context, value reflect.Value) (ast.Node, error) {
   454  	if e.indentSequence {
   455  		e.column += e.indent
   456  	}
   457  	column := e.column
   458  	sequence := ast.Sequence(token.New("-", "-", e.pos(column)), e.isFlowStyle)
   459  	for i := 0; i < value.Len(); i++ {
   460  		node, err := e.encodeValue(ctx, value.Index(i), column)
   461  		if err != nil {
   462  			return nil, errors.Wrapf(err, "failed to encode value for array")
   463  		}
   464  		sequence.Values = append(sequence.Values, node)
   465  	}
   466  	if e.indentSequence {
   467  		e.column -= e.indent
   468  	}
   469  	return sequence, nil
   470  }
   471  
   472  func (e *Encoder) encodeMapItem(ctx context.Context, item MapItem, column int) (*ast.MappingValueNode, error) {
   473  	k := reflect.ValueOf(item.Key)
   474  	v := reflect.ValueOf(item.Value)
   475  	value, err := e.encodeValue(ctx, v, column)
   476  	if err != nil {
   477  		return nil, errors.Wrapf(err, "failed to encode MapItem")
   478  	}
   479  	if m, ok := value.(*ast.MappingNode); ok {
   480  		m.AddColumn(e.indent)
   481  	}
   482  	return ast.MappingValue(
   483  		token.New("", "", e.pos(column)),
   484  		e.encodeString(k.Interface().(string), column, nil),
   485  		value,
   486  	), nil
   487  }
   488  
   489  func (e *Encoder) encodeMapSlice(ctx context.Context, value MapSlice, column int) (ast.Node, error) {
   490  	node := ast.Mapping(token.New("", "", e.pos(column)), e.isFlowStyle)
   491  	for _, item := range value {
   492  		value, err := e.encodeMapItem(ctx, item, column)
   493  		if err != nil {
   494  			return nil, errors.Wrapf(err, "failed to encode MapItem for MapSlice")
   495  		}
   496  		node.Values = append(node.Values, value)
   497  	}
   498  	return node, nil
   499  }
   500  
   501  func (e *Encoder) encodeMap(ctx context.Context, value reflect.Value, column int) ast.Node {
   502  	node := ast.Mapping(token.New("", "", e.pos(column)), e.isFlowStyle)
   503  	keys := make([]interface{}, len(value.MapKeys()))
   504  	for i, k := range value.MapKeys() {
   505  		keys[i] = k.Interface()
   506  	}
   507  	sort.Slice(keys, func(i, j int) bool {
   508  		return fmt.Sprint(keys[i]) < fmt.Sprint(keys[j])
   509  	})
   510  	for _, key := range keys {
   511  		k := reflect.ValueOf(key)
   512  		v := value.MapIndex(k)
   513  		value, err := e.encodeValue(ctx, v, column)
   514  		if err != nil {
   515  			return nil
   516  		}
   517  		if m, ok := value.(*ast.MappingNode); ok {
   518  			m.AddColumn(e.indent)
   519  		}
   520  		node.Values = append(node.Values, ast.MappingValue(
   521  			nil,
   522  			e.encodeString(fmt.Sprint(key), column, nil),
   523  			value,
   524  		))
   525  	}
   526  	return node
   527  }
   528  
   529  // IsZeroer is used to check whether an object is zero to determine
   530  // whether it should be omitted when marshaling with the omitempty flag.
   531  // One notable implementation is time.Time.
   532  type IsZeroer interface {
   533  	IsZero() bool
   534  }
   535  
   536  func (e *Encoder) isZeroValue(v reflect.Value) bool {
   537  	kind := v.Kind()
   538  	if z, ok := v.Interface().(IsZeroer); ok {
   539  		if (kind == reflect.Ptr || kind == reflect.Interface) && v.IsNil() {
   540  			return true
   541  		}
   542  		return z.IsZero()
   543  	}
   544  	switch kind {
   545  	case reflect.String:
   546  		return len(v.String()) == 0
   547  	case reflect.Interface, reflect.Ptr:
   548  		return v.IsNil()
   549  	case reflect.Slice:
   550  		return v.Len() == 0
   551  	case reflect.Map:
   552  		return v.Len() == 0
   553  	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
   554  		return v.Int() == 0
   555  	case reflect.Float32, reflect.Float64:
   556  		return v.Float() == 0
   557  	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
   558  		return v.Uint() == 0
   559  	case reflect.Bool:
   560  		return !v.Bool()
   561  	case reflect.Struct:
   562  		vt := v.Type()
   563  		for i := v.NumField() - 1; i >= 0; i-- {
   564  			if vt.Field(i).PkgPath != "" {
   565  				continue // private field
   566  			}
   567  			if !e.isZeroValue(v.Field(i)) {
   568  				return false
   569  			}
   570  		}
   571  		return true
   572  	}
   573  	return false
   574  }
   575  
   576  func (e *Encoder) encodeTime(v time.Time, column int) ast.Node {
   577  	value := v.Format(time.RFC3339Nano)
   578  	if e.isJSONStyle {
   579  		value = strconv.Quote(value)
   580  	}
   581  	return ast.String(token.New(value, value, e.pos(column)))
   582  }
   583  
   584  func (e *Encoder) encodeDuration(v time.Duration, column int) ast.Node {
   585  	value := v.String()
   586  	if e.isJSONStyle {
   587  		value = strconv.Quote(value)
   588  	}
   589  	return ast.String(token.New(value, value, e.pos(column)))
   590  }
   591  
   592  func (e *Encoder) encodeAnchor(anchorName string, value ast.Node, fieldValue reflect.Value, column int) (ast.Node, error) {
   593  	anchorNode := ast.Anchor(token.New("&", "&", e.pos(column)))
   594  	anchorNode.Name = ast.String(token.New(anchorName, anchorName, e.pos(column)))
   595  	anchorNode.Value = value
   596  	if e.anchorCallback != nil {
   597  		if err := e.anchorCallback(anchorNode, fieldValue.Interface()); err != nil {
   598  			return nil, errors.Wrapf(err, "failed to marshal anchor")
   599  		}
   600  		if snode, ok := anchorNode.Name.(*ast.StringNode); ok {
   601  			anchorName = snode.Value
   602  		}
   603  	}
   604  	if fieldValue.Kind() == reflect.Ptr {
   605  		e.anchorPtrToNameMap[fieldValue.Pointer()] = anchorName
   606  	}
   607  	return anchorNode, nil
   608  }
   609  
   610  func (e *Encoder) encodeStruct(ctx context.Context, value reflect.Value, column int) (ast.Node, error) {
   611  	node := ast.Mapping(token.New("", "", e.pos(column)), e.isFlowStyle)
   612  	structType := value.Type()
   613  	structFieldMap, err := structFieldMap(structType)
   614  	if err != nil {
   615  		return nil, errors.Wrapf(err, "failed to get struct field map")
   616  	}
   617  	hasInlineAnchorField := false
   618  	var inlineAnchorValue reflect.Value
   619  	for i := 0; i < value.NumField(); i++ {
   620  		field := structType.Field(i)
   621  		if isIgnoredStructField(field) {
   622  			continue
   623  		}
   624  		fieldValue := value.FieldByName(field.Name)
   625  		structField := structFieldMap[field.Name]
   626  		if structField.IsOmitEmpty && e.isZeroValue(fieldValue) {
   627  			// omit encoding
   628  			continue
   629  		}
   630  		value, err := e.encodeValue(ctx, fieldValue, column)
   631  		if err != nil {
   632  			return nil, errors.Wrapf(err, "failed to encode value")
   633  		}
   634  		if m, ok := value.(*ast.MappingNode); ok {
   635  			if !e.isFlowStyle && structField.IsFlow {
   636  				m.SetIsFlowStyle(true)
   637  			}
   638  			value.AddColumn(e.indent)
   639  		} else if s, ok := value.(*ast.SequenceNode); ok {
   640  			if !e.isFlowStyle && structField.IsFlow {
   641  				s.SetIsFlowStyle(true)
   642  			}
   643  		}
   644  		key := e.encodeString(structField.RenderName, column, &structFieldNameOption{
   645  			FieldName:         structField.FieldName,
   646  			RenderNameFromTag: structField.RenderNameFromTag,
   647  		})
   648  		switch {
   649  		case structField.AnchorName != "":
   650  			anchorNode, err := e.encodeAnchor(structField.AnchorName, value, fieldValue, column)
   651  			if err != nil {
   652  				return nil, errors.Wrapf(err, "failed to encode anchor")
   653  			}
   654  			value = anchorNode
   655  		case structField.IsAutoAlias:
   656  			if fieldValue.Kind() != reflect.Ptr {
   657  				return nil, xerrors.Errorf(
   658  					"%s in struct is not pointer type. but required automatically alias detection",
   659  					structField.FieldName,
   660  				)
   661  			}
   662  			anchorName := e.anchorPtrToNameMap[fieldValue.Pointer()]
   663  			if anchorName == "" {
   664  				return nil, xerrors.Errorf(
   665  					"cannot find anchor name from pointer address for automatically alias detection",
   666  				)
   667  			}
   668  			aliasName := anchorName
   669  			alias := ast.Alias(token.New("*", "*", e.pos(column)))
   670  			alias.Value = ast.String(token.New(aliasName, aliasName, e.pos(column)))
   671  			value = alias
   672  			if structField.IsInline {
   673  				// if both used alias and inline, output `<<: *alias`
   674  				key = ast.MergeKey(token.New("<<", "<<", e.pos(column)))
   675  			}
   676  		case structField.AliasName != "":
   677  			aliasName := structField.AliasName
   678  			alias := ast.Alias(token.New("*", "*", e.pos(column)))
   679  			alias.Value = ast.String(token.New(aliasName, aliasName, e.pos(column)))
   680  			value = alias
   681  			if structField.IsInline {
   682  				// if both used alias and inline, output `<<: *alias`
   683  				key = ast.MergeKey(token.New("<<", "<<", e.pos(column)))
   684  			}
   685  		case structField.IsInline:
   686  			isAutoAnchor := structField.IsAutoAnchor
   687  			if !hasInlineAnchorField {
   688  				hasInlineAnchorField = isAutoAnchor
   689  			}
   690  			if isAutoAnchor {
   691  				inlineAnchorValue = fieldValue
   692  			}
   693  			mapNode, ok := value.(ast.MapNode)
   694  			if !ok {
   695  				return nil, xerrors.Errorf("inline value is must be map or struct type")
   696  			}
   697  			mapIter := mapNode.MapRange()
   698  			for mapIter.Next() {
   699  				key := mapIter.Key()
   700  				value := mapIter.Value()
   701  				keyName := key.GetToken().Value
   702  				if structFieldMap.isIncludedRenderName(keyName) {
   703  					// if declared same key name, skip encoding this field
   704  					continue
   705  				}
   706  				key.AddColumn(-e.indent)
   707  				value.AddColumn(-e.indent)
   708  				node.Values = append(node.Values, ast.MappingValue(nil, key, value))
   709  			}
   710  			continue
   711  		case structField.IsAutoAnchor:
   712  			anchorNode, err := e.encodeAnchor(structField.RenderName, value, fieldValue, column)
   713  			if err != nil {
   714  				return nil, errors.Wrapf(err, "failed to encode anchor")
   715  			}
   716  			value = anchorNode
   717  		}
   718  		node.Values = append(node.Values, ast.MappingValue(nil, key, value))
   719  	}
   720  	if hasInlineAnchorField {
   721  		node.AddColumn(e.indent)
   722  		anchorName := "anchor"
   723  		anchorNode := ast.Anchor(token.New("&", "&", e.pos(column)))
   724  		anchorNode.Name = ast.String(token.New(anchorName, anchorName, e.pos(column)))
   725  		anchorNode.Value = node
   726  		if e.anchorCallback != nil {
   727  			if err := e.anchorCallback(anchorNode, value.Addr().Interface()); err != nil {
   728  				return nil, errors.Wrapf(err, "failed to marshal anchor")
   729  			}
   730  			if snode, ok := anchorNode.Name.(*ast.StringNode); ok {
   731  				anchorName = snode.Value
   732  			}
   733  		}
   734  		if inlineAnchorValue.Kind() == reflect.Ptr {
   735  			e.anchorPtrToNameMap[inlineAnchorValue.Pointer()] = anchorName
   736  		}
   737  		return anchorNode, nil
   738  	}
   739  	return node, nil
   740  }
   741  
   742  type structFieldNameOption struct {
   743  	FieldName         string
   744  	RenderNameFromTag bool
   745  }
   746  
   747  func (e *Encoder) keyNameChange(name string, s *structFieldNameOption) string {
   748  	if s == nil || s.RenderNameFromTag {
   749  		return name
   750  	}
   751  	switch e.KeyNaming {
   752  	case KeyNamingDefault:
   753  		return name
   754  	case KeyNamingRaw:
   755  		return s.FieldName
   756  	case KeyNamingLowerCamel:
   757  		return strings.ToLower(s.FieldName[:1]) + s.FieldName[1:]
   758  	default:
   759  		return name
   760  	}
   761  }