encode.go raw

   1  package toml
   2  
   3  import (
   4  	"bufio"
   5  	"bytes"
   6  	"encoding"
   7  	"encoding/json"
   8  	"errors"
   9  	"fmt"
  10  	"io"
  11  	"math"
  12  	"reflect"
  13  	"sort"
  14  	"strconv"
  15  	"strings"
  16  	"time"
  17  
  18  	"github.com/BurntSushi/toml/internal"
  19  )
  20  
  21  type tomlEncodeError struct{ error }
  22  
  23  var (
  24  	errArrayNilElement = errors.New("toml: cannot encode array with nil element")
  25  	errNonString       = errors.New("toml: cannot encode a map with non-string key type")
  26  	errNoKey           = errors.New("toml: top-level values must be Go maps or structs")
  27  	errAnything        = errors.New("") // used in testing
  28  )
  29  
  30  var dblQuotedReplacer = strings.NewReplacer(
  31  	"\"", "\\\"",
  32  	"\\", "\\\\",
  33  	"\x00", `\u0000`,
  34  	"\x01", `\u0001`,
  35  	"\x02", `\u0002`,
  36  	"\x03", `\u0003`,
  37  	"\x04", `\u0004`,
  38  	"\x05", `\u0005`,
  39  	"\x06", `\u0006`,
  40  	"\x07", `\u0007`,
  41  	"\b", `\b`,
  42  	"\t", `\t`,
  43  	"\n", `\n`,
  44  	"\x0b", `\u000b`,
  45  	"\f", `\f`,
  46  	"\r", `\r`,
  47  	"\x0e", `\u000e`,
  48  	"\x0f", `\u000f`,
  49  	"\x10", `\u0010`,
  50  	"\x11", `\u0011`,
  51  	"\x12", `\u0012`,
  52  	"\x13", `\u0013`,
  53  	"\x14", `\u0014`,
  54  	"\x15", `\u0015`,
  55  	"\x16", `\u0016`,
  56  	"\x17", `\u0017`,
  57  	"\x18", `\u0018`,
  58  	"\x19", `\u0019`,
  59  	"\x1a", `\u001a`,
  60  	"\x1b", `\u001b`,
  61  	"\x1c", `\u001c`,
  62  	"\x1d", `\u001d`,
  63  	"\x1e", `\u001e`,
  64  	"\x1f", `\u001f`,
  65  	"\x7f", `\u007f`,
  66  )
  67  
  68  var (
  69  	marshalToml = reflect.TypeOf((*Marshaler)(nil)).Elem()
  70  	marshalText = reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem()
  71  	timeType    = reflect.TypeOf((*time.Time)(nil)).Elem()
  72  )
  73  
  74  // Marshaler is the interface implemented by types that can marshal themselves
  75  // into valid TOML.
  76  type Marshaler interface {
  77  	MarshalTOML() ([]byte, error)
  78  }
  79  
  80  // Marshal returns a TOML representation of the Go value.
  81  //
  82  // See [Encoder] for a description of the encoding process.
  83  func Marshal(v any) ([]byte, error) {
  84  	buff := new(bytes.Buffer)
  85  	if err := NewEncoder(buff).Encode(v); err != nil {
  86  		return nil, err
  87  	}
  88  	return buff.Bytes(), nil
  89  }
  90  
  91  // Encoder encodes a Go to a TOML document.
  92  //
  93  // The mapping between Go values and TOML values should be precisely the same as
  94  // for [Decode].
  95  //
  96  // time.Time is encoded as a RFC 3339 string, and time.Duration as its string
  97  // representation.
  98  //
  99  // The [Marshaler] and [encoding.TextMarshaler] interfaces are supported to
 100  // encoding the value as custom TOML.
 101  //
 102  // If you want to write arbitrary binary data then you will need to use
 103  // something like base64 since TOML does not have any binary types.
 104  //
 105  // When encoding TOML hashes (Go maps or structs), keys without any sub-hashes
 106  // are encoded first.
 107  //
 108  // Go maps will be sorted alphabetically by key for deterministic output.
 109  //
 110  // The toml struct tag can be used to provide the key name; if omitted the
 111  // struct field name will be used. If the "omitempty" option is present the
 112  // following value will be skipped:
 113  //
 114  //   - arrays, slices, maps, and string with len of 0
 115  //   - struct with all zero values
 116  //   - bool false
 117  //
 118  // If omitzero is given all int and float types with a value of 0 will be
 119  // skipped.
 120  //
 121  // Encoding Go values without a corresponding TOML representation will return an
 122  // error. Examples of this includes maps with non-string keys, slices with nil
 123  // elements, embedded non-struct types, and nested slices containing maps or
 124  // structs. (e.g. [][]map[string]string is not allowed but []map[string]string
 125  // is okay, as is []map[string][]string).
 126  //
 127  // NOTE: only exported keys are encoded due to the use of reflection. Unexported
 128  // keys are silently discarded.
 129  type Encoder struct {
 130  	Indent     string // string for a single indentation level; default is two spaces.
 131  	hasWritten bool   // written any output to w yet?
 132  	w          *bufio.Writer
 133  }
 134  
 135  // NewEncoder create a new Encoder.
 136  func NewEncoder(w io.Writer) *Encoder {
 137  	return &Encoder{w: bufio.NewWriter(w), Indent: "  "}
 138  }
 139  
 140  // Encode writes a TOML representation of the Go value to the [Encoder]'s writer.
 141  //
 142  // An error is returned if the value given cannot be encoded to a valid TOML
 143  // document.
 144  func (enc *Encoder) Encode(v any) error {
 145  	rv := eindirect(reflect.ValueOf(v))
 146  	err := enc.safeEncode(Key([]string{}), rv)
 147  	if err != nil {
 148  		return err
 149  	}
 150  	return enc.w.Flush()
 151  }
 152  
 153  func (enc *Encoder) safeEncode(key Key, rv reflect.Value) (err error) {
 154  	defer func() {
 155  		if r := recover(); r != nil {
 156  			if terr, ok := r.(tomlEncodeError); ok {
 157  				err = terr.error
 158  				return
 159  			}
 160  			panic(r)
 161  		}
 162  	}()
 163  	enc.encode(key, rv)
 164  	return nil
 165  }
 166  
 167  func (enc *Encoder) encode(key Key, rv reflect.Value) {
 168  	// If we can marshal the type to text, then we use that. This prevents the
 169  	// encoder for handling these types as generic structs (or whatever the
 170  	// underlying type of a TextMarshaler is).
 171  	switch {
 172  	case isMarshaler(rv):
 173  		enc.writeKeyValue(key, rv, false)
 174  		return
 175  	case rv.Type() == primitiveType: // TODO: #76 would make this superfluous after implemented.
 176  		enc.encode(key, reflect.ValueOf(rv.Interface().(Primitive).undecoded))
 177  		return
 178  	}
 179  
 180  	k := rv.Kind()
 181  	switch k {
 182  	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32,
 183  		reflect.Int64,
 184  		reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32,
 185  		reflect.Uint64,
 186  		reflect.Float32, reflect.Float64, reflect.String, reflect.Bool:
 187  		enc.writeKeyValue(key, rv, false)
 188  	case reflect.Array, reflect.Slice:
 189  		if typeEqual(tomlArrayHash, tomlTypeOfGo(rv)) {
 190  			enc.eArrayOfTables(key, rv)
 191  		} else {
 192  			enc.writeKeyValue(key, rv, false)
 193  		}
 194  	case reflect.Interface:
 195  		if rv.IsNil() {
 196  			return
 197  		}
 198  		enc.encode(key, rv.Elem())
 199  	case reflect.Map:
 200  		if rv.IsNil() {
 201  			return
 202  		}
 203  		enc.eTable(key, rv)
 204  	case reflect.Ptr:
 205  		if rv.IsNil() {
 206  			return
 207  		}
 208  		enc.encode(key, rv.Elem())
 209  	case reflect.Struct:
 210  		enc.eTable(key, rv)
 211  	default:
 212  		encPanic(fmt.Errorf("unsupported type for key '%s': %s", key, k))
 213  	}
 214  }
 215  
 216  // eElement encodes any value that can be an array element.
 217  func (enc *Encoder) eElement(rv reflect.Value) {
 218  	switch v := rv.Interface().(type) {
 219  	case time.Time: // Using TextMarshaler adds extra quotes, which we don't want.
 220  		format := time.RFC3339Nano
 221  		switch v.Location() {
 222  		case internal.LocalDatetime:
 223  			format = "2006-01-02T15:04:05.999999999"
 224  		case internal.LocalDate:
 225  			format = "2006-01-02"
 226  		case internal.LocalTime:
 227  			format = "15:04:05.999999999"
 228  		}
 229  		switch v.Location() {
 230  		default:
 231  			enc.write(v.Format(format))
 232  		case internal.LocalDatetime, internal.LocalDate, internal.LocalTime:
 233  			enc.write(v.In(time.UTC).Format(format))
 234  		}
 235  		return
 236  	case Marshaler:
 237  		s, err := v.MarshalTOML()
 238  		if err != nil {
 239  			encPanic(err)
 240  		}
 241  		if s == nil {
 242  			encPanic(errors.New("MarshalTOML returned nil and no error"))
 243  		}
 244  		enc.w.Write(s)
 245  		return
 246  	case encoding.TextMarshaler:
 247  		s, err := v.MarshalText()
 248  		if err != nil {
 249  			encPanic(err)
 250  		}
 251  		if s == nil {
 252  			encPanic(errors.New("MarshalText returned nil and no error"))
 253  		}
 254  		enc.writeQuoted(string(s))
 255  		return
 256  	case time.Duration:
 257  		enc.writeQuoted(v.String())
 258  		return
 259  	case json.Number:
 260  		n, _ := rv.Interface().(json.Number)
 261  
 262  		if n == "" { /// Useful zero value.
 263  			enc.w.WriteByte('0')
 264  			return
 265  		} else if v, err := n.Int64(); err == nil {
 266  			enc.eElement(reflect.ValueOf(v))
 267  			return
 268  		} else if v, err := n.Float64(); err == nil {
 269  			enc.eElement(reflect.ValueOf(v))
 270  			return
 271  		}
 272  		encPanic(fmt.Errorf("unable to convert %q to int64 or float64", n))
 273  	}
 274  
 275  	switch rv.Kind() {
 276  	case reflect.Ptr:
 277  		enc.eElement(rv.Elem())
 278  		return
 279  	case reflect.String:
 280  		enc.writeQuoted(rv.String())
 281  	case reflect.Bool:
 282  		enc.write(strconv.FormatBool(rv.Bool()))
 283  	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
 284  		enc.write(strconv.FormatInt(rv.Int(), 10))
 285  	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
 286  		enc.write(strconv.FormatUint(rv.Uint(), 10))
 287  	case reflect.Float32:
 288  		f := rv.Float()
 289  		if math.IsNaN(f) {
 290  			if math.Signbit(f) {
 291  				enc.write("-")
 292  			}
 293  			enc.write("nan")
 294  		} else if math.IsInf(f, 0) {
 295  			if math.Signbit(f) {
 296  				enc.write("-")
 297  			}
 298  			enc.write("inf")
 299  		} else {
 300  			enc.write(floatAddDecimal(strconv.FormatFloat(f, 'g', -1, 32)))
 301  		}
 302  	case reflect.Float64:
 303  		f := rv.Float()
 304  		if math.IsNaN(f) {
 305  			if math.Signbit(f) {
 306  				enc.write("-")
 307  			}
 308  			enc.write("nan")
 309  		} else if math.IsInf(f, 0) {
 310  			if math.Signbit(f) {
 311  				enc.write("-")
 312  			}
 313  			enc.write("inf")
 314  		} else {
 315  			enc.write(floatAddDecimal(strconv.FormatFloat(f, 'g', -1, 64)))
 316  		}
 317  	case reflect.Array, reflect.Slice:
 318  		enc.eArrayOrSliceElement(rv)
 319  	case reflect.Struct:
 320  		enc.eStruct(nil, rv, true)
 321  	case reflect.Map:
 322  		enc.eMap(nil, rv, true)
 323  	case reflect.Interface:
 324  		enc.eElement(rv.Elem())
 325  	default:
 326  		encPanic(fmt.Errorf("unexpected type: %s", fmtType(rv.Interface())))
 327  	}
 328  }
 329  
 330  // By the TOML spec, all floats must have a decimal with at least one number on
 331  // either side.
 332  func floatAddDecimal(fstr string) string {
 333  	for _, c := range fstr {
 334  		if c == 'e' { // Exponent syntax
 335  			return fstr
 336  		}
 337  		if c == '.' {
 338  			return fstr
 339  		}
 340  	}
 341  	return fstr + ".0"
 342  }
 343  
 344  func (enc *Encoder) writeQuoted(s string) {
 345  	enc.write(`"` + dblQuotedReplacer.Replace(s) + `"`)
 346  }
 347  
 348  func (enc *Encoder) eArrayOrSliceElement(rv reflect.Value) {
 349  	length := rv.Len()
 350  	enc.write("[")
 351  	for i := 0; i < length; i++ {
 352  		elem := eindirect(rv.Index(i))
 353  		enc.eElement(elem)
 354  		if i != length-1 {
 355  			enc.write(", ")
 356  		}
 357  	}
 358  	enc.write("]")
 359  }
 360  
 361  func (enc *Encoder) eArrayOfTables(key Key, rv reflect.Value) {
 362  	if len(key) == 0 {
 363  		encPanic(errNoKey)
 364  	}
 365  	for i := 0; i < rv.Len(); i++ {
 366  		trv := eindirect(rv.Index(i))
 367  		if isNil(trv) {
 368  			continue
 369  		}
 370  		enc.newline()
 371  		enc.writef("%s[[%s]]", enc.indentStr(key), key)
 372  		enc.newline()
 373  		enc.eMapOrStruct(key, trv, false)
 374  	}
 375  }
 376  
 377  func (enc *Encoder) eTable(key Key, rv reflect.Value) {
 378  	if len(key) == 1 {
 379  		// Output an extra newline between top-level tables.
 380  		// (The newline isn't written if nothing else has been written though.)
 381  		enc.newline()
 382  	}
 383  	if len(key) > 0 {
 384  		enc.writef("%s[%s]", enc.indentStr(key), key)
 385  		enc.newline()
 386  	}
 387  	enc.eMapOrStruct(key, rv, false)
 388  }
 389  
 390  func (enc *Encoder) eMapOrStruct(key Key, rv reflect.Value, inline bool) {
 391  	switch rv.Kind() {
 392  	case reflect.Map:
 393  		enc.eMap(key, rv, inline)
 394  	case reflect.Struct:
 395  		enc.eStruct(key, rv, inline)
 396  	default:
 397  		// Should never happen?
 398  		panic("eTable: unhandled reflect.Value Kind: " + rv.Kind().String())
 399  	}
 400  }
 401  
 402  func (enc *Encoder) eMap(key Key, rv reflect.Value, inline bool) {
 403  	rt := rv.Type()
 404  	if rt.Key().Kind() != reflect.String {
 405  		encPanic(errNonString)
 406  	}
 407  
 408  	// Sort keys so that we have deterministic output. And write keys directly
 409  	// underneath this key first, before writing sub-structs or sub-maps.
 410  	var mapKeysDirect, mapKeysSub []reflect.Value
 411  	for _, mapKey := range rv.MapKeys() {
 412  		if typeIsTable(tomlTypeOfGo(eindirect(rv.MapIndex(mapKey)))) {
 413  			mapKeysSub = append(mapKeysSub, mapKey)
 414  		} else {
 415  			mapKeysDirect = append(mapKeysDirect, mapKey)
 416  		}
 417  	}
 418  
 419  	writeMapKeys := func(mapKeys []reflect.Value, trailC bool) {
 420  		sort.Slice(mapKeys, func(i, j int) bool { return mapKeys[i].String() < mapKeys[j].String() })
 421  		for i, mapKey := range mapKeys {
 422  			val := eindirect(rv.MapIndex(mapKey))
 423  			if isNil(val) {
 424  				continue
 425  			}
 426  
 427  			if inline {
 428  				enc.writeKeyValue(Key{mapKey.String()}, val, true)
 429  				if trailC || i != len(mapKeys)-1 {
 430  					enc.write(", ")
 431  				}
 432  			} else {
 433  				enc.encode(key.add(mapKey.String()), val)
 434  			}
 435  		}
 436  	}
 437  
 438  	if inline {
 439  		enc.write("{")
 440  	}
 441  	writeMapKeys(mapKeysDirect, len(mapKeysSub) > 0)
 442  	writeMapKeys(mapKeysSub, false)
 443  	if inline {
 444  		enc.write("}")
 445  	}
 446  }
 447  
 448  func pointerTo(t reflect.Type) reflect.Type {
 449  	if t.Kind() == reflect.Ptr {
 450  		return pointerTo(t.Elem())
 451  	}
 452  	return t
 453  }
 454  
 455  func (enc *Encoder) eStruct(key Key, rv reflect.Value, inline bool) {
 456  	// Write keys for fields directly under this key first, because if we write
 457  	// a field that creates a new table then all keys under it will be in that
 458  	// table (not the one we're writing here).
 459  	//
 460  	// Fields is a [][]int: for fieldsDirect this always has one entry (the
 461  	// struct index). For fieldsSub it contains two entries: the parent field
 462  	// index from tv, and the field indexes for the fields of the sub.
 463  	var (
 464  		rt                      = rv.Type()
 465  		fieldsDirect, fieldsSub [][]int
 466  		addFields               func(rt reflect.Type, rv reflect.Value, start []int)
 467  	)
 468  	addFields = func(rt reflect.Type, rv reflect.Value, start []int) {
 469  		for i := 0; i < rt.NumField(); i++ {
 470  			f := rt.Field(i)
 471  			isEmbed := f.Anonymous && pointerTo(f.Type).Kind() == reflect.Struct
 472  			if f.PkgPath != "" && !isEmbed { /// Skip unexported fields.
 473  				continue
 474  			}
 475  			opts := getOptions(f.Tag)
 476  			if opts.skip {
 477  				continue
 478  			}
 479  
 480  			frv := eindirect(rv.Field(i))
 481  
 482  			// Need to make a copy because ... ehm, I don't know why... I guess
 483  			// allocating a new array can cause it to fail(?)
 484  			//
 485  			// Done for: https://github.com/BurntSushi/toml/issues/430
 486  			// Previously only on 32bit for: https://github.com/BurntSushi/toml/issues/314
 487  			copyStart := make([]int, len(start))
 488  			copy(copyStart, start)
 489  			start = copyStart
 490  
 491  			// Treat anonymous struct fields with tag names as though they are
 492  			// not anonymous, like encoding/json does.
 493  			//
 494  			// Non-struct anonymous fields use the normal encoding logic.
 495  			if isEmbed {
 496  				if getOptions(f.Tag).name == "" && frv.Kind() == reflect.Struct {
 497  					addFields(frv.Type(), frv, append(start, f.Index...))
 498  					continue
 499  				}
 500  			}
 501  
 502  			if typeIsTable(tomlTypeOfGo(frv)) {
 503  				fieldsSub = append(fieldsSub, append(start, f.Index...))
 504  			} else {
 505  				fieldsDirect = append(fieldsDirect, append(start, f.Index...))
 506  			}
 507  		}
 508  	}
 509  	addFields(rt, rv, nil)
 510  
 511  	writeFields := func(fields [][]int, totalFields int) {
 512  		for _, fieldIndex := range fields {
 513  			fieldType := rt.FieldByIndex(fieldIndex)
 514  			fieldVal := rv.FieldByIndex(fieldIndex)
 515  
 516  			opts := getOptions(fieldType.Tag)
 517  			if opts.skip {
 518  				continue
 519  			}
 520  			if opts.omitempty && isEmpty(fieldVal) {
 521  				continue
 522  			}
 523  
 524  			fieldVal = eindirect(fieldVal)
 525  
 526  			if isNil(fieldVal) { /// Don't write anything for nil fields.
 527  				continue
 528  			}
 529  
 530  			keyName := fieldType.Name
 531  			if opts.name != "" {
 532  				keyName = opts.name
 533  			}
 534  
 535  			if opts.omitzero && isZero(fieldVal) {
 536  				continue
 537  			}
 538  
 539  			if inline {
 540  				enc.writeKeyValue(Key{keyName}, fieldVal, true)
 541  				if fieldIndex[0] != totalFields-1 {
 542  					enc.write(", ")
 543  				}
 544  			} else {
 545  				enc.encode(key.add(keyName), fieldVal)
 546  			}
 547  		}
 548  	}
 549  
 550  	if inline {
 551  		enc.write("{")
 552  	}
 553  
 554  	l := len(fieldsDirect) + len(fieldsSub)
 555  	writeFields(fieldsDirect, l)
 556  	writeFields(fieldsSub, l)
 557  	if inline {
 558  		enc.write("}")
 559  	}
 560  }
 561  
 562  // tomlTypeOfGo returns the TOML type name of the Go value's type.
 563  //
 564  // It is used to determine whether the types of array elements are mixed (which
 565  // is forbidden). If the Go value is nil, then it is illegal for it to be an
 566  // array element, and valueIsNil is returned as true.
 567  //
 568  // The type may be `nil`, which means no concrete TOML type could be found.
 569  func tomlTypeOfGo(rv reflect.Value) tomlType {
 570  	if isNil(rv) || !rv.IsValid() {
 571  		return nil
 572  	}
 573  
 574  	if rv.Kind() == reflect.Struct {
 575  		if rv.Type() == timeType {
 576  			return tomlDatetime
 577  		}
 578  		if isMarshaler(rv) {
 579  			return tomlString
 580  		}
 581  		return tomlHash
 582  	}
 583  
 584  	if isMarshaler(rv) {
 585  		return tomlString
 586  	}
 587  
 588  	switch rv.Kind() {
 589  	case reflect.Bool:
 590  		return tomlBool
 591  	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32,
 592  		reflect.Int64,
 593  		reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32,
 594  		reflect.Uint64:
 595  		return tomlInteger
 596  	case reflect.Float32, reflect.Float64:
 597  		return tomlFloat
 598  	case reflect.Array, reflect.Slice:
 599  		if isTableArray(rv) {
 600  			return tomlArrayHash
 601  		}
 602  		return tomlArray
 603  	case reflect.Ptr, reflect.Interface:
 604  		return tomlTypeOfGo(rv.Elem())
 605  	case reflect.String:
 606  		return tomlString
 607  	case reflect.Map:
 608  		return tomlHash
 609  	default:
 610  		encPanic(errors.New("unsupported type: " + rv.Kind().String()))
 611  		panic("unreachable")
 612  	}
 613  }
 614  
 615  func isMarshaler(rv reflect.Value) bool {
 616  	return rv.Type().Implements(marshalText) || rv.Type().Implements(marshalToml)
 617  }
 618  
 619  // isTableArray reports if all entries in the array or slice are a table.
 620  func isTableArray(arr reflect.Value) bool {
 621  	if isNil(arr) || !arr.IsValid() || arr.Len() == 0 {
 622  		return false
 623  	}
 624  
 625  	ret := true
 626  	for i := 0; i < arr.Len(); i++ {
 627  		tt := tomlTypeOfGo(eindirect(arr.Index(i)))
 628  		// Don't allow nil.
 629  		if tt == nil {
 630  			encPanic(errArrayNilElement)
 631  		}
 632  
 633  		if ret && !typeEqual(tomlHash, tt) {
 634  			ret = false
 635  		}
 636  	}
 637  	return ret
 638  }
 639  
 640  type tagOptions struct {
 641  	skip      bool // "-"
 642  	name      string
 643  	omitempty bool
 644  	omitzero  bool
 645  }
 646  
 647  func getOptions(tag reflect.StructTag) tagOptions {
 648  	t := tag.Get("toml")
 649  	if t == "-" {
 650  		return tagOptions{skip: true}
 651  	}
 652  	var opts tagOptions
 653  	parts := strings.Split(t, ",")
 654  	opts.name = parts[0]
 655  	for _, s := range parts[1:] {
 656  		switch s {
 657  		case "omitempty":
 658  			opts.omitempty = true
 659  		case "omitzero":
 660  			opts.omitzero = true
 661  		}
 662  	}
 663  	return opts
 664  }
 665  
 666  func isZero(rv reflect.Value) bool {
 667  	switch rv.Kind() {
 668  	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
 669  		return rv.Int() == 0
 670  	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
 671  		return rv.Uint() == 0
 672  	case reflect.Float32, reflect.Float64:
 673  		return rv.Float() == 0.0
 674  	}
 675  	return false
 676  }
 677  
 678  func isEmpty(rv reflect.Value) bool {
 679  	switch rv.Kind() {
 680  	case reflect.Array, reflect.Slice, reflect.Map, reflect.String:
 681  		return rv.Len() == 0
 682  	case reflect.Struct:
 683  		if rv.Type().Comparable() {
 684  			return reflect.Zero(rv.Type()).Interface() == rv.Interface()
 685  		}
 686  		// Need to also check if all the fields are empty, otherwise something
 687  		// like this with uncomparable types will always return true:
 688  		//
 689  		//   type a struct{ field b }
 690  		//   type b struct{ s []string }
 691  		//   s := a{field: b{s: []string{"AAA"}}}
 692  		for i := 0; i < rv.NumField(); i++ {
 693  			if !isEmpty(rv.Field(i)) {
 694  				return false
 695  			}
 696  		}
 697  		return true
 698  	case reflect.Bool:
 699  		return !rv.Bool()
 700  	case reflect.Ptr:
 701  		return rv.IsNil()
 702  	}
 703  	return false
 704  }
 705  
 706  func (enc *Encoder) newline() {
 707  	if enc.hasWritten {
 708  		enc.write("\n")
 709  	}
 710  }
 711  
 712  // Write a key/value pair:
 713  //
 714  //	key = <any value>
 715  //
 716  // This is also used for "k = v" in inline tables; so something like this will
 717  // be written in three calls:
 718  //
 719  //	┌───────────────────┐
 720  //	│      ┌───┐  ┌────┐│
 721  //	v      v   v  v    vv
 722  //	key = {k = 1, k2 = 2}
 723  func (enc *Encoder) writeKeyValue(key Key, val reflect.Value, inline bool) {
 724  	/// Marshaler used on top-level document; call eElement() to just call
 725  	/// Marshal{TOML,Text}.
 726  	if len(key) == 0 {
 727  		enc.eElement(val)
 728  		return
 729  	}
 730  	enc.writef("%s%s = ", enc.indentStr(key), key.maybeQuoted(len(key)-1))
 731  	enc.eElement(val)
 732  	if !inline {
 733  		enc.newline()
 734  	}
 735  }
 736  
 737  func (enc *Encoder) write(s string) {
 738  	_, err := enc.w.WriteString(s)
 739  	if err != nil {
 740  		encPanic(err)
 741  	}
 742  	enc.hasWritten = true
 743  }
 744  
 745  func (enc *Encoder) writef(format string, v ...any) {
 746  	_, err := fmt.Fprintf(enc.w, format, v...)
 747  	if err != nil {
 748  		encPanic(err)
 749  	}
 750  	enc.hasWritten = true
 751  }
 752  
 753  func (enc *Encoder) indentStr(key Key) string {
 754  	return strings.Repeat(enc.Indent, len(key)-1)
 755  }
 756  
 757  func encPanic(err error) {
 758  	panic(tomlEncodeError{err})
 759  }
 760  
 761  // Resolve any level of pointers to the actual value (e.g. **string → string).
 762  func eindirect(v reflect.Value) reflect.Value {
 763  	if v.Kind() != reflect.Ptr && v.Kind() != reflect.Interface {
 764  		if isMarshaler(v) {
 765  			return v
 766  		}
 767  		if v.CanAddr() { /// Special case for marshalers; see #358.
 768  			if pv := v.Addr(); isMarshaler(pv) {
 769  				return pv
 770  			}
 771  		}
 772  		return v
 773  	}
 774  
 775  	if v.IsNil() {
 776  		return v
 777  	}
 778  
 779  	return eindirect(v.Elem())
 780  }
 781  
 782  func isNil(rv reflect.Value) bool {
 783  	switch rv.Kind() {
 784  	case reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice:
 785  		return rv.IsNil()
 786  	default:
 787  		return false
 788  	}
 789  }
 790