podman

Форк
0
1058 строк · 22.0 Кб
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
// Marshal serializes a Go value as a TOML document.
20
//
21
// It is a shortcut for Encoder.Encode() with the default options.
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
// Encoder writes a TOML document to an output stream.
35
type Encoder struct {
36
	// output
37
	w io.Writer
38

39
	// global settings
40
	tablesInline    bool
41
	arraysMultiline bool
42
	indentSymbol    string
43
	indentTables    bool
44
}
45

46
// NewEncoder returns a new Encoder that writes to w.
47
func NewEncoder(w io.Writer) *Encoder {
48
	return &Encoder{
49
		w:            w,
50
		indentSymbol: "  ",
51
	}
52
}
53

54
// SetTablesInline forces the encoder to emit all tables inline.
55
//
56
// This behavior can be controlled on an individual struct field basis with the
57
// inline tag:
58
//
59
//	MyField `toml:",inline"`
60
func (enc *Encoder) SetTablesInline(inline bool) *Encoder {
61
	enc.tablesInline = inline
62
	return enc
63
}
64

65
// SetArraysMultiline forces the encoder to emit all arrays with one element per
66
// line.
67
//
68
// This behavior can be controlled on an individual struct field basis with the multiline tag:
69
//
70
//	MyField `multiline:"true"`
71
func (enc *Encoder) SetArraysMultiline(multiline bool) *Encoder {
72
	enc.arraysMultiline = multiline
73
	return enc
74
}
75

76
// SetIndentSymbol defines the string that should be used for indentation. The
77
// provided string is repeated for each indentation level. Defaults to two
78
// spaces.
79
func (enc *Encoder) SetIndentSymbol(s string) *Encoder {
80
	enc.indentSymbol = s
81
	return enc
82
}
83

84
// SetIndentTables forces the encoder to intent tables and array tables.
85
func (enc *Encoder) SetIndentTables(indent bool) *Encoder {
86
	enc.indentTables = indent
87
	return enc
88
}
89

90
// Encode writes a TOML representation of v to the stream.
91
//
92
// If v cannot be represented to TOML it returns an error.
93
//
94
// # Encoding rules
95
//
96
// A top level slice containing only maps or structs is encoded as [[table
97
// array]].
98
//
99
// All slices not matching rule 1 are encoded as [array]. As a result, any map
100
// or struct they contain is encoded as an {inline table}.
101
//
102
// Nil interfaces and nil pointers are not supported.
103
//
104
// Keys in key-values always have one part.
105
//
106
// Intermediate tables are always printed.
107
//
108
// By default, strings are encoded as literal string, unless they contain either
109
// a newline character or a single quote. In that case they are emitted as
110
// quoted strings.
111
//
112
// Unsigned integers larger than math.MaxInt64 cannot be encoded. Doing so
113
// results in an error. This rule exists because the TOML specification only
114
// requires parsers to support at least the 64 bits integer range. Allowing
115
// larger numbers would create non-standard TOML documents, which may not be
116
// readable (at best) by other implementations. To encode such numbers, a
117
// solution is a custom type that implements encoding.TextMarshaler.
118
//
119
// When encoding structs, fields are encoded in order of definition, with their
120
// exact name.
121
//
122
// Tables and array tables are separated by empty lines. However, consecutive
123
// subtables definitions are not. For example:
124
//
125
//	[top1]
126
//
127
//	[top2]
128
//	[top2.child1]
129
//
130
//	[[array]]
131
//
132
//	[[array]]
133
//	[array.child2]
134
//
135
// # Struct tags
136
//
137
// The encoding of each public struct field can be customized by the format
138
// string in the "toml" key of the struct field's tag. This follows
139
// encoding/json's convention. The format string starts with the name of the
140
// field, optionally followed by a comma-separated list of options. The name may
141
// be empty in order to provide options without overriding the default name.
142
//
143
// The "multiline" option emits strings as quoted multi-line TOML strings. It
144
// has no effect on fields that would not be encoded as strings.
145
//
146
// The "inline" option turns fields that would be emitted as tables into inline
147
// tables instead. It has no effect on other fields.
148
//
149
// The "omitempty" option prevents empty values or groups from being emitted.
150
//
151
// In addition to the "toml" tag struct tag, a "comment" tag can be used to emit
152
// a TOML comment before the value being annotated. Comments are ignored inside
153
// inline tables. For array tables, the comment is only present before the first
154
// element of the array.
155
func (enc *Encoder) Encode(v interface{}) error {
156
	var (
157
		b   []byte
158
		ctx encoderCtx
159
	)
160

161
	ctx.inline = enc.tablesInline
162

163
	if v == nil {
164
		return fmt.Errorf("toml: cannot encode a nil interface")
165
	}
166

167
	b, err := enc.encode(b, ctx, reflect.ValueOf(v))
168
	if err != nil {
169
		return err
170
	}
171

172
	_, err = enc.w.Write(b)
173
	if err != nil {
174
		return fmt.Errorf("toml: cannot write: %w", err)
175
	}
176

177
	return nil
178
}
179

