1 package toml
2
3 import (
4 "bytes"
5 "encoding"
6 "encoding/json"
7 "fmt"
8 "io"
9 "math"
10 "reflect"
11 "sort"
12 "strconv"
13 "strings"
14 "time"
15 "unicode"
16
17 "github.com/pelletier/go-toml/v2/internal/characters"
18 )
19
20
21
22
23 func Marshal(v interface{}) ([]byte, error) {
24 var buf bytes.Buffer
25 enc := NewEncoder(&buf)
26
27 err := enc.Encode(v)
28 if err != nil {
29 return nil, err
30 }
31
32 return buf.Bytes(), nil
33 }
34
35
36 type Encoder struct {
37
38 w io.Writer
39
40
41 tablesInline bool
42 arraysMultiline bool
43 indentSymbol string
44 indentTables bool
45 marshalJsonNumbers bool
46 }
47
48
49 func NewEncoder(w io.Writer) *Encoder {
50 return &Encoder{
51 w: w,
52 indentSymbol: " ",
53 }
54 }
55
56
57
58
59
60
61
62 func (enc *Encoder) SetTablesInline(inline bool) *Encoder {
63 enc.tablesInline = inline
64 return enc
65 }
66
67
68
69
70
71
72
73 func (enc *Encoder) SetArraysMultiline(multiline bool) *Encoder {
74 enc.arraysMultiline = multiline
75 return enc
76 }
77
78
79
80
81 func (enc *Encoder) SetIndentSymbol(s string) *Encoder {
82 enc.indentSymbol = s
83 return enc
84 }
85
86
87 func (enc *Encoder) SetIndentTables(indent bool) *Encoder {
88 enc.indentTables = indent
89 return enc
90 }
91
92
93
94
95
96
97
98 func (enc *Encoder) SetMarshalJsonNumbers(indent bool) *Encoder {
99 enc.marshalJsonNumbers = indent
100 return enc
101 }
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171 func (enc *Encoder) Encode(v interface{}) error {
172 var (
173 b []byte
174 ctx encoderCtx
175 )
176
177 ctx.inline = enc.tablesInline
178
179 if v == nil {
180 return fmt.Errorf("toml: cannot encode a nil interface")
181 }
182
183 b, err := enc.encode(b, ctx, reflect.ValueOf(v))
184 if err != nil {
185 return err
186 }
187
188 _, err = enc.w.Write(b)
189 if err != nil {
190 return fmt.Errorf("toml: cannot write: %w", err)
191 }
192
193 return nil
194 }
195
196 type valueOptions struct {
197 multiline bool
198 omitempty bool
199 commented bool
200 comment string
201 }
202
203 type encoderCtx struct {
204
205 parentKey []string
206
207
208 key string
209
210 hasKey bool
211
212
213
214 insideKv bool
215
216
217 skipTableHeader bool
218
219
220 inline bool
221
222
223 indent int
224
225
226 commented bool
227
228
229 options valueOptions
230 }
231
232 func (ctx *encoderCtx) shiftKey() {
233 if ctx.hasKey {
234 ctx.parentKey = append(ctx.parentKey, ctx.key)
235 ctx.clearKey()
236 }
237 }
238
239 func (ctx *encoderCtx) setKey(k string) {
240 ctx.key = k
241 ctx.hasKey = true
242 }
243
244 func (ctx *encoderCtx) clearKey() {
245 ctx.key = ""
246 ctx.hasKey = false
247 }
248
249 func (ctx *encoderCtx) isRoot() bool {
250 return len(ctx.parentKey) == 0 && !ctx.hasKey
251 }
252
253 func (enc *Encoder) encode(b []byte, ctx encoderCtx, v reflect.Value) ([]byte, error) {
254 i := v.Interface()
255
256 switch x := i.(type) {
257 case time.Time:
258 if x.Nanosecond() > 0 {
259 return x.AppendFormat(b, time.RFC3339Nano), nil
260 }
261 return x.AppendFormat(b, time.RFC3339), nil
262 case LocalTime:
263 return append(b, x.String()...), nil
264 case LocalDate:
265 return append(b, x.String()...), nil
266 case LocalDateTime:
267 return append(b, x.String()...), nil
268 case json.Number:
269 if enc.marshalJsonNumbers {
270 if x == "" {
271 return append(b, "0"...), nil
272 } else if v, err := x.Int64(); err == nil {
273 return enc.encode(b, ctx, reflect.ValueOf(v))
274 } else if f, err := x.Float64(); err == nil {
275 return enc.encode(b, ctx, reflect.ValueOf(f))
276 } else {
277 return nil, fmt.Errorf("toml: unable to convert %q to int64 or float64", x)
278 }
279 }
280 }
281
282 hasTextMarshaler := v.Type().Implements(textMarshalerType)
283 if hasTextMarshaler || (v.CanAddr() && reflect.PtrTo(v.Type()).Implements(textMarshalerType)) {
284 if !hasTextMarshaler {
285 v = v.Addr()
286 }
287
288 if ctx.isRoot() {
289 return nil, fmt.Errorf("toml: type %s implementing the TextMarshaler interface cannot be a root element", v.Type())
290 }
291
292 text, err := v.Interface().(encoding.TextMarshaler).MarshalText()
293 if err != nil {
294 return nil, err
295 }
296
297 b = enc.encodeString(b, string(text), ctx.options)
298
299 return b, nil
300 }
301
302 switch v.Kind() {
303
304 case reflect.Map:
305 return enc.encodeMap(b, ctx, v)
306 case reflect.Struct:
307 return enc.encodeStruct(b, ctx, v)
308 case reflect.Slice, reflect.Array:
309 return enc.encodeSlice(b, ctx, v)
310 case reflect.Interface:
311 if v.IsNil() {
312 return nil, fmt.Errorf("toml: encoding a nil interface is not supported")
313 }
314
315 return enc.encode(b, ctx, v.Elem())
316 case reflect.Ptr:
317 if v.IsNil() {
318 return enc.encode(b, ctx, reflect.Zero(v.Type().Elem()))
319 }
320
321 return enc.encode(b, ctx, v.Elem())
322
323
324 case reflect.String:
325 b = enc.encodeString(b, v.String(), ctx.options)
326 case reflect.Float32:
327 f := v.Float()
328
329 if math.IsNaN(f) {
330 b = append(b, "nan"...)
331 } else if f > math.MaxFloat32 {
332 b = append(b, "inf"...)
333 } else if f < -math.MaxFloat32 {
334 b = append(b, "-inf"...)
335 } else if math.Trunc(f) == f {
336 b = strconv.AppendFloat(b, f, 'f', 1, 32)
337 } else {
338 b = strconv.AppendFloat(b, f, 'f', -1, 32)
339 }
340 case reflect.Float64:
341 f := v.Float()
342 if math.IsNaN(f) {
343 b = append(b, "nan"...)
344 } else if f > math.MaxFloat64 {
345 b = append(b, "inf"...)
346 } else if f < -math.MaxFloat64 {
347 b = append(b, "-inf"...)
348 } else if math.Trunc(f) == f {
349 b = strconv.AppendFloat(b, f, 'f', 1, 64)
350 } else {
351 b = strconv.AppendFloat(b, f, 'f', -1, 64)
352 }
353 case reflect.Bool:
354 if v.Bool() {
355 b = append(b, "true"...)
356 } else {
357 b = append(b, "false"...)
358 }
359 case reflect.Uint64, reflect.Uint32, reflect.Uint16, reflect.Uint8, reflect.Uint:
360 x := v.Uint()
361 if x > uint64(math.MaxInt64) {
362 return nil, fmt.Errorf("toml: not encoding uint (%d) greater than max int64 (%d)", x, int64(math.MaxInt64))
363 }
364 b = strconv.AppendUint(b, x, 10)
365 case reflect.Int64, reflect.Int32, reflect.Int16, reflect.Int8, reflect.Int:
366 b = strconv.AppendInt(b, v.Int(), 10)
367 default:
368 return nil, fmt.Errorf("toml: cannot encode value of type %s", v.Kind())
369 }
370
371 return b, nil
372 }
373
374 func isNil(v reflect.Value) bool {
375 switch v.Kind() {
376 case reflect.Ptr, reflect.Interface, reflect.Map:
377 return v.IsNil()
378 default:
379 return false
380 }
381 }
382
383 func shouldOmitEmpty(options valueOptions, v reflect.Value) bool {
384 return options.omitempty && isEmptyValue(v)
385 }
386
387 func (enc *Encoder) encodeKv(b []byte, ctx encoderCtx, options valueOptions, v reflect.Value) ([]byte, error) {
388 var err error
389
390 if !ctx.inline {
391 b = enc.encodeComment(ctx.indent, options.comment, b)
392 b = enc.commented(ctx.commented, b)
393 b = enc.indent(ctx.indent, b)
394 }
395
396 b = enc.encodeKey(b, ctx.key)
397 b = append(b, " = "...)
398
399
400
401 subctx := ctx
402 subctx.insideKv = true
403 subctx.shiftKey()
404 subctx.options = options
405
406 b, err = enc.encode(b, subctx, v)
407 if err != nil {
408 return nil, err
409 }
410
411 return b, nil
412 }
413
414 func (enc *Encoder) commented(commented bool, b []byte) []byte {
415 if commented {
416 return append(b, "# "...)
417 }
418 return b
419 }
420
421 func isEmptyValue(v reflect.Value) bool {
422 switch v.Kind() {
423 case reflect.Struct:
424 return isEmptyStruct(v)
425 case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
426 return v.Len() == 0
427 case reflect.Bool:
428 return !v.Bool()
429 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
430 return v.Int() == 0
431 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
432 return v.Uint() == 0
433 case reflect.Float32, reflect.Float64:
434 return v.Float() == 0
435 case reflect.Interface, reflect.Ptr:
436 return v.IsNil()
437 }
438 return false
439 }
440
441 func isEmptyStruct(v reflect.Value) bool {
442
443 typ := v.Type()
444 for i := 0; i < typ.NumField(); i++ {
445 fieldType := typ.Field(i)
446
447
448 if fieldType.PkgPath != "" {
449 continue
450 }
451
452 tag := fieldType.Tag.Get("toml")
453
454
455 if tag == "-" {
456 continue
457 }
458
459 f := v.Field(i)
460
461 if !isEmptyValue(f) {
462 return false
463 }
464 }
465
466 return true
467 }
468
469 const literalQuote = '\''
470
471 func (enc *Encoder) encodeString(b []byte, v string, options valueOptions) []byte {
472 if needsQuoting(v) {
473 return enc.encodeQuotedString(options.multiline, b, v)
474 }
475
476 return enc.encodeLiteralString(b, v)
477 }
478
479 func needsQuoting(v string) bool {
480
481 for _, b := range []byte(v) {
482 if b == '\'' || b == '\r' || b == '\n' || characters.InvalidAscii(b) {
483 return true
484 }
485 }
486 return false
487 }
488
489
490 func (enc *Encoder) encodeLiteralString(b []byte, v string) []byte {
491 b = append(b, literalQuote)
492 b = append(b, v...)
493 b = append(b, literalQuote)
494
495 return b
496 }
497
498 func (enc *Encoder) encodeQuotedString(multiline bool, b []byte, v string) []byte {
499 stringQuote := `"`
500
501 if multiline {
502 stringQuote = `"""`
503 }
504
505 b = append(b, stringQuote...)
506 if multiline {
507 b = append(b, '\n')
508 }
509
510 const (
511 hextable = "0123456789ABCDEF"
512
513 nul = 0x0
514 bs = 0x8
515 lf = 0xa
516 us = 0x1f
517 del = 0x7f
518 )
519
520 for _, r := range []byte(v) {
521 switch r {
522 case '\\':
523 b = append(b, `\\`...)
524 case '"':
525 b = append(b, `\"`...)
526 case '\b':
527 b = append(b, `\b`...)
528 case '\f':
529 b = append(b, `\f`...)
530 case '\n':
531 if multiline {
532 b = append(b, r)
533 } else {
534 b = append(b, `\n`...)
535 }
536 case '\r':
537 b = append(b, `\r`...)
538 case '\t':
539 b = append(b, `\t`...)
540 default:
541 switch {
542 case r >= nul && r <= bs, r >= lf && r <= us, r == del:
543 b = append(b, `\u00`...)
544 b = append(b, hextable[r>>4])
545 b = append(b, hextable[r&0x0f])
546 default:
547 b = append(b, r)
548 }
549 }
550 }
551
552 b = append(b, stringQuote...)
553
554 return b
555 }
556
557
558 func (enc *Encoder) encodeUnquotedKey(b []byte, v string) []byte {
559 return append(b, v...)
560 }
561
562 func (enc *Encoder) encodeTableHeader(ctx encoderCtx, b []byte) ([]byte, error) {
563 if len(ctx.parentKey) == 0 {
564 return b, nil
565 }
566
567 b = enc.encodeComment(ctx.indent, ctx.options.comment, b)
568
569 b = enc.commented(ctx.commented, b)
570
571 b = enc.indent(ctx.indent, b)
572
573 b = append(b, '[')
574
575 b = enc.encodeKey(b, ctx.parentKey[0])
576
577 for _, k := range ctx.parentKey[1:] {
578 b = append(b, '.')
579 b = enc.encodeKey(b, k)
580 }
581
582 b = append(b, "]\n"...)
583
584 return b, nil
585 }
586
587
588 func (enc *Encoder) encodeKey(b []byte, k string) []byte {
589 needsQuotation := false
590 cannotUseLiteral := false
591
592 if len(k) == 0 {
593 return append(b, "''"...)
594 }
595
596 for _, c := range k {
597 if (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '-' || c == '_' {
598 continue
599 }
600
601 if c == literalQuote {
602 cannotUseLiteral = true
603 }
604
605 needsQuotation = true
606 }
607
608 if needsQuotation && needsQuoting(k) {
609 cannotUseLiteral = true
610 }
611
612 switch {
613 case cannotUseLiteral:
614 return enc.encodeQuotedString(false, b, k)
615 case needsQuotation:
616 return enc.encodeLiteralString(b, k)
617 default:
618 return enc.encodeUnquotedKey(b, k)
619 }
620 }
621
622 func (enc *Encoder) keyToString(k reflect.Value) (string, error) {
623 keyType := k.Type()
624 switch {
625 case keyType.Kind() == reflect.String:
626 return k.String(), nil
627
628 case keyType.Implements(textMarshalerType):
629 keyB, err := k.Interface().(encoding.TextMarshaler).MarshalText()
630 if err != nil {
631 return "", fmt.Errorf("toml: error marshalling key %v from text: %w", k, err)
632 }
633 return string(keyB), nil
634 }
635 return "", fmt.Errorf("toml: type %s is not supported as a map key", keyType.Kind())
636 }
637
638 func (enc *Encoder) encodeMap(b []byte, ctx encoderCtx, v reflect.Value) ([]byte, error) {
639 var (
640 t table
641 emptyValueOptions valueOptions
642 )
643
644 iter := v.MapRange()
645 for iter.Next() {
646 v := iter.Value()
647
648 if isNil(v) {
649 continue
650 }
651
652 k, err := enc.keyToString(iter.Key())
653 if err != nil {
654 return nil, err
655 }
656
657 if willConvertToTableOrArrayTable(ctx, v) {
658 t.pushTable(k, v, emptyValueOptions)
659 } else {
660 t.pushKV(k, v, emptyValueOptions)
661 }
662 }
663
664 sortEntriesByKey(t.kvs)
665 sortEntriesByKey(t.tables)
666
667 return enc.encodeTable(b, ctx, t)
668 }
669
670 func sortEntriesByKey(e []entry) {
671 sort.Slice(e, func(i, j int) bool {
672 return e[i].Key < e[j].Key
673 })
674 }
675
676 type entry struct {
677 Key string
678 Value reflect.Value
679 Options valueOptions
680 }
681
682 type table struct {
683 kvs []entry
684 tables []entry
685 }
686
687 func (t *table) pushKV(k string, v reflect.Value, options valueOptions) {
688 for _, e := range t.kvs {
689 if e.Key == k {
690 return
691 }
692 }
693
694 t.kvs = append(t.kvs, entry{Key: k, Value: v, Options: options})
695 }
696
697 func (t *table) pushTable(k string, v reflect.Value, options valueOptions) {
698 for _, e := range t.tables {
699 if e.Key == k {
700 return
701 }
702 }
703 t.tables = append(t.tables, entry{Key: k, Value: v, Options: options})
704 }
705
706 func walkStruct(ctx encoderCtx, t *table, v reflect.Value) {
707
708 typ := v.Type()
709 for i := 0; i < typ.NumField(); i++ {
710 fieldType := typ.Field(i)
711
712
713 if fieldType.PkgPath != "" {
714 continue
715 }
716
717 tag := fieldType.Tag.Get("toml")
718
719
720 if tag == "-" {
721 continue
722 }
723
724 k, opts := parseTag(tag)
725 if !isValidName(k) {
726 k = ""
727 }
728
729 f := v.Field(i)
730
731 if k == "" {
732 if fieldType.Anonymous {
733 if fieldType.Type.Kind() == reflect.Struct {
734 walkStruct(ctx, t, f)
735 } else if fieldType.Type.Kind() == reflect.Pointer && !f.IsNil() && f.Elem().Kind() == reflect.Struct {
736 walkStruct(ctx, t, f.Elem())
737 }
738 continue
739 } else {
740 k = fieldType.Name
741 }
742 }
743
744 if isNil(f) {
745 continue
746 }
747
748 options := valueOptions{
749 multiline: opts.multiline,
750 omitempty: opts.omitempty,
751 commented: opts.commented,
752 comment: fieldType.Tag.Get("comment"),
753 }
754
755 if opts.inline || !willConvertToTableOrArrayTable(ctx, f) {
756 t.pushKV(k, f, options)
757 } else {
758 t.pushTable(k, f, options)
759 }
760 }
761 }
762
763 func (enc *Encoder) encodeStruct(b []byte, ctx encoderCtx, v reflect.Value) ([]byte, error) {
764 var t table
765
766 walkStruct(ctx, &t, v)
767
768 return enc.encodeTable(b, ctx, t)
769 }
770
771 func (enc *Encoder) encodeComment(indent int, comment string, b []byte) []byte {
772 for len(comment) > 0 {
773 var line string
774 idx := strings.IndexByte(comment, '\n')
775 if idx >= 0 {
776 line = comment[:idx]
777 comment = comment[idx+1:]
778 } else {
779 line = comment
780 comment = ""
781 }
782 b = enc.indent(indent, b)
783 b = append(b, "# "...)
784 b = append(b, line...)
785 b = append(b, '\n')
786 }
787 return b
788 }
789
790 func isValidName(s string) bool {
791 if s == "" {
792 return false
793 }
794 for _, c := range s {
795 switch {
796 case strings.ContainsRune("!#$%&()*+-./:;<=>?@[]^_{|}~ ", c):
797
798
799
800 case !unicode.IsLetter(c) && !unicode.IsDigit(c):
801 return false
802 }
803 }
804 return true
805 }
806
807 type tagOptions struct {
808 multiline bool
809 inline bool
810 omitempty bool
811 commented bool
812 }
813
814 func parseTag(tag string) (string, tagOptions) {
815 opts := tagOptions{}
816
817 idx := strings.Index(tag, ",")
818 if idx == -1 {
819 return tag, opts
820 }
821
822 raw := tag[idx+1:]
823 tag = string(tag[:idx])
824 for raw != "" {
825 var o string
826 i := strings.Index(raw, ",")
827 if i >= 0 {
828 o, raw = raw[:i], raw[i+1:]
829 } else {
830 o, raw = raw, ""
831 }
832 switch o {
833 case "multiline":
834 opts.multiline = true
835 case "inline":
836 opts.inline = true
837 case "omitempty":
838 opts.omitempty = true
839 case "commented":
840 opts.commented = true
841 }
842 }
843
844 return tag, opts
845 }
846
847 func (enc *Encoder) encodeTable(b []byte, ctx encoderCtx, t table) ([]byte, error) {
848 var err error
849
850 ctx.shiftKey()
851
852 if ctx.insideKv || (ctx.inline && !ctx.isRoot()) {
853 return enc.encodeTableInline(b, ctx, t)
854 }
855
856 if !ctx.skipTableHeader {
857 b, err = enc.encodeTableHeader(ctx, b)
858 if err != nil {
859 return nil, err
860 }
861
862 if enc.indentTables && len(ctx.parentKey) > 0 {
863 ctx.indent++
864 }
865 }
866 ctx.skipTableHeader = false
867
868 hasNonEmptyKV := false
869 for _, kv := range t.kvs {
870 if shouldOmitEmpty(kv.Options, kv.Value) {
871 continue
872 }
873 hasNonEmptyKV = true
874
875 ctx.setKey(kv.Key)
876 ctx2 := ctx
877 ctx2.commented = kv.Options.commented || ctx2.commented
878
879 b, err = enc.encodeKv(b, ctx2, kv.Options, kv.Value)
880 if err != nil {
881 return nil, err
882 }
883
884 b = append(b, '\n')
885 }
886
887 first := true
888 for _, table := range t.tables {
889 if shouldOmitEmpty(table.Options, table.Value) {
890 continue
891 }
892 if first {
893 first = false
894 if hasNonEmptyKV {
895 b = append(b, '\n')
896 }
897 } else {
898 b = append(b, "\n"...)
899 }
900
901 ctx.setKey(table.Key)
902
903 ctx.options = table.Options
904 ctx2 := ctx
905 ctx2.commented = ctx2.commented || ctx.options.commented
906
907 b, err = enc.encode(b, ctx2, table.Value)
908 if err != nil {
909 return nil, err
910 }
911 }
912
913 return b, nil
914 }
915
916 func (enc *Encoder) encodeTableInline(b []byte, ctx encoderCtx, t table) ([]byte, error) {
917 var err error
918
919 b = append(b, '{')
920
921 first := true
922 for _, kv := range t.kvs {
923 if shouldOmitEmpty(kv.Options, kv.Value) {
924 continue
925 }
926
927 if first {
928 first = false
929 } else {
930 b = append(b, `, `...)
931 }
932
933 ctx.setKey(kv.Key)
934
935 b, err = enc.encodeKv(b, ctx, kv.Options, kv.Value)
936 if err != nil {
937 return nil, err
938 }
939 }
940
941 if len(t.tables) > 0 {
942 panic("inline table cannot contain nested tables, only key-values")
943 }
944
945 b = append(b, "}"...)
946
947 return b, nil
948 }
949
950 func willConvertToTable(ctx encoderCtx, v reflect.Value) bool {
951 if !v.IsValid() {
952 return false
953 }
954 if v.Type() == timeType || v.Type().Implements(textMarshalerType) || (v.Kind() != reflect.Ptr && v.CanAddr() && reflect.PtrTo(v.Type()).Implements(textMarshalerType)) {
955 return false
956 }
957
958 t := v.Type()
959 switch t.Kind() {
960 case reflect.Map, reflect.Struct:
961 return !ctx.inline
962 case reflect.Interface:
963 return willConvertToTable(ctx, v.Elem())
964 case reflect.Ptr:
965 if v.IsNil() {
966 return false
967 }
968
969 return willConvertToTable(ctx, v.Elem())
970 default:
971 return false
972 }
973 }
974
975 func willConvertToTableOrArrayTable(ctx encoderCtx, v reflect.Value) bool {
976 if ctx.insideKv {
977 return false
978 }
979 t := v.Type()
980
981 if t.Kind() == reflect.Interface {
982 return willConvertToTableOrArrayTable(ctx, v.Elem())
983 }
984
985 if t.Kind() == reflect.Slice || t.Kind() == reflect.Array {
986 if v.Len() == 0 {
987
988 return false
989 }
990
991 for i := 0; i < v.Len(); i++ {
992 t := willConvertToTable(ctx, v.Index(i))
993
994 if !t {
995 return false
996 }
997 }
998
999 return true
1000 }
1001
1002 return willConvertToTable(ctx, v)
1003 }
1004
1005 func (enc *Encoder) encodeSlice(b []byte, ctx encoderCtx, v reflect.Value) ([]byte, error) {
1006 if v.Len() == 0 {
1007 b = append(b, "[]"...)
1008
1009 return b, nil
1010 }
1011
1012 if willConvertToTableOrArrayTable(ctx, v) {
1013 return enc.encodeSliceAsArrayTable(b, ctx, v)
1014 }
1015
1016 return enc.encodeSliceAsArray(b, ctx, v)
1017 }
1018
1019
1020
1021 func (enc *Encoder) encodeSliceAsArrayTable(b []byte, ctx encoderCtx, v reflect.Value) ([]byte, error) {
1022 ctx.shiftKey()
1023
1024 scratch := make([]byte, 0, 64)
1025
1026 scratch = enc.commented(ctx.commented, scratch)
1027
1028 if enc.indentTables {
1029 scratch = enc.indent(ctx.indent, scratch)
1030 }
1031
1032 scratch = append(scratch, "[["...)
1033
1034 for i, k := range ctx.parentKey {
1035 if i > 0 {
1036 scratch = append(scratch, '.')
1037 }
1038
1039 scratch = enc.encodeKey(scratch, k)
1040 }
1041
1042 scratch = append(scratch, "]]\n"...)
1043 ctx.skipTableHeader = true
1044
1045 b = enc.encodeComment(ctx.indent, ctx.options.comment, b)
1046
1047 if enc.indentTables {
1048 ctx.indent++
1049 }
1050
1051 for i := 0; i < v.Len(); i++ {
1052 if i != 0 {
1053 b = append(b, "\n"...)
1054 }
1055
1056 b = append(b, scratch...)
1057
1058 var err error
1059 b, err = enc.encode(b, ctx, v.Index(i))
1060 if err != nil {
1061 return nil, err
1062 }
1063 }
1064
1065 return b, nil
1066 }
1067
1068 func (enc *Encoder) encodeSliceAsArray(b []byte, ctx encoderCtx, v reflect.Value) ([]byte, error) {
1069 multiline := ctx.options.multiline || enc.arraysMultiline
1070 separator := ", "
1071
1072 b = append(b, '[')
1073
1074 subCtx := ctx
1075 subCtx.options = valueOptions{}
1076
1077 if multiline {
1078 separator = ",\n"
1079
1080 b = append(b, '\n')
1081
1082 subCtx.indent++
1083 }
1084
1085 var err error
1086 first := true
1087
1088 for i := 0; i < v.Len(); i++ {
1089 if first {
1090 first = false
1091 } else {
1092 b = append(b, separator...)
1093 }
1094
1095 if multiline {
1096 b = enc.indent(subCtx.indent, b)
1097 }
1098
1099 b, err = enc.encode(b, subCtx, v.Index(i))
1100 if err != nil {
1101 return nil, err
1102 }
1103 }
1104
1105 if multiline {
1106 b = append(b, '\n')
1107 b = enc.indent(ctx.indent, b)
1108 }
1109
1110 b = append(b, ']')
1111
1112 return b, nil
1113 }
1114
1115 func (enc *Encoder) indent(level int, b []byte) []byte {
1116 for i := 0; i < level; i++ {
1117 b = append(b, enc.indentSymbol...)
1118 }
1119
1120 return b
1121 }
1122
View as plain text