marshaler.go raw

   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  // The "commented" option prefixes the value and all its children with a comment
 152  // symbol.
 153  //
 154  // In addition to the "toml" tag struct tag, a "comment" tag can be used to emit
 155  // a TOML comment before the value being annotated. Comments are ignored inside
 156  // inline tables. For array tables, the comment is only present before the first
 157  // element of the array.
 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  	// Current top-level key.
 192  	parentKey []string
 193  
 194  	// Key that should be used for a KV.
 195  	key string
 196  	// Extra flag to account for the empty string
 197  	hasKey bool
 198  
 199  	// Set to true to indicate that the encoder is inside a KV, so that all
 200  	// tables need to be inlined.
 201  	insideKv bool
 202  
 203  	// Set to true to skip the first table header in an array table.
 204  	skipTableHeader bool
 205  
 206  	// Should the next table be encoded as inline
 207  	inline bool
 208  
 209  	// Indentation level
 210  	indent int
 211  
 212  	// Prefix the current value with a comment.
 213  	commented bool
 214  
 215  	// Options coming from struct tags
 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  	// containers
 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  	// values
 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  	// create a copy of the context because the value of a KV shouldn't
 375  	// modify the global context.
 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  	// TODO: merge with walkStruct and cache.
 418  	typ := v.Type()
 419  	for i := 0; i < typ.NumField(); i++ {
 420  		fieldType := typ.Field(i)
 421  
 422  		// only consider exported fields
 423  		if fieldType.PkgPath != "" {
 424  			continue
 425  		}
 426  
 427  		tag := fieldType.Tag.Get("toml")
 428  
 429  		// special field name to skip field
 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  	// TODO: vectorize
 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  // caller should have checked that the string does not contain new lines or ' .
 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  		// U+0000 to U+0008, U+000A to U+001F, U+007F
 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  // caller should have checked that the string is in A-Z / a-z / 0-9 / - / _ .
 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  //nolint:cyclop
 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  	// TODO: cache this
 683  	typ := v.Type()
 684  	for i := 0; i < typ.NumField(); i++ {
 685  		fieldType := typ.Field(i)
 686  
 687  		// only consider exported fields
 688  		if fieldType.PkgPath != "" {
 689  			continue
 690  		}
 691  
 692  		tag := fieldType.Tag.Get("toml")
 693  
 694  		// special field name to skip field
 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  			// Backslash and quote chars are reserved, but
 771  			// otherwise any punctuation chars are allowed
 772  			// in a tag name.
 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  			// An empty slice should be a kv = [].
 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  // caller should have checked that v is a slice that only contains values that
 993  // encode into tables.
 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