180
type valueOptions struct {
181
	multiline bool
182
	omitempty bool
183
	comment   string
184
}
185

186
type encoderCtx struct {
187
	// Current top-level key.
188
	parentKey []string
189

190
	// Key that should be used for a KV.
191
	key string
192
	// Extra flag to account for the empty string
193
	hasKey bool
194

195
	// Set to true to indicate that the encoder is inside a KV, so that all
196
	// tables need to be inlined.
197
	insideKv bool
198

199
	// Set to true to skip the first table header in an array table.
200
	skipTableHeader bool
201

202
	// Should the next table be encoded as inline
203
	inline bool
204

205
	// Indentation level
206
	indent int
207

208
	// Options coming from struct tags
209
	options valueOptions
210
}
211

212
func (ctx *encoderCtx) shiftKey() {
213
	if ctx.hasKey {
214
		ctx.parentKey = append(ctx.parentKey, ctx.key)
215
		ctx.clearKey()
216
	}
217
}
218

219
func (ctx *encoderCtx) setKey(k string) {
220
	ctx.key = k
221
	ctx.hasKey = true
222
}
223

224
func (ctx *encoderCtx) clearKey() {
225
	ctx.key = ""
226
	ctx.hasKey = false
227
}
228

229
func (ctx *encoderCtx) isRoot() bool {
230
	return len(ctx.parentKey) == 0 && !ctx.hasKey
231
}
232

