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