gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

marshaler.go (22480B)


      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 }