vendor/github.com/pelletier/go-toml/v2/marshaler.go
changeset 260 445e01aede7e
child 265 05c40b36d3b2
equal deleted inserted replaced
259:db4911b0c721 260:445e01aede7e
       
     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 
       
    17 // Marshal serializes a Go value as a TOML document.
       
    18 //
       
    19 // It is a shortcut for Encoder.Encode() with the default options.
       
    20 func Marshal(v interface{}) ([]byte, error) {
       
    21 	var buf bytes.Buffer
       
    22 	enc := NewEncoder(&buf)
       
    23 
       
    24 	err := enc.Encode(v)
       
    25 	if err != nil {
       
    26 		return nil, err
       
    27 	}
       
    28 
       
    29 	return buf.Bytes(), nil
       
    30 }
       
    31 
       
    32 // Encoder writes a TOML document to an output stream.
       
    33 type Encoder struct {
       
    34 	// output
       
    35 	w io.Writer
       
    36 
       
    37 	// global settings
       
    38 	tablesInline    bool
       
    39 	arraysMultiline bool
       
    40 	indentSymbol    string
       
    41 	indentTables    bool
       
    42 }
       
    43 
       
    44 // NewEncoder returns a new Encoder that writes to w.
       
    45 func NewEncoder(w io.Writer) *Encoder {
       
    46 	return &Encoder{
       
    47 		w:            w,
       
    48 		indentSymbol: "  ",
       
    49 	}
       
    50 }
       
    51 
       
    52 // SetTablesInline forces the encoder to emit all tables inline.
       
    53 //
       
    54 // This behavior can be controlled on an individual struct field basis with the
       
    55 // inline tag:
       
    56 //
       
    57 //	MyField `toml:",inline"`
       
    58 func (enc *Encoder) SetTablesInline(inline bool) *Encoder {
       
    59 	enc.tablesInline = inline
       
    60 	return enc
       
    61 }
       
    62 
       
    63 // SetArraysMultiline forces the encoder to emit all arrays with one element per
       
    64 // line.
       
    65 //
       
    66 // This behavior can be controlled on an individual struct field basis with the multiline tag:
       
    67 //
       
    68 //	MyField `multiline:"true"`
       
    69 func (enc *Encoder) SetArraysMultiline(multiline bool) *Encoder {
       
    70 	enc.arraysMultiline = multiline
       
    71 	return enc
       
    72 }
       
    73 
       
    74 // SetIndentSymbol defines the string that should be used for indentation. The
       
    75 // provided string is repeated for each indentation level. Defaults to two
       
    76 // spaces.
       
    77 func (enc *Encoder) SetIndentSymbol(s string) *Encoder {
       
    78 	enc.indentSymbol = s
       
    79 	return enc
       
    80 }
       
    81 
       
    82 // SetIndentTables forces the encoder to intent tables and array tables.
       
    83 func (enc *Encoder) SetIndentTables(indent bool) *Encoder {
       
    84 	enc.indentTables = indent
       
    85 	return enc
       
    86 }
       
    87 
       
    88 // Encode writes a TOML representation of v to the stream.
       
    89 //
       
    90 // If v cannot be represented to TOML it returns an error.
       
    91 //
       
    92 // # Encoding rules
       
    93 //
       
    94 // A top level slice containing only maps or structs is encoded as [[table
       
    95 // array]].
       
    96 //
       
    97 // All slices not matching rule 1 are encoded as [array]. As a result, any map
       
    98 // or struct they contain is encoded as an {inline table}.
       
    99 //
       
   100 // Nil interfaces and nil pointers are not supported.
       
   101 //
       
   102 // Keys in key-values always have one part.
       
   103 //
       
   104 // Intermediate tables are always printed.
       
   105 //
       
   106 // By default, strings are encoded as literal string, unless they contain either
       
   107 // a newline character or a single quote. In that case they are emitted as
       
   108 // quoted strings.
       
   109 //
       
   110 // Unsigned integers larger than math.MaxInt64 cannot be encoded. Doing so
       
   111 // results in an error. This rule exists because the TOML specification only
       
   112 // requires parsers to support at least the 64 bits integer range. Allowing
       
   113 // larger numbers would create non-standard TOML documents, which may not be
       
   114 // readable (at best) by other implementations. To encode such numbers, a
       
   115 // solution is a custom type that implements encoding.TextMarshaler.
       
   116 //
       
   117 // When encoding structs, fields are encoded in order of definition, with their
       
   118 // exact name.
       
   119 //
       
   120 // Tables and array tables are separated by empty lines. However, consecutive
       
   121 // subtables definitions are not. For example:
       
   122 //
       
   123 //	[top1]
       
   124 //
       
   125 //	[top2]
       
   126 //	[top2.child1]
       
   127 //
       
   128 //	[[array]]
       
   129 //
       
   130 //	[[array]]
       
   131 //	[array.child2]
       
   132 //
       
   133 // # Struct tags
       
   134 //
       
   135 // The encoding of each public struct field can be customized by the format
       
   136 // string in the "toml" key of the struct field's tag. This follows
       
   137 // encoding/json's convention. The format string starts with the name of the
       
   138 // field, optionally followed by a comma-separated list of options. The name may
       
   139 // be empty in order to provide options without overriding the default name.
       
   140 //
       
   141 // The "multiline" option emits strings as quoted multi-line TOML strings. It
       
   142 // has no effect on fields that would not be encoded as strings.
       
   143 //
       
   144 // The "inline" option turns fields that would be emitted as tables into inline
       
   145 // tables instead. It has no effect on other fields.
       
   146 //
       
   147 // The "omitempty" option prevents empty values or groups from being emitted.
       
   148 //
       
   149 // In addition to the "toml" tag struct tag, a "comment" tag can be used to emit
       
   150 // a TOML comment before the value being annotated. Comments are ignored inside
       
   151 // inline tables. For array tables, the comment is only present before the first
       
   152 // element of the array.
       
   153 func (enc *Encoder) Encode(v interface{}) error {
       
   154 	var (
       
   155 		b   []byte
       
   156 		ctx encoderCtx
       
   157 	)
       
   158 
       
   159 	ctx.inline = enc.tablesInline
       
   160 
       
   161 	if v == nil {
       
   162 		return fmt.Errorf("toml: cannot encode a nil interface")
       
   163 	}
       
   164 
       
   165 	b, err := enc.encode(b, ctx, reflect.ValueOf(v))
       
   166 	if err != nil {
       
   167 		return err
       
   168 	}
       
   169 
       
   170 	_, err = enc.w.Write(b)
       
   171 	if err != nil {
       
   172 		return fmt.Errorf("toml: cannot write: %w", err)
       
   173 	}
       
   174 
       
   175 	return nil
       
   176 }
       
   177 
       
   178 type valueOptions struct {
       
   179 	multiline bool
       
   180 	omitempty bool
       
   181 	comment   string
       
   182 }
       
   183 
       
   184 type encoderCtx struct {
       
   185 	// Current top-level key.
       
   186 	parentKey []string
       
   187 
       
   188 	// Key that should be used for a KV.
       
   189 	key string
       
   190 	// Extra flag to account for the empty string
       
   191 	hasKey bool
       
   192 
       
   193 	// Set to true to indicate that the encoder is inside a KV, so that all
       
   194 	// tables need to be inlined.
       
   195 	insideKv bool
       
   196 
       
   197 	// Set to true to skip the first table header in an array table.
       
   198 	skipTableHeader bool
       
   199 
       
   200 	// Should the next table be encoded as inline
       
   201 	inline bool
       
   202 
       
   203 	// Indentation level
       
   204 	indent int
       
   205 
       
   206 	// Options coming from struct tags
       
   207 	options valueOptions
       
   208 }
       
   209 
       
   210 func (ctx *encoderCtx) shiftKey() {
       
   211 	if ctx.hasKey {
       
   212 		ctx.parentKey = append(ctx.parentKey, ctx.key)
       
   213 		ctx.clearKey()
       
   214 	}
       
   215 }
       
   216 
       
   217 func (ctx *encoderCtx) setKey(k string) {
       
   218 	ctx.key = k
       
   219 	ctx.hasKey = true
       
   220 }
       
   221 
       
   222 func (ctx *encoderCtx) clearKey() {
       
   223 	ctx.key = ""
       
   224 	ctx.hasKey = false
       
   225 }
       
   226 
       
   227 func (ctx *encoderCtx) isRoot() bool {
       
   228 	return len(ctx.parentKey) == 0 && !ctx.hasKey
       
   229 }
       
   230 
       
   231 func (enc *Encoder) encode(b []byte, ctx encoderCtx, v reflect.Value) ([]byte, error) {
       
   232 	i := v.Interface()
       
   233 
       
   234 	switch x := i.(type) {
       
   235 	case time.Time:
       
   236 		if x.Nanosecond() > 0 {
       
   237 			return x.AppendFormat(b, time.RFC3339Nano), nil
       
   238 		}
       
   239 		return x.AppendFormat(b, time.RFC3339), nil
       
   240 	case LocalTime:
       
   241 		return append(b, x.String()...), nil
       
   242 	case LocalDate:
       
   243 		return append(b, x.String()...), nil
       
   244 	case LocalDateTime:
       
   245 		return append(b, x.String()...), nil
       
   246 	}
       
   247 
       
   248 	hasTextMarshaler := v.Type().Implements(textMarshalerType)
       
   249 	if hasTextMarshaler || (v.CanAddr() && reflect.PtrTo(v.Type()).Implements(textMarshalerType)) {
       
   250 		if !hasTextMarshaler {
       
   251 			v = v.Addr()
       
   252 		}
       
   253 
       
   254 		if ctx.isRoot() {
       
   255 			return nil, fmt.Errorf("toml: type %s implementing the TextMarshaler interface cannot be a root element", v.Type())
       
   256 		}
       
   257 
       
   258 		text, err := v.Interface().(encoding.TextMarshaler).MarshalText()
       
   259 		if err != nil {
       
   260 			return nil, err
       
   261 		}
       
   262 
       
   263 		b = enc.encodeString(b, string(text), ctx.options)
       
   264 
       
   265 		return b, nil
       
   266 	}
       
   267 
       
   268 	switch v.Kind() {
       
   269 	// containers
       
   270 	case reflect.Map:
       
   271 		return enc.encodeMap(b, ctx, v)
       
   272 	case reflect.Struct:
       
   273 		return enc.encodeStruct(b, ctx, v)
       
   274 	case reflect.Slice:
       
   275 		return enc.encodeSlice(b, ctx, v)
       
   276 	case reflect.Interface:
       
   277 		if v.IsNil() {
       
   278 			return nil, fmt.Errorf("toml: encoding a nil interface is not supported")
       
   279 		}
       
   280 
       
   281 		return enc.encode(b, ctx, v.Elem())
       
   282 	case reflect.Ptr:
       
   283 		if v.IsNil() {
       
   284 			return enc.encode(b, ctx, reflect.Zero(v.Type().Elem()))
       
   285 		}
       
   286 
       
   287 		return enc.encode(b, ctx, v.Elem())
       
   288 
       
   289 	// values
       
   290 	case reflect.String:
       
   291 		b = enc.encodeString(b, v.String(), ctx.options)
       
   292 	case reflect.Float32:
       
   293 		f := v.Float()
       
   294 
       
   295 		if math.IsNaN(f) {
       
   296 			b = append(b, "nan"...)
       
   297 		} else if f > math.MaxFloat32 {
       
   298 			b = append(b, "inf"...)
       
   299 		} else if f < -math.MaxFloat32 {
       
   300 			b = append(b, "-inf"...)
       
   301 		} else if math.Trunc(f) == f {
       
   302 			b = strconv.AppendFloat(b, f, 'f', 1, 32)
       
   303 		} else {
       
   304 			b = strconv.AppendFloat(b, f, 'f', -1, 32)
       
   305 		}
       
   306 	case reflect.Float64:
       
   307 		f := v.Float()
       
   308 		if math.IsNaN(f) {
       
   309 			b = append(b, "nan"...)
       
   310 		} else if f > math.MaxFloat64 {
       
   311 			b = append(b, "inf"...)
       
   312 		} else if f < -math.MaxFloat64 {
       
   313 			b = append(b, "-inf"...)
       
   314 		} else if math.Trunc(f) == f {
       
   315 			b = strconv.AppendFloat(b, f, 'f', 1, 64)
       
   316 		} else {
       
   317 			b = strconv.AppendFloat(b, f, 'f', -1, 64)
       
   318 		}
       
   319 	case reflect.Bool:
       
   320 		if v.Bool() {
       
   321 			b = append(b, "true"...)
       
   322 		} else {
       
   323 			b = append(b, "false"...)
       
   324 		}
       
   325 	case reflect.Uint64, reflect.Uint32, reflect.Uint16, reflect.Uint8, reflect.Uint:
       
   326 		x := v.Uint()
       
   327 		if x > uint64(math.MaxInt64) {
       
   328 			return nil, fmt.Errorf("toml: not encoding uint (%d) greater than max int64 (%d)", x, int64(math.MaxInt64))
       
   329 		}
       
   330 		b = strconv.AppendUint(b, x, 10)
       
   331 	case reflect.Int64, reflect.Int32, reflect.Int16, reflect.Int8, reflect.Int:
       
   332 		b = strconv.AppendInt(b, v.Int(), 10)
       
   333 	default:
       
   334 		return nil, fmt.Errorf("toml: cannot encode value of type %s", v.Kind())
       
   335 	}
       
   336 
       
   337 	return b, nil
       
   338 }
       
   339 
       
   340 func isNil(v reflect.Value) bool {
       
   341 	switch v.Kind() {
       
   342 	case reflect.Ptr, reflect.Interface, reflect.Map:
       
   343 		return v.IsNil()
       
   344 	default:
       
   345 		return false
       
   346 	}
       
   347 }
       
   348 
       
   349 func shouldOmitEmpty(options valueOptions, v reflect.Value) bool {
       
   350 	return options.omitempty && isEmptyValue(v)
       
   351 }
       
   352 
       
   353 func (enc *Encoder) encodeKv(b []byte, ctx encoderCtx, options valueOptions, v reflect.Value) ([]byte, error) {
       
   354 	var err error
       
   355 
       
   356 	if !ctx.inline {
       
   357 		b = enc.encodeComment(ctx.indent, options.comment, b)
       
   358 	}
       
   359 
       
   360 	b = enc.indent(ctx.indent, b)
       
   361 	b = enc.encodeKey(b, ctx.key)
       
   362 	b = append(b, " = "...)
       
   363 
       
   364 	// create a copy of the context because the value of a KV shouldn't
       
   365 	// modify the global context.
       
   366 	subctx := ctx
       
   367 	subctx.insideKv = true
       
   368 	subctx.shiftKey()
       
   369 	subctx.options = options
       
   370 
       
   371 	b, err = enc.encode(b, subctx, v)
       
   372 	if err != nil {
       
   373 		return nil, err
       
   374 	}
       
   375 
       
   376 	return b, nil
       
   377 }
       
   378 
       
   379 func isEmptyValue(v reflect.Value) bool {
       
   380 	switch v.Kind() {
       
   381 	case reflect.Struct:
       
   382 		return isEmptyStruct(v)
       
   383 	case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
       
   384 		return v.Len() == 0
       
   385 	case reflect.Bool:
       
   386 		return !v.Bool()
       
   387 	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
       
   388 		return v.Int() == 0
       
   389 	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
       
   390 		return v.Uint() == 0
       
   391 	case reflect.Float32, reflect.Float64:
       
   392 		return v.Float() == 0
       
   393 	case reflect.Interface, reflect.Ptr:
       
   394 		return v.IsNil()
       
   395 	}
       
   396 	return false
       
   397 }
       
   398 
       
   399 func isEmptyStruct(v reflect.Value) bool {
       
   400 	// TODO: merge with walkStruct and cache.
       
   401 	typ := v.Type()
       
   402 	for i := 0; i < typ.NumField(); i++ {
       
   403 		fieldType := typ.Field(i)
       
   404 
       
   405 		// only consider exported fields
       
   406 		if fieldType.PkgPath != "" {
       
   407 			continue
       
   408 		}
       
   409 
       
   410 		tag := fieldType.Tag.Get("toml")
       
   411 
       
   412 		// special field name to skip field
       
   413 		if tag == "-" {
       
   414 			continue
       
   415 		}
       
   416 
       
   417 		f := v.Field(i)
       
   418 
       
   419 		if !isEmptyValue(f) {
       
   420 			return false
       
   421 		}
       
   422 	}
       
   423 
       
   424 	return true
       
   425 }
       
   426 
       
   427 const literalQuote = '\''
       
   428 
       
   429 func (enc *Encoder) encodeString(b []byte, v string, options valueOptions) []byte {
       
   430 	if needsQuoting(v) {
       
   431 		return enc.encodeQuotedString(options.multiline, b, v)
       
   432 	}
       
   433 
       
   434 	return enc.encodeLiteralString(b, v)
       
   435 }
       
   436 
       
   437 func needsQuoting(v string) bool {
       
   438 	// TODO: vectorize
       
   439 	for _, b := range []byte(v) {
       
   440 		if b == '\'' || b == '\r' || b == '\n' || invalidAscii(b) {
       
   441 			return true
       
   442 		}
       
   443 	}
       
   444 	return false
       
   445 }
       
   446 
       
   447 // caller should have checked that the string does not contain new lines or ' .
       
   448 func (enc *Encoder) encodeLiteralString(b []byte, v string) []byte {
       
   449 	b = append(b, literalQuote)
       
   450 	b = append(b, v...)
       
   451 	b = append(b, literalQuote)
       
   452 
       
   453 	return b
       
   454 }
       
   455 
       
   456 func (enc *Encoder) encodeQuotedString(multiline bool, b []byte, v string) []byte {
       
   457 	stringQuote := `"`
       
   458 
       
   459 	if multiline {
       
   460 		stringQuote = `"""`
       
   461 	}
       
   462 
       
   463 	b = append(b, stringQuote...)
       
   464 	if multiline {
       
   465 		b = append(b, '\n')
       
   466 	}
       
   467 
       
   468 	const (
       
   469 		hextable = "0123456789ABCDEF"
       
   470 		// U+0000 to U+0008, U+000A to U+001F, U+007F
       
   471 		nul = 0x0
       
   472 		bs  = 0x8
       
   473 		lf  = 0xa
       
   474 		us  = 0x1f
       
   475 		del = 0x7f
       
   476 	)
       
   477 
       
   478 	for _, r := range []byte(v) {
       
   479 		switch r {
       
   480 		case '\\':
       
   481 			b = append(b, `\\`...)
       
   482 		case '"':
       
   483 			b = append(b, `\"`...)
       
   484 		case '\b':
       
   485 			b = append(b, `\b`...)
       
   486 		case '\f':
       
   487 			b = append(b, `\f`...)
       
   488 		case '\n':
       
   489 			if multiline {
       
   490 				b = append(b, r)
       
   491 			} else {
       
   492 				b = append(b, `\n`...)
       
   493 			}
       
   494 		case '\r':
       
   495 			b = append(b, `\r`...)
       
   496 		case '\t':
       
   497 			b = append(b, `\t`...)
       
   498 		default:
       
   499 			switch {
       
   500 			case r >= nul && r <= bs, r >= lf && r <= us, r == del:
       
   501 				b = append(b, `\u00`...)
       
   502 				b = append(b, hextable[r>>4])
       
   503 				b = append(b, hextable[r&0x0f])
       
   504 			default:
       
   505 				b = append(b, r)
       
   506 			}
       
   507 		}
       
   508 	}
       
   509 
       
   510 	b = append(b, stringQuote...)
       
   511 
       
   512 	return b
       
   513 }
       
   514 
       
   515 // caller should have checked that the string is in A-Z / a-z / 0-9 / - / _ .
       
   516 func (enc *Encoder) encodeUnquotedKey(b []byte, v string) []byte {
       
   517 	return append(b, v...)
       
   518 }
       
   519 
       
   520 func (enc *Encoder) encodeTableHeader(ctx encoderCtx, b []byte) ([]byte, error) {
       
   521 	if len(ctx.parentKey) == 0 {
       
   522 		return b, nil
       
   523 	}
       
   524 
       
   525 	b = enc.encodeComment(ctx.indent, ctx.options.comment, b)
       
   526 
       
   527 	b = enc.indent(ctx.indent, b)
       
   528 
       
   529 	b = append(b, '[')
       
   530 
       
   531 	b = enc.encodeKey(b, ctx.parentKey[0])
       
   532 
       
   533 	for _, k := range ctx.parentKey[1:] {
       
   534 		b = append(b, '.')
       
   535 		b = enc.encodeKey(b, k)
       
   536 	}
       
   537 
       
   538 	b = append(b, "]\n"...)
       
   539 
       
   540 	return b, nil
       
   541 }
       
   542 
       
   543 //nolint:cyclop
       
   544 func (enc *Encoder) encodeKey(b []byte, k string) []byte {
       
   545 	needsQuotation := false
       
   546 	cannotUseLiteral := false
       
   547 
       
   548 	if len(k) == 0 {
       
   549 		return append(b, "''"...)
       
   550 	}
       
   551 
       
   552 	for _, c := range k {
       
   553 		if (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '-' || c == '_' {
       
   554 			continue
       
   555 		}
       
   556 
       
   557 		if c == literalQuote {
       
   558 			cannotUseLiteral = true
       
   559 		}
       
   560 
       
   561 		needsQuotation = true
       
   562 	}
       
   563 
       
   564 	if needsQuotation && needsQuoting(k) {
       
   565 		cannotUseLiteral = true
       
   566 	}
       
   567 
       
   568 	switch {
       
   569 	case cannotUseLiteral:
       
   570 		return enc.encodeQuotedString(false, b, k)
       
   571 	case needsQuotation:
       
   572 		return enc.encodeLiteralString(b, k)
       
   573 	default:
       
   574 		return enc.encodeUnquotedKey(b, k)
       
   575 	}
       
   576 }
       
   577 
       
   578 func (enc *Encoder) encodeMap(b []byte, ctx encoderCtx, v reflect.Value) ([]byte, error) {
       
   579 	if v.Type().Key().Kind() != reflect.String {
       
   580 		return nil, fmt.Errorf("toml: type %s is not supported as a map key", v.Type().Key().Kind())
       
   581 	}
       
   582 
       
   583 	var (
       
   584 		t                 table
       
   585 		emptyValueOptions valueOptions
       
   586 	)
       
   587 
       
   588 	iter := v.MapRange()
       
   589 	for iter.Next() {
       
   590 		k := iter.Key().String()
       
   591 		v := iter.Value()
       
   592 
       
   593 		if isNil(v) {
       
   594 			continue
       
   595 		}
       
   596 
       
   597 		if willConvertToTableOrArrayTable(ctx, v) {
       
   598 			t.pushTable(k, v, emptyValueOptions)
       
   599 		} else {
       
   600 			t.pushKV(k, v, emptyValueOptions)
       
   601 		}
       
   602 	}
       
   603 
       
   604 	sortEntriesByKey(t.kvs)
       
   605 	sortEntriesByKey(t.tables)
       
   606 
       
   607 	return enc.encodeTable(b, ctx, t)
       
   608 }
       
   609 
       
   610 func sortEntriesByKey(e []entry) {
       
   611 	sort.Slice(e, func(i, j int) bool {
       
   612 		return e[i].Key < e[j].Key
       
   613 	})
       
   614 }
       
   615 
       
   616 type entry struct {
       
   617 	Key     string
       
   618 	Value   reflect.Value
       
   619 	Options valueOptions
       
   620 }
       
   621 
       
   622 type table struct {
       
   623 	kvs    []entry
       
   624 	tables []entry
       
   625 }
       
   626 
       
   627 func (t *table) pushKV(k string, v reflect.Value, options valueOptions) {
       
   628 	for _, e := range t.kvs {
       
   629 		if e.Key == k {
       
   630 			return
       
   631 		}
       
   632 	}
       
   633 
       
   634 	t.kvs = append(t.kvs, entry{Key: k, Value: v, Options: options})
       
   635 }
       
   636 
       
   637 func (t *table) pushTable(k string, v reflect.Value, options valueOptions) {
       
   638 	for _, e := range t.tables {
       
   639 		if e.Key == k {
       
   640 			return
       
   641 		}
       
   642 	}
       
   643 	t.tables = append(t.tables, entry{Key: k, Value: v, Options: options})
       
   644 }
       
   645 
       
   646 func walkStruct(ctx encoderCtx, t *table, v reflect.Value) {
       
   647 	// TODO: cache this
       
   648 	typ := v.Type()
       
   649 	for i := 0; i < typ.NumField(); i++ {
       
   650 		fieldType := typ.Field(i)
       
   651 
       
   652 		// only consider exported fields
       
   653 		if fieldType.PkgPath != "" {
       
   654 			continue
       
   655 		}
       
   656 
       
   657 		tag := fieldType.Tag.Get("toml")
       
   658 
       
   659 		// special field name to skip field
       
   660 		if tag == "-" {
       
   661 			continue
       
   662 		}
       
   663 
       
   664 		k, opts := parseTag(tag)
       
   665 		if !isValidName(k) {
       
   666 			k = ""
       
   667 		}
       
   668 
       
   669 		f := v.Field(i)
       
   670 
       
   671 		if k == "" {
       
   672 			if fieldType.Anonymous {
       
   673 				if fieldType.Type.Kind() == reflect.Struct {
       
   674 					walkStruct(ctx, t, f)
       
   675 				}
       
   676 				continue
       
   677 			} else {
       
   678 				k = fieldType.Name
       
   679 			}
       
   680 		}
       
   681 
       
   682 		if isNil(f) {
       
   683 			continue
       
   684 		}
       
   685 
       
   686 		options := valueOptions{
       
   687 			multiline: opts.multiline,
       
   688 			omitempty: opts.omitempty,
       
   689 			comment:   fieldType.Tag.Get("comment"),
       
   690 		}
       
   691 
       
   692 		if opts.inline || !willConvertToTableOrArrayTable(ctx, f) {
       
   693 			t.pushKV(k, f, options)
       
   694 		} else {
       
   695 			t.pushTable(k, f, options)
       
   696 		}
       
   697 	}
       
   698 }
       
   699 
       
   700 func (enc *Encoder) encodeStruct(b []byte, ctx encoderCtx, v reflect.Value) ([]byte, error) {
       
   701 	var t table
       
   702 
       
   703 	walkStruct(ctx, &t, v)
       
   704 
       
   705 	return enc.encodeTable(b, ctx, t)
       
   706 }
       
   707 
       
   708 func (enc *Encoder) encodeComment(indent int, comment string, b []byte) []byte {
       
   709 	for len(comment) > 0 {
       
   710 		var line string
       
   711 		idx := strings.IndexByte(comment, '\n')
       
   712 		if idx >= 0 {
       
   713 			line = comment[:idx]
       
   714 			comment = comment[idx+1:]
       
   715 		} else {
       
   716 			line = comment
       
   717 			comment = ""
       
   718 		}
       
   719 		b = enc.indent(indent, b)
       
   720 		b = append(b, "# "...)
       
   721 		b = append(b, line...)
       
   722 		b = append(b, '\n')
       
   723 	}
       
   724 	return b
       
   725 }
       
   726 
       
   727 func isValidName(s string) bool {
       
   728 	if s == "" {
       
   729 		return false
       
   730 	}
       
   731 	for _, c := range s {
       
   732 		switch {
       
   733 		case strings.ContainsRune("!#$%&()*+-./:;<=>?@[]^_{|}~ ", c):
       
   734 			// Backslash and quote chars are reserved, but
       
   735 			// otherwise any punctuation chars are allowed
       
   736 			// in a tag name.
       
   737 		case !unicode.IsLetter(c) && !unicode.IsDigit(c):
       
   738 			return false
       
   739 		}
       
   740 	}
       
   741 	return true
       
   742 }
       
   743 
       
   744 type tagOptions struct {
       
   745 	multiline bool
       
   746 	inline    bool
       
   747 	omitempty bool
       
   748 }
       
   749 
       
   750 func parseTag(tag string) (string, tagOptions) {
       
   751 	opts := tagOptions{}
       
   752 
       
   753 	idx := strings.Index(tag, ",")
       
   754 	if idx == -1 {
       
   755 		return tag, opts
       
   756 	}
       
   757 
       
   758 	raw := tag[idx+1:]
       
   759 	tag = string(tag[:idx])
       
   760 	for raw != "" {
       
   761 		var o string
       
   762 		i := strings.Index(raw, ",")
       
   763 		if i >= 0 {
       
   764 			o, raw = raw[:i], raw[i+1:]
       
   765 		} else {
       
   766 			o, raw = raw, ""
       
   767 		}
       
   768 		switch o {
       
   769 		case "multiline":
       
   770 			opts.multiline = true
       
   771 		case "inline":
       
   772 			opts.inline = true
       
   773 		case "omitempty":
       
   774 			opts.omitempty = true
       
   775 		}
       
   776 	}
       
   777 
       
   778 	return tag, opts
       
   779 }
       
   780 
       
   781 func (enc *Encoder) encodeTable(b []byte, ctx encoderCtx, t table) ([]byte, error) {
       
   782 	var err error
       
   783 
       
   784 	ctx.shiftKey()
       
   785 
       
   786 	if ctx.insideKv || (ctx.inline && !ctx.isRoot()) {
       
   787 		return enc.encodeTableInline(b, ctx, t)
       
   788 	}
       
   789 
       
   790 	if !ctx.skipTableHeader {
       
   791 		b, err = enc.encodeTableHeader(ctx, b)
       
   792 		if err != nil {
       
   793 			return nil, err
       
   794 		}
       
   795 
       
   796 		if enc.indentTables && len(ctx.parentKey) > 0 {
       
   797 			ctx.indent++
       
   798 		}
       
   799 	}
       
   800 	ctx.skipTableHeader = false
       
   801 
       
   802 	hasNonEmptyKV := false
       
   803 	for _, kv := range t.kvs {
       
   804 		if shouldOmitEmpty(kv.Options, kv.Value) {
       
   805 			continue
       
   806 		}
       
   807 		hasNonEmptyKV = true
       
   808 
       
   809 		ctx.setKey(kv.Key)
       
   810 
       
   811 		b, err = enc.encodeKv(b, ctx, kv.Options, kv.Value)
       
   812 		if err != nil {
       
   813 			return nil, err
       
   814 		}
       
   815 
       
   816 		b = append(b, '\n')
       
   817 	}
       
   818 
       
   819 	first := true
       
   820 	for _, table := range t.tables {
       
   821 		if shouldOmitEmpty(table.Options, table.Value) {
       
   822 			continue
       
   823 		}
       
   824 		if first {
       
   825 			first = false
       
   826 			if hasNonEmptyKV {
       
   827 				b = append(b, '\n')
       
   828 			}
       
   829 		} else {
       
   830 			b = append(b, "\n"...)
       
   831 		}
       
   832 
       
   833 		ctx.setKey(table.Key)
       
   834 
       
   835 		ctx.options = table.Options
       
   836 
       
   837 		b, err = enc.encode(b, ctx, table.Value)
       
   838 		if err != nil {
       
   839 			return nil, err
       
   840 		}
       
   841 	}
       
   842 
       
   843 	return b, nil
       
   844 }
       
   845 
       
   846 func (enc *Encoder) encodeTableInline(b []byte, ctx encoderCtx, t table) ([]byte, error) {
       
   847 	var err error
       
   848 
       
   849 	b = append(b, '{')
       
   850 
       
   851 	first := true
       
   852 	for _, kv := range t.kvs {
       
   853 		if shouldOmitEmpty(kv.Options, kv.Value) {
       
   854 			continue
       
   855 		}
       
   856 
       
   857 		if first {
       
   858 			first = false
       
   859 		} else {
       
   860 			b = append(b, `, `...)
       
   861 		}
       
   862 
       
   863 		ctx.setKey(kv.Key)
       
   864 
       
   865 		b, err = enc.encodeKv(b, ctx, kv.Options, kv.Value)
       
   866 		if err != nil {
       
   867 			return nil, err
       
   868 		}
       
   869 	}
       
   870 
       
   871 	if len(t.tables) > 0 {
       
   872 		panic("inline table cannot contain nested tables, only key-values")
       
   873 	}
       
   874 
       
   875 	b = append(b, "}"...)
       
   876 
       
   877 	return b, nil
       
   878 }
       
   879 
       
   880 func willConvertToTable(ctx encoderCtx, v reflect.Value) bool {
       
   881 	if !v.IsValid() {
       
   882 		return false
       
   883 	}
       
   884 	if v.Type() == timeType || v.Type().Implements(textMarshalerType) || (v.Kind() != reflect.Ptr && v.CanAddr() && reflect.PtrTo(v.Type()).Implements(textMarshalerType)) {
       
   885 		return false
       
   886 	}
       
   887 
       
   888 	t := v.Type()
       
   889 	switch t.Kind() {
       
   890 	case reflect.Map, reflect.Struct:
       
   891 		return !ctx.inline
       
   892 	case reflect.Interface:
       
   893 		return willConvertToTable(ctx, v.Elem())
       
   894 	case reflect.Ptr:
       
   895 		if v.IsNil() {
       
   896 			return false
       
   897 		}
       
   898 
       
   899 		return willConvertToTable(ctx, v.Elem())
       
   900 	default:
       
   901 		return false
       
   902 	}
       
   903 }
       
   904 
       
   905 func willConvertToTableOrArrayTable(ctx encoderCtx, v reflect.Value) bool {
       
   906 	if ctx.insideKv {
       
   907 		return false
       
   908 	}
       
   909 	t := v.Type()
       
   910 
       
   911 	if t.Kind() == reflect.Interface {
       
   912 		return willConvertToTableOrArrayTable(ctx, v.Elem())
       
   913 	}
       
   914 
       
   915 	if t.Kind() == reflect.Slice {
       
   916 		if v.Len() == 0 {
       
   917 			// An empty slice should be a kv = [].
       
   918 			return false
       
   919 		}
       
   920 
       
   921 		for i := 0; i < v.Len(); i++ {
       
   922 			t := willConvertToTable(ctx, v.Index(i))
       
   923 
       
   924 			if !t {
       
   925 				return false
       
   926 			}
       
   927 		}
       
   928 
       
   929 		return true
       
   930 	}
       
   931 
       
   932 	return willConvertToTable(ctx, v)
       
   933 }
       
   934 
       
   935 func (enc *Encoder) encodeSlice(b []byte, ctx encoderCtx, v reflect.Value) ([]byte, error) {
       
   936 	if v.Len() == 0 {
       
   937 		b = append(b, "[]"...)
       
   938 
       
   939 		return b, nil
       
   940 	}
       
   941 
       
   942 	if willConvertToTableOrArrayTable(ctx, v) {
       
   943 		return enc.encodeSliceAsArrayTable(b, ctx, v)
       
   944 	}
       
   945 
       
   946 	return enc.encodeSliceAsArray(b, ctx, v)
       
   947 }
       
   948 
       
   949 // caller should have checked that v is a slice that only contains values that
       
   950 // encode into tables.
       
   951 func (enc *Encoder) encodeSliceAsArrayTable(b []byte, ctx encoderCtx, v reflect.Value) ([]byte, error) {
       
   952 	ctx.shiftKey()
       
   953 
       
   954 	scratch := make([]byte, 0, 64)
       
   955 	scratch = append(scratch, "[["...)
       
   956 
       
   957 	for i, k := range ctx.parentKey {
       
   958 		if i > 0 {
       
   959 			scratch = append(scratch, '.')
       
   960 		}
       
   961 
       
   962 		scratch = enc.encodeKey(scratch, k)
       
   963 	}
       
   964 
       
   965 	scratch = append(scratch, "]]\n"...)
       
   966 	ctx.skipTableHeader = true
       
   967 
       
   968 	b = enc.encodeComment(ctx.indent, ctx.options.comment, b)
       
   969 
       
   970 	for i := 0; i < v.Len(); i++ {
       
   971 		if i != 0 {
       
   972 			b = append(b, "\n"...)
       
   973 		}
       
   974 
       
   975 		b = append(b, scratch...)
       
   976 
       
   977 		var err error
       
   978 		b, err = enc.encode(b, ctx, v.Index(i))
       
   979 		if err != nil {
       
   980 			return nil, err
       
   981 		}
       
   982 	}
       
   983 
       
   984 	return b, nil
       
   985 }
       
   986 
       
   987 func (enc *Encoder) encodeSliceAsArray(b []byte, ctx encoderCtx, v reflect.Value) ([]byte, error) {
       
   988 	multiline := ctx.options.multiline || enc.arraysMultiline
       
   989 	separator := ", "
       
   990 
       
   991 	b = append(b, '[')
       
   992 
       
   993 	subCtx := ctx
       
   994 	subCtx.options = valueOptions{}
       
   995 
       
   996 	if multiline {
       
   997 		separator = ",\n"
       
   998 
       
   999 		b = append(b, '\n')
       
  1000 
       
  1001 		subCtx.indent++
       
  1002 	}
       
  1003 
       
  1004 	var err error
       
  1005 	first := true
       
  1006 
       
  1007 	for i := 0; i < v.Len(); i++ {
       
  1008 		if first {
       
  1009 			first = false
       
  1010 		} else {
       
  1011 			b = append(b, separator...)
       
  1012 		}
       
  1013 
       
  1014 		if multiline {
       
  1015 			b = enc.indent(subCtx.indent, b)
       
  1016 		}
       
  1017 
       
  1018 		b, err = enc.encode(b, subCtx, v.Index(i))
       
  1019 		if err != nil {
       
  1020 			return nil, err
       
  1021 		}
       
  1022 	}
       
  1023 
       
  1024 	if multiline {
       
  1025 		b = append(b, '\n')
       
  1026 		b = enc.indent(ctx.indent, b)
       
  1027 	}
       
  1028 
       
  1029 	b = append(b, ']')
       
  1030 
       
  1031 	return b, nil
       
  1032 }
       
  1033 
       
  1034 func (enc *Encoder) indent(level int, b []byte) []byte {
       
  1035 	for i := 0; i < level; i++ {
       
  1036 		b = append(b, enc.indentSymbol...)
       
  1037 	}
       
  1038 
       
  1039 	return b
       
  1040 }