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 }