233
func (enc *Encoder) encode(b []byte, ctx encoderCtx, v reflect.Value) ([]byte, error) {
234
	i := v.Interface()
235

236
	switch x := i.(type) {
237
	case time.Time:
238
		if x.Nanosecond() > 0 {
239
			return x.AppendFormat(b, time.RFC3339Nano), nil
240
		}
241
		return x.AppendFormat(b, time.RFC3339), nil
242
	case LocalTime:
243
		return append(b, x.String()...), nil
244
	case LocalDate:
245
		return append(b, x.String()...), nil
246
	case LocalDateTime:
247
		return append(b, x.String()...), nil
248
	}
249

250
	hasTextMarshaler := v.Type().Implements(textMarshalerType)
251
	if hasTextMarshaler || (v.CanAddr() && reflect.PtrTo(v.Type()).Implements(textMarshalerType)) {
252
		if !hasTextMarshaler {
253
			v = v.Addr()
254
		}
255

256
		if ctx.isRoot() {
257
			return nil, fmt.Errorf("toml: type %s implementing the TextMarshaler interface cannot be a root element", v.Type())
258
		}
259

260
		text, err := v.Interface().(encoding.TextMarshaler).MarshalText()
261
		if err != nil {
262
			return nil, err
263
		}
264

265
		b = enc.encodeString(b, string(text), ctx.options)
266

267
		return b, nil
268
	}
269

270
	switch v.Kind() {
271
	// containers
272
	case reflect.Map:
273
		return enc.encodeMap(b, ctx, v)
274
	case reflect.Struct:
275
		return enc.encodeStruct(b, ctx, v)
276
	case reflect.Slice:
277
		return enc.encodeSlice(b, ctx, v)
278
	case reflect.Interface:
279
		if v.IsNil() {
280
			return nil, fmt.Errorf("toml: encoding a nil interface is not supported")
281
		}
282

283
		return enc.encode(b, ctx, v.Elem())
284
	case reflect.Ptr:
285
		if v.IsNil() {
286
			return enc.encode(b, ctx, reflect.Zero(v.Type().Elem()))
287
		}
288

289
		return enc.encode(b, ctx, v.Elem())
290

291
	// values
292
	case reflect.String:
293
		b = enc.encodeString(b, v.String(), ctx.options)
294
	case reflect.Float32:
295
		f := v.Float()
296

297
		if math.IsNaN(f) {
298
			b = append(b, "nan"...)
299
		} else if f > math.MaxFloat32 {
300
			b = append(b, "inf"...)
301
		} else if f < -math.MaxFloat32 {
302
			b = append(b, "-inf"...)
303
		} else if math.Trunc(f) == f {
304
			b = strconv.AppendFloat(b, f, 'f', 1, 32)
305
		} else {
306
			b = strconv.AppendFloat(b, f, 'f', -1, 32)
307
		}
308
	case reflect.Float64:
309
		f := v.Float()
310
		if math.IsNaN(f) {
311
			b = append(b, "nan"...)
312
		} else if f > math.MaxFloat64 {
313
			b = append(b, "inf"...)
314
		} else if f < -math.MaxFloat64 {
315
			b = append(b, "-inf"...)
316
		} else if math.Trunc(f) == f {
317
			b = strconv.AppendFloat(b, f, 'f', 1, 64)
318
		} else {
319
			b = strconv.AppendFloat(b, f, 'f', -1, 64)
320
		}
321
	case reflect.Bool:
322
		if v.Bool() {
323
			b = append(b, "true"...)
324
		} else {
325
			b = append(b, "false"...)
326
		}
327
	case reflect.Uint64, reflect.Uint32, reflect.Uint16, reflect.Uint8, reflect.Uint:
328
		x := v.Uint()
329
		if x > uint64(math.MaxInt64) {
330
			return nil, fmt.Errorf("toml: not encoding uint (%d) greater than max int64 (%d)", x, int64(math.MaxInt64))
331
		}
332
		b = strconv.AppendUint(b, x, 10)
333
	case reflect.Int64, reflect.Int32, reflect.Int16, reflect.Int8, reflect.Int:
334
		b = strconv.AppendInt(b, v.Int(), 10)
335
	default:
336
		return nil, fmt.Errorf("toml: cannot encode value of type %s", v.Kind())
337
	}
338

339
	return b, nil
340
}
341

342
func isNil(v reflect.Value) bool {
343
	switch v.Kind() {
344
	case reflect.Ptr, reflect.Interface, reflect.Map:
345
		return v.IsNil()
346
	default:
347
		return false
348
	}
349
}
350

351
func shouldOmitEmpty(options valueOptions, v reflect.Value) bool {
352
	return options.omitempty && isEmptyValue(v)
353
}
354

355
func (enc *Encoder) encodeKv(b []byte, ctx encoderCtx, options valueOptions, v reflect.Value) ([]byte, error) {
356
	var err error
357

358
	if !ctx.inline {
359
		b = enc.encodeComment(ctx.indent, options.comment, b)
360
		b = enc.indent(ctx.indent, b)
361
	}
362

363
	b = enc.encodeKey(b, ctx.key)
364
	b = append(b, " = "...)
365

366
	// create a copy of the context because the value of a KV shouldn't
367
	// modify the global context.
368
	subctx := ctx
369
	subctx.insideKv = true
370
	subctx.shiftKey()
371
	subctx.options = options
372

373
	b, err = enc.encode(b, subctx, v)
374
	if err != nil {
375
		return nil, err
376
	}
377

378
	return b, nil
379
}
380

381
func isEmptyValue(v reflect.Value) bool {
382
	switch v.Kind() {
383
	case reflect.Struct:
384
		return isEmptyStruct(v)
385
	case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
386
		return v.Len() == 0
387
	case reflect.Bool:
388
		return !v.Bool()
389
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
390
		return v.Int() == 0
391
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
392
		return v.Uint() == 0
393
	case reflect.Float32, reflect.Float64:
394
		return v.Float() == 0
395
	case reflect.Interface, reflect.Ptr:
396
		return v.IsNil()
397
	}
398
	return false
399
}
400

401
func isEmptyStruct(v reflect.Value) bool {
402
	// TODO: merge with walkStruct and cache.
403
	typ := v.Type()
404
	for i := 0; i < typ.NumField(); i++ {
405
		fieldType := typ.Field(i)
406

407
		// only consider exported fields
408
		if fieldType.PkgPath != "" {
409
			continue
410
		}
411

412
		tag := fieldType.Tag.Get("toml")
413

414
		// special field name to skip field
415
		if tag == "-" {
416
			continue
417
		}
418

419
		f := v.Field(i)
420

421
		if !isEmptyValue(f) {
422
			return false
423
		}
424
	}
425

426
	return true
427
}
428

429
const literalQuote = '\''
430

431
func (enc *Encoder) encodeString(b []byte, v string, options valueOptions) []byte {
432
	if needsQuoting(v) {
433
		return enc.encodeQuotedString(options.multiline, b, v)
434
	}
435

436
	return enc.encodeLiteralString(b, v)
437
}
438

439
func needsQuoting(v string) bool {
440
	// TODO: vectorize
441
	for _, b := range []byte(v) {
442
		if b == '\'' || b == '\r' || b == '\n' || characters.InvalidAscii(b) {
443
			return true
444
		}
445
	}
446
	return false
447
}
448

449
// caller should have checked that the string does not contain new lines or ' .
450
func (enc *Encoder) encodeLiteralString(b []byte, v string) []byte {
451
	b = append(b, literalQuote)
452
	b = append(b, v...)
453
	b = append(b, literalQuote)
454

455
	return b
456
}
457

458
func (enc *Encoder) encodeQuotedString(multiline bool, b []byte, v string) []byte {
459
	stringQuote := `"`
460

461
	if multiline {
462
		stringQuote = `"""`
463
	}
464

465
	b = append(b, stringQuote...)
466
	if multiline {
467
		b = append(b, '\n')
468
	}
469

470
	const (
471
		hextable = "0123456789ABCDEF"
472
		// U+0000 to U+0008, U+000A to U+001F, U+007F
473
		nul = 0x0
474
		bs  = 0x8
475
		lf  = 0xa
476
		us  = 0x1f
477
		del = 0x7f
478
	)
479

480
	for _, r := range []byte(v) {
481
		switch r {
482
		case '\\':
483
			b = append(b, `\\`...)
484
		case '"':
485
			b = append(b, `\"`...)
486
		case '\b':
487
			b = append(b, `\b`...)
488
		case '\f':
489
			b = append(b, `\f`...)
490
		case '\n':
491
			if multiline {
492
				b = append(b, r)
493
			} else {
494
				b = append(b, `\n`...)
495
			}
496
		case '\r':
497
			b = append(b, `\r`...)
498
		case '\t':
499
			b = append(b, `\t`...)
500
		default:
501
			switch {
502
			case r >= nul && r <= bs, r >= lf && r <= us, r == del:
503
				b = append(b, `\u00`...)
504
				b = append(b, hextable[r>>4])
505
				b = append(b, hextable[r&0x0f])
506
			default:
507
				b = append(b, r)
508
			}
509
		}
510
	}
511

512
	b = append(b, stringQuote...)
513

514
	return b
515
}
516

517
// caller should have checked that the string is in A-Z / a-z / 0-9 / - / _ .
518
func (enc *Encoder) encodeUnquotedKey(b []byte, v string) []byte {
519
	return append(b, v...)
520
}
521

522
func (enc *Encoder) encodeTableHeader(ctx encoderCtx, b []byte) ([]byte, error) {
523
	if len(ctx.parentKey) == 0 {
524
		return b, nil
525
	}
526

527
	b = enc.encodeComment(ctx.indent, ctx.options.comment, b)
528

529
	b = enc.indent(ctx.indent, b)
530

531
	b = append(b, '[')
532

533
	b = enc.encodeKey(b, ctx.parentKey[0])
534

535
	for _, k := range ctx.parentKey[1:] {
536
		b = append(b, '.')
537
		b = enc.encodeKey(b, k)
538
	}
539

540
	b = append(b, "]\n"...)
541

542
	return b, nil
543
}
544

545
//nolint:cyclop
546
func (enc *Encoder) encodeKey(b []byte, k string) []byte {
547
	needsQuotation := false
548
	cannotUseLiteral := false
549

550
	if len(k) == 0 {
551
		return append(b, "''"...)
552
	}
553

554
	for _, c := range k {
555
		if (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '-' || c == '_' {
556
			continue
557
		}
558

559
		if c == literalQuote {
560
			cannotUseLiteral = true
561
		}
562

563
		needsQuotation = true
564
	}
565

566
	if needsQuotation && needsQuoting(k) {
567
		cannotUseLiteral = true
568
	}
569

570
	switch {
571
	case cannotUseLiteral:
572
		return enc.encodeQuotedString(false, b, k)
573
	case needsQuotation:
574
		return enc.encodeLiteralString(b, k)
575
	default:
576
		return enc.encodeUnquotedKey(b, k)
577
	}
578
}
579

580
func (enc *Encoder) keyToString(k reflect.Value) (string, error) {
581
	keyType := k.Type()
582
	switch {
583
	case keyType.Kind() == reflect.String:
584
		return k.String(), nil
585

586
	case keyType.Implements(textMarshalerType):
587
		keyB, err := k.Interface().(encoding.TextMarshaler).MarshalText()
588
		if err != nil {
589
			return "", fmt.Errorf("toml: error marshalling key %v from text: %w", k, err)
590
		}
591
		return string(keyB), nil
592
	}
593
	return "", fmt.Errorf("toml: type %s is not supported as a map key", keyType.Kind())
594
}
595

596
func (enc *Encoder) encodeMap(b []byte, ctx encoderCtx, v reflect.Value) ([]byte, error) {
597
	var (
598
		t                 table
599
		emptyValueOptions valueOptions
600
	)
601

602
	iter := v.MapRange()
603
	for iter.Next() {
604
		v := iter.Value()
605

606
		if isNil(v) {
607
			continue
608
		}
609

610
		k, err := enc.keyToString(iter.Key())
611
		if err != nil {
612
			return nil, err
613
		}
614

615
		if willConvertToTableOrArrayTable(ctx, v) {
616
			t.pushTable(k, v, emptyValueOptions)
617
		} else {
618
			t.pushKV(k, v, emptyValueOptions)
619
		}
620
	}
621

622
	sortEntriesByKey(t.kvs)
623
	sortEntriesByKey(t.tables)
624

625
	return enc.encodeTable(b, ctx, t)
626
}
627

628
func sortEntriesByKey(e []entry) {
629
	sort.Slice(e, func(i, j int) bool {
630
		return e[i].Key < e[j].Key
631
	})
632
}
633

634
type entry struct {
635
	Key     string
636
	Value   reflect.Value
637
	Options valueOptions
638
}
639

640
type table struct {
641
	kvs    []entry
642
	tables []entry
643
}
644

645
func (t *table) pushKV(k string, v reflect.Value, options valueOptions) {
646
	for _, e := range t.kvs {
647
		if e.Key == k {
648
			return
649
		}
650
	}
651

652
	t.kvs = append(t.kvs, entry{Key: k, Value: v, Options: options})
653
}
654

655
func (t *table) pushTable(k string, v reflect.Value, options valueOptions) {
656
	for _, e := range t.tables {
657
		if e.Key == k {
658
			return
659
		}
660
	}
661
	t.tables = append(t.tables, entry{Key: k, Value: v, Options: options})
662
}
663

664
func walkStruct(ctx encoderCtx, t *table, v reflect.Value) {
665
	// TODO: cache this
666
	typ := v.Type()
667
	for i := 0; i < typ.NumField(); i++ {
668
		fieldType := typ.Field(i)
669

670
		// only consider exported fields
671
		if fieldType.PkgPath != "" {
672
			continue
673
		}
674

675
		tag := fieldType.Tag.Get("toml")
676

677
		// special field name to skip field
678
		if tag == "-" {
679
			continue
680
		}
681

682
		k, opts := parseTag(tag)
683
		if !isValidName(k) {
684
			k = ""
685
		}
686

687
		f := v.Field(i)
688

689
		if k == "" {
690
			if fieldType.Anonymous {
691
				if fieldType.Type.Kind() == reflect.Struct {
692
					walkStruct(ctx, t, f)
693
				}
694
				continue
695
			} else {
696
				k = fieldType.Name
697
			}
698
		}
699

700
		if isNil(f) {
701
			continue
702
		}
703

704
		options := valueOptions{
705
			multiline: opts.multiline,
706
			omitempty: opts.omitempty,
707
			comment:   fieldType.Tag.Get("comment"),
708
		}
709

710
		if opts.inline || !willConvertToTableOrArrayTable(ctx, f) {
711
			t.pushKV(k, f, options)
712
		} else {
713
			t.pushTable(k, f, options)
714
		}
715
	}
716
}
717

718
func (enc *Encoder) encodeStruct(b []byte, ctx encoderCtx, v reflect.Value) ([]byte, error) {
719
	var t table
720

721
	walkStruct(ctx, &t, v)
722

723
	return enc.encodeTable(b, ctx, t)
724
}
725

726
func (enc *Encoder) encodeComment(indent int, comment string, b []byte) []byte {
727
	for len(comment) > 0 {
728
		var line string
729
		idx := strings.IndexByte(comment, '\n')
730
		if idx >= 0 {
731
			line = comment[:idx]
732
			comment = comment[idx+1:]
733
		} else {
734
			line = comment
735
			comment = ""
736
		}
737
		b = enc.indent(indent, b)
738
		b = append(b, "# "...)
739
		b = append(b, line...)
740
		b = append(b, '\n')
741
	}
742
	return b
743
}
744

745
func isValidName(s string) bool {
746
	if s == "" {
747
		return false
748
	}
749
	for _, c := range s {
750
		switch {
751
		case strings.ContainsRune("!#$%&()*+-./:;<=>?@[]^_{|}~ ", c):
752
			// Backslash and quote chars are reserved, but
753
			// otherwise any punctuation chars are allowed
754
			// in a tag name.
755
		case !unicode.IsLetter(c) && !unicode.IsDigit(c):
756
			return false
757
		}
758
	}
759
	return true
760
}
761

762
type tagOptions struct {
763
	multiline bool
764
	inline    bool
765
	omitempty bool
766
}
767

768
func parseTag(tag string) (string, tagOptions) {
769
	opts := tagOptions{}
770

771
	idx := strings.Index(tag, ",")
772
	if idx == -1 {
773
		return tag, opts
774
	}
775

776
	raw := tag[idx+1:]
777
	tag = string(tag[:idx])
778
	for raw != "" {
779
		var o string
780
		i := strings.Index(raw, ",")
781
		if i >= 0 {
782
			o, raw = raw[:i], raw[i+1:]
783
		} else {
784
			o, raw = raw, ""
785
		}
786
		switch o {
787
		case "multiline":
788
			opts.multiline = true
789
		case "inline":
790
			opts.inline = true
791
		case "omitempty":
792
			opts.omitempty = true
793
		}
794
	}
795

796
	return tag, opts
797
}
798

799
func (enc *Encoder) encodeTable(b []byte, ctx encoderCtx, t table) ([]byte, error) {
800
	var err error
801

802
	ctx.shiftKey()
803

804
	if ctx.insideKv || (ctx.inline && !ctx.isRoot()) {
805
		return enc.encodeTableInline(b, ctx, t)
806
	}
807

808
	if !ctx.skipTableHeader {
809
		b, err = enc.encodeTableHeader(ctx, b)
810
		if err != nil {
811
			return nil, err
812
		}
813

814
		if enc.indentTables && len(ctx.parentKey) > 0 {
815
			ctx.indent++
816
		}
817
	}
818
	ctx.skipTableHeader = false
819

820
	hasNonEmptyKV := false
821
	for _, kv := range t.kvs {
822
		if shouldOmitEmpty(kv.Options, kv.Value) {
823
			continue
824
		}
825
		hasNonEmptyKV = true
826

827
		ctx.setKey(kv.Key)
828

829
		b, err = enc.encodeKv(b, ctx, kv.Options, kv.Value)
830
		if err != nil {
831
			return nil, err
832
		}
833

834
		b = append(b, '\n')
835
	}
836

837
	first := true
838
	for _, table := range t.tables {
839
		if shouldOmitEmpty(table.Options, table.Value) {
840
			continue
841
		}
842
		if first {
843
			first = false
844
			if hasNonEmptyKV {
845
				b = append(b, '\n')
846
			}
847
		} else {
848
			b = append(b, "\n"...)
849
		}
850

851
		ctx.setKey(table.Key)
852

853
		ctx.options = table.Options
854

855
		b, err = enc.encode(b, ctx, table.Value)
856
		if err != nil {
857
			return nil, err
858
		}
859
	}
860

861
	return b, nil
862
}
863

864
func (enc *Encoder) encodeTableInline(b []byte, ctx encoderCtx, t table) ([]byte, error) {
865
	var err error
866

867
	b = append(b, '{')
868

869
	first := true
870
	for _, kv := range t.kvs {
871
		if shouldOmitEmpty(kv.Options, kv.Value) {
872
			continue
873
		}
874

875
		if first {
876
			first = false
877
		} else {
878
			b = append(b, `, `...)
879
		}
880

881
		ctx.setKey(kv.Key)
882

883
		b, err = enc.encodeKv(b, ctx, kv.Options, kv.Value)
884
		if err != nil {
885
			return nil, err
886
		}
887
	}
888

889
	if len(t.tables) > 0 {
890
		panic("inline table cannot contain nested tables, only key-values")
891
	}
892

893
	b = append(b, "}"...)
894

895
	return b, nil
896
}
897

898
func willConvertToTable(ctx encoderCtx, v reflect.Value) bool {
899
	if !v.IsValid() {
900
		return false
901
	}
902
	if v.Type() == timeType || v.Type().Implements(textMarshalerType) || (v.Kind() != reflect.Ptr && v.CanAddr() && reflect.PtrTo(v.Type()).Implements(textMarshalerType)) {
903
		return false
904
	}
905

906
	t := v.Type()
907
	switch t.Kind() {
908
	case reflect.Map, reflect.Struct:
909
		return !ctx.inline
910
	case reflect.Interface:
911
		return willConvertToTable(ctx, v.Elem())
912
	case reflect.Ptr:
913
		if v.IsNil() {
914
			return false
915
		}
916

917
		return willConvertToTable(ctx, v.Elem())
918
	default:
919
		return false
920
	}
921
}
922

923
func willConvertToTableOrArrayTable(ctx encoderCtx, v reflect.Value) bool {
924
	if ctx.insideKv {
925
		return false
926
	}
927
	t := v.Type()
928

929
	if t.Kind() == reflect.Interface {
930
		return willConvertToTableOrArrayTable(ctx, v.Elem())
931
	}
932

933
	if t.Kind() == reflect.Slice {
934
		if v.Len() == 0 {
935
			// An empty slice should be a kv = [].
936
			return false
937
		}
938

939
		for i := 0; i < v.Len(); i++ {
940
			t := willConvertToTable(ctx, v.Index(i))
941

942
			if !t {
943
				return false
944
			}
945
		}
946

947
		return true
948
	}
949

950
	return willConvertToTable(ctx, v)
951
}
952

953
func (enc *Encoder) encodeSlice(b []byte, ctx encoderCtx, v reflect.Value) ([]byte, error) {
954
	if v.Len() == 0 {
955
		b = append(b, "[]"...)
956

957
		return b, nil
958
	}
959

960
	if willConvertToTableOrArrayTable(ctx, v) {
961
		return enc.encodeSliceAsArrayTable(b, ctx, v)
962
	}
963

964
	return enc.encodeSliceAsArray(b, ctx, v)
965
}
966

967
// caller should have checked that v is a slice that only contains values that
968
// encode into tables.
969
func (enc *Encoder) encodeSliceAsArrayTable(b []byte, ctx encoderCtx, v reflect.Value) ([]byte, error) {
970
	ctx.shiftKey()
971

972
	scratch := make([]byte, 0, 64)
973
	scratch = append(scratch, "[["...)
974

975
	for i, k := range ctx.parentKey {
976
		if i > 0 {
977
			scratch = append(scratch, '.')
978
		}
979

980
		scratch = enc.encodeKey(scratch, k)
981
	}
982

983
	scratch = append(scratch, "]]\n"...)
984
	ctx.skipTableHeader = true
985

986
	b = enc.encodeComment(ctx.indent, ctx.options.comment, b)
987

988
	for i := 0; i < v.Len(); i++ {
989
		if i != 0 {
990
			b = append(b, "\n"...)
991
		}
992

993
		b = append(b, scratch...)
994

995
		var err error
996
		b, err = enc.encode(b, ctx, v.Index(i))
997
		if err != nil {
998
			return nil, err
999
		}
1000
	}
1001

1002
	return b, nil
1003
}
1004

1005
func (enc *Encoder) encodeSliceAsArray(b []byte, ctx encoderCtx, v reflect.Value) ([]byte, error) {
1006
	multiline := ctx.options.multiline || enc.arraysMultiline
1007
	separator := ", "
1008

1009
	b = append(b, '[')
1010

1011
	subCtx := ctx
1012
	subCtx.options = valueOptions{}
1013

1014
	if multiline {
1015
		separator = ",\n"
1016

1017
		b = append(b, '\n')
1018

1019
		subCtx.indent++
1020
	}
1021

1022
	var err error
1023
	first := true
1024

1025
	for i := 0; i < v.Len(); i++ {
1026
		if first {
1027
			first = false
1028
		} else {
1029
			b = append(b, separator...)
1030
		}
1031

1032
		if multiline {
1033
			b = enc.indent(subCtx.indent, b)
1034
		}
1035

1036
		b, err = enc.encode(b, subCtx, v.Index(i))
1037
		if err != nil {
1038
			return nil, err
1039
		}
1040
	}
1041

1042
	if multiline {
1043
		b = append(b, '\n')
1044
		b = enc.indent(ctx.indent, b)
1045
	}
1046

1047
	b = append(b, ']')
1048

1049
	return b, nil
1050
}
1051

1052
func (enc *Encoder) indent(level int, b []byte) []byte {
1053
	for i := 0; i < level; i++ {
1054
		b = append(b, enc.indentSymbol...)
1055
	}
1056

1057
	return b
1058
}
1059

